m68000-python 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- m68000_python-0.1.0/.github/workflows/ci.yml +67 -0
- m68000_python-0.1.0/.github/workflows/oracles.yml +90 -0
- m68000_python-0.1.0/.github/workflows/publish.yml +66 -0
- m68000_python-0.1.0/.gitignore +31 -0
- m68000_python-0.1.0/CHANGELOG.md +123 -0
- m68000_python-0.1.0/CONTRIBUTING.md +45 -0
- m68000_python-0.1.0/LICENSE +21 -0
- m68000_python-0.1.0/PKG-INFO +379 -0
- m68000_python-0.1.0/README.md +329 -0
- m68000_python-0.1.0/SECURITY.md +13 -0
- m68000_python-0.1.0/benchmarks/compare_revisions.py +110 -0
- m68000_python-0.1.0/benchmarks/m68000_core_benchmark.py +246 -0
- m68000_python-0.1.0/docs/README.md +38 -0
- m68000_python-0.1.0/docs/ai-assisted-development.md +41 -0
- m68000_python-0.1.0/docs/api-stability.md +82 -0
- m68000_python-0.1.0/docs/claims.md +257 -0
- m68000_python-0.1.0/docs/conformance.md +202 -0
- m68000_python-0.1.0/docs/coverage.md +293 -0
- m68000_python-0.1.0/docs/cpu-state.md +52 -0
- m68000_python-0.1.0/docs/debug-session.md +122 -0
- m68000_python-0.1.0/docs/disassembly.md +61 -0
- m68000_python-0.1.0/docs/handoff-polish.md +428 -0
- m68000_python-0.1.0/docs/history/handoff-brief.md +207 -0
- m68000_python-0.1.0/docs/history/worklog.md +501 -0
- m68000_python-0.1.0/docs/interrupt-lifecycle.md +134 -0
- m68000_python-0.1.0/docs/mame-oracle.md +160 -0
- m68000_python-0.1.0/docs/mutation.md +231 -0
- m68000_python-0.1.0/docs/referees.md +290 -0
- m68000_python-0.1.0/docs/releases/0.1.0.md +75 -0
- m68000_python-0.1.0/docs/start-here.md +466 -0
- m68000_python-0.1.0/docs/timing.md +357 -0
- m68000_python-0.1.0/docs/trace-comparison.md +79 -0
- m68000_python-0.1.0/docs/trace-schema.md +118 -0
- m68000_python-0.1.0/docs/undocumented-behavior.md +380 -0
- m68000_python-0.1.0/docs/validation.md +564 -0
- m68000_python-0.1.0/examples/conformance/build.py +524 -0
- m68000_python-0.1.0/examples/conformance/exceptions.json +41 -0
- m68000_python-0.1.0/examples/conformance/exceptions.jsonl +49 -0
- m68000_python-0.1.0/examples/conformance/flags-and-branches.json +23 -0
- m68000_python-0.1.0/examples/conformance/flags-and-branches.jsonl +48 -0
- m68000_python-0.1.0/examples/conformance/interrupts/acknowledge-spurious.json +42 -0
- m68000_python-0.1.0/examples/conformance/interrupts/acknowledge-spurious.jsonl +9 -0
- m68000_python-0.1.0/examples/conformance/interrupts/acknowledge-uninitialized-vector-15.json +42 -0
- m68000_python-0.1.0/examples/conformance/interrupts/acknowledge-uninitialized-vector-15.jsonl +9 -0
- m68000_python-0.1.0/examples/conformance/interrupts/acknowledge-vectored.json +42 -0
- m68000_python-0.1.0/examples/conformance/interrupts/acknowledge-vectored.jsonl +9 -0
- m68000_python-0.1.0/examples/conformance/interrupts/autovector-e-clock-phase-0.json +40 -0
- m68000_python-0.1.0/examples/conformance/interrupts/autovector-e-clock-phase-0.jsonl +7 -0
- m68000_python-0.1.0/examples/conformance/interrupts/autovector-e-clock-phase-1.json +40 -0
- m68000_python-0.1.0/examples/conformance/interrupts/autovector-e-clock-phase-1.jsonl +7 -0
- m68000_python-0.1.0/examples/conformance/interrupts/autovector-e-clock-phase-2.json +40 -0
- m68000_python-0.1.0/examples/conformance/interrupts/autovector-e-clock-phase-2.jsonl +7 -0
- m68000_python-0.1.0/examples/conformance/interrupts/autovector-e-clock-phase-3.json +40 -0
- m68000_python-0.1.0/examples/conformance/interrupts/autovector-e-clock-phase-3.jsonl +7 -0
- m68000_python-0.1.0/examples/conformance/interrupts/autovector-e-clock-phase-4.json +40 -0
- m68000_python-0.1.0/examples/conformance/interrupts/autovector-e-clock-phase-4.jsonl +7 -0
- m68000_python-0.1.0/examples/conformance/interrupts/autovector-e-clock-phase-5.json +40 -0
- m68000_python-0.1.0/examples/conformance/interrupts/autovector-e-clock-phase-5.jsonl +7 -0
- m68000_python-0.1.0/examples/conformance/interrupts/autovector-e-clock-phase-6.json +40 -0
- m68000_python-0.1.0/examples/conformance/interrupts/autovector-e-clock-phase-6.jsonl +7 -0
- m68000_python-0.1.0/examples/conformance/interrupts/autovector-e-clock-phase-7.json +40 -0
- m68000_python-0.1.0/examples/conformance/interrupts/autovector-e-clock-phase-7.jsonl +7 -0
- m68000_python-0.1.0/examples/conformance/interrupts/autovector-e-clock-phase-8.json +40 -0
- m68000_python-0.1.0/examples/conformance/interrupts/autovector-e-clock-phase-8.jsonl +7 -0
- m68000_python-0.1.0/examples/conformance/interrupts/autovector-e-clock-phase-9.json +40 -0
- m68000_python-0.1.0/examples/conformance/interrupts/autovector-e-clock-phase-9.jsonl +7 -0
- m68000_python-0.1.0/examples/conformance/interrupts/level-above-mask-taken.json +39 -0
- m68000_python-0.1.0/examples/conformance/interrupts/level-above-mask-taken.jsonl +11 -0
- m68000_python-0.1.0/examples/conformance/interrupts/level-at-mask-held-until-mask-lowered.json +39 -0
- m68000_python-0.1.0/examples/conformance/interrupts/level-at-mask-held-until-mask-lowered.jsonl +7 -0
- m68000_python-0.1.0/examples/conformance/interrupts/level-seven-edge-ignores-mask.json +44 -0
- m68000_python-0.1.0/examples/conformance/interrupts/level-seven-edge-ignores-mask.jsonl +17 -0
- m68000_python-0.1.0/examples/conformance/interrupts/stop-in-user-mode-privilege-violation.json +28 -0
- m68000_python-0.1.0/examples/conformance/interrupts/stop-in-user-mode-privilege-violation.jsonl +7 -0
- m68000_python-0.1.0/examples/conformance/interrupts/stop-waits-for-level-above-new-mask.json +44 -0
- m68000_python-0.1.0/examples/conformance/interrupts/stop-waits-for-level-above-new-mask.jsonl +8 -0
- m68000_python-0.1.0/examples/conformance/interrupts/trace-before-pending-interrupt.json +39 -0
- m68000_python-0.1.0/examples/conformance/interrupts/trace-before-pending-interrupt.jsonl +9 -0
- m68000_python-0.1.0/examples/conformance/interrupts/traced-stop-takes-trace.json +28 -0
- m68000_python-0.1.0/examples/conformance/interrupts/traced-stop-takes-trace.jsonl +7 -0
- m68000_python-0.1.0/examples/conformance/tas-drop.json +23 -0
- m68000_python-0.1.0/examples/conformance/tas-drop.jsonl +6 -0
- m68000_python-0.1.0/examples/interrupt_host.py +64 -0
- m68000_python-0.1.0/examples/minimal_m68000_host.py +36 -0
- m68000_python-0.1.0/examples/reference_trace.jsonl +15 -0
- m68000_python-0.1.0/examples/reference_trace.py +59 -0
- m68000_python-0.1.0/pyproject.toml +69 -0
- m68000_python-0.1.0/scripts/check_decoder_vs_mame.py +122 -0
- m68000_python-0.1.0/scripts/classify_680x0.py +125 -0
- m68000_python-0.1.0/scripts/coverage_report.py +1140 -0
- m68000_python-0.1.0/scripts/fetch_test_vectors.py +225 -0
- m68000_python-0.1.0/scripts/mutate.py +1338 -0
- m68000_python-0.1.0/scripts/run_680x0.py +78 -0
- m68000_python-0.1.0/scripts/run_corpus.py +67 -0
- m68000_python-0.1.0/scripts/smoke_installed_package.py +38 -0
- m68000_python-0.1.0/src/m68000_python/__init__.py +73 -0
- m68000_python-0.1.0/src/m68000_python/__main__.py +112 -0
- m68000_python-0.1.0/src/m68000_python/_alu.py +656 -0
- m68000_python-0.1.0/src/m68000_python/_bcd.py +137 -0
- m68000_python-0.1.0/src/m68000_python/_bits.py +100 -0
- m68000_python-0.1.0/src/m68000_python/_control.py +208 -0
- m68000_python-0.1.0/src/m68000_python/_core.py +545 -0
- m68000_python-0.1.0/src/m68000_python/_dispatch.py +243 -0
- m68000_python-0.1.0/src/m68000_python/_ea.py +177 -0
- m68000_python-0.1.0/src/m68000_python/_flags.py +152 -0
- m68000_python-0.1.0/src/m68000_python/_loads.py +405 -0
- m68000_python-0.1.0/src/m68000_python/_shifts.py +168 -0
- m68000_python-0.1.0/src/m68000_python/_system.py +314 -0
- m68000_python-0.1.0/src/m68000_python/conformance.py +796 -0
- m68000_python-0.1.0/src/m68000_python/console.py +254 -0
- m68000_python-0.1.0/src/m68000_python/cpu.py +373 -0
- m68000_python-0.1.0/src/m68000_python/debug.py +361 -0
- m68000_python-0.1.0/src/m68000_python/disasm.py +354 -0
- m68000_python-0.1.0/src/m68000_python/py.typed +0 -0
- m68000_python-0.1.0/src/m68000_python/state.py +76 -0
- m68000_python-0.1.0/src/m68000_python/trace.py +296 -0
- m68000_python-0.1.0/tests/conftest.py +61 -0
- m68000_python-0.1.0/tests/corpus.py +194 -0
- m68000_python-0.1.0/tests/disasm_mame.txt +1300 -0
- m68000_python-0.1.0/tests/harness.py +147 -0
- m68000_python-0.1.0/tests/harness_680x0.py +89 -0
- m68000_python-0.1.0/tests/test_bcd.py +98 -0
- m68000_python-0.1.0/tests/test_benchmark.py +26 -0
- m68000_python-0.1.0/tests/test_command_debugger.py +197 -0
- m68000_python-0.1.0/tests/test_conformance.py +315 -0
- m68000_python-0.1.0/tests/test_corpus.py +49 -0
- m68000_python-0.1.0/tests/test_coverage_gaps.py +1299 -0
- m68000_python-0.1.0/tests/test_debug_session.py +294 -0
- m68000_python-0.1.0/tests/test_disasm.py +127 -0
- m68000_python-0.1.0/tests/test_dispatch.py +52 -0
- m68000_python-0.1.0/tests/test_interrupts.py +210 -0
- m68000_python-0.1.0/tests/test_main.py +89 -0
- m68000_python-0.1.0/tests/test_mutation_survivors.py +140 -0
- m68000_python-0.1.0/tests/test_public_api.py +219 -0
- m68000_python-0.1.0/tests/test_readability.py +318 -0
- m68000_python-0.1.0/tests/test_referee_evidence.py +294 -0
- m68000_python-0.1.0/tests/test_register_renaming.py +349 -0
- m68000_python-0.1.0/tests/test_step_clocks.py +119 -0
- m68000_python-0.1.0/tests/test_trace_comparison.py +295 -0
- m68000_python-0.1.0/validation/disasm_vs_mame.py +79 -0
- m68000_python-0.1.0/validation/lockstep.lua +11 -0
- m68000_python-0.1.0/validation/lockstep.py +579 -0
- m68000_python-0.1.0/validation/mame_trace.py +107 -0
- m68000_python-0.1.0/validation/referees/build_referees.py +249 -0
- m68000_python-0.1.0/validation/referees/bus_errors.py +190 -0
- m68000_python-0.1.0/validation/referees/calibrate.py +183 -0
- m68000_python-0.1.0/validation/referees/musashi_referee.c +172 -0
- m68000_python-0.1.0/validation/referees/questions.py +429 -0
- m68000_python-0.1.0/validation/referees/referee.py +504 -0
- m68000_python-0.1.0/validation/referees/rerun_680x0.py +214 -0
- m68000_python-0.1.0/validation/referees/winuae_referee.cpp +498 -0
- m68000_python-0.1.0/validation/referees/winuae_shim.cpp +21 -0
- m68000_python-0.1.0/validation/referees/winuae_shim.h +11 -0
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
pull_request:
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
test:
|
|
9
|
+
runs-on: ubuntu-latest
|
|
10
|
+
strategy:
|
|
11
|
+
fail-fast: false
|
|
12
|
+
matrix:
|
|
13
|
+
python-version: ["3.11", "3.12", "3.13", "3.14", "pypy3.11"]
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v4
|
|
16
|
+
- uses: actions/setup-python@v5
|
|
17
|
+
with:
|
|
18
|
+
python-version: ${{ matrix.python-version }}
|
|
19
|
+
- run: python -m pip install --upgrade pip
|
|
20
|
+
- run: python -m pip install -e ".[dev]"
|
|
21
|
+
# Everything but the corpus gates: the decoder, readability, the BCD
|
|
22
|
+
# tables, the manual-derived and referee-pinned tests, the tooling.
|
|
23
|
+
- run: python -m pytest -q
|
|
24
|
+
- run: python -m ruff check .
|
|
25
|
+
- run: python -m ruff format --check .
|
|
26
|
+
- run: python examples/minimal_m68000_host.py
|
|
27
|
+
- run: python examples/interrupt_host.py
|
|
28
|
+
|
|
29
|
+
# The SingleStepTests/m68000 gate at the pinned commit: 127 files, 317,500
|
|
30
|
+
# cases, compared on registers, SR, the queue, RAM, clocks and every bus
|
|
31
|
+
# access (docs/validation.md), and the step_clocks claim over the same
|
|
32
|
+
# cases. The 112 MB archive is fetched once per pin and cached.
|
|
33
|
+
corpus:
|
|
34
|
+
runs-on: ubuntu-latest
|
|
35
|
+
strategy:
|
|
36
|
+
fail-fast: false
|
|
37
|
+
matrix:
|
|
38
|
+
python-version: ["3.14", "pypy3.11"]
|
|
39
|
+
steps:
|
|
40
|
+
- uses: actions/checkout@v4
|
|
41
|
+
- uses: actions/setup-python@v5
|
|
42
|
+
with:
|
|
43
|
+
python-version: ${{ matrix.python-version }}
|
|
44
|
+
- run: python -m pip install --upgrade pip
|
|
45
|
+
- run: python -m pip install -e ".[dev]"
|
|
46
|
+
- uses: actions/cache@v4
|
|
47
|
+
with:
|
|
48
|
+
path: tests/68000_test_vectors/m68000
|
|
49
|
+
key: m68000-64b253116a3de04aaac4346c43680960dc9b67e5
|
|
50
|
+
- run: test -f tests/68000_test_vectors/m68000/REVISION || python scripts/fetch_test_vectors.py
|
|
51
|
+
- run: python -m pytest -q tests/test_corpus.py tests/test_step_clocks.py tests/test_bcd.py
|
|
52
|
+
|
|
53
|
+
# The wheel a release would publish, installed into a clean interpreter and
|
|
54
|
+
# exercised from outside the source tree.
|
|
55
|
+
package:
|
|
56
|
+
runs-on: ubuntu-latest
|
|
57
|
+
steps:
|
|
58
|
+
- uses: actions/checkout@v4
|
|
59
|
+
- uses: actions/setup-python@v5
|
|
60
|
+
with:
|
|
61
|
+
python-version: "3.12"
|
|
62
|
+
- run: python -m pip install --upgrade pip build
|
|
63
|
+
- run: python -m build --wheel --sdist
|
|
64
|
+
- run: python -m pip install --force-reinstall dist/*.whl
|
|
65
|
+
- name: Smoke-test the installed public API
|
|
66
|
+
working-directory: ${{ runner.temp }}
|
|
67
|
+
run: python ${{ github.workspace }}/scripts/smoke_installed_package.py
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# The evidence CI cannot rerun on every push: the referees built from pinned
|
|
2
|
+
# sources and calibrated against the gate, the second corpus as a detector,
|
|
3
|
+
# the decoder against MAME's listing, the coverage map and the mutation
|
|
4
|
+
# score. Each step's headline number is the one docs/validation.md,
|
|
5
|
+
# docs/referees.md, docs/coverage.md and docs/mutation.md record; a step
|
|
6
|
+
# fails when its number moves, so the documents cannot drift from the tree.
|
|
7
|
+
#
|
|
8
|
+
# Not covered here: the MAME lockstep (needs MAME 0.285 and the ROMs) and
|
|
9
|
+
# the T1 BCD gate, which ci.yml runs on every push. Scheduled weekly, and
|
|
10
|
+
# runnable on demand.
|
|
11
|
+
name: Oracles
|
|
12
|
+
|
|
13
|
+
on:
|
|
14
|
+
schedule:
|
|
15
|
+
- cron: "23 6 * * 1" # Mondays, 06:23 UTC
|
|
16
|
+
workflow_dispatch:
|
|
17
|
+
|
|
18
|
+
jobs:
|
|
19
|
+
oracles:
|
|
20
|
+
runs-on: ubuntu-latest
|
|
21
|
+
timeout-minutes: 150
|
|
22
|
+
steps:
|
|
23
|
+
- uses: actions/checkout@v4
|
|
24
|
+
- uses: actions/setup-python@v5
|
|
25
|
+
with:
|
|
26
|
+
python-version: "pypy3.11"
|
|
27
|
+
- run: python -m pip install --upgrade pip
|
|
28
|
+
- run: python -m pip install -e ".[dev]"
|
|
29
|
+
|
|
30
|
+
- name: Restore the SingleStepTests/m68000 corpus
|
|
31
|
+
uses: actions/cache@v4
|
|
32
|
+
with:
|
|
33
|
+
path: tests/68000_test_vectors/m68000
|
|
34
|
+
key: m68000-64b253116a3de04aaac4346c43680960dc9b67e5
|
|
35
|
+
- name: Restore the SingleStepTests/680x0 corpus
|
|
36
|
+
uses: actions/cache@v4
|
|
37
|
+
with:
|
|
38
|
+
path: tests/68000_test_vectors/680x0
|
|
39
|
+
key: 680x0-e0d5ece9670205cc84a0101081837deb446f86a3
|
|
40
|
+
- name: Fetch what the caches did not hold
|
|
41
|
+
# The two corpora cache separately, so one can be present while the
|
|
42
|
+
# other is not -- and the 680x0 cache has never once hit here. Using
|
|
43
|
+
# --with-680x0 for the second line (as this step used to) asks the
|
|
44
|
+
# script to fetch m68000 *and* 680x0 together; since m68000 was
|
|
45
|
+
# already there, that call's own fetch_m68000() immediately hit the
|
|
46
|
+
# script's "directory exists" guard, every single run. --680x0-only
|
|
47
|
+
# fetches just the corpus this step actually still needs.
|
|
48
|
+
run: |
|
|
49
|
+
test -f tests/68000_test_vectors/m68000/REVISION || {
|
|
50
|
+
rm -rf tests/68000_test_vectors/m68000
|
|
51
|
+
python scripts/fetch_test_vectors.py
|
|
52
|
+
}
|
|
53
|
+
test -d tests/68000_test_vectors/680x0/68000/v1 || {
|
|
54
|
+
rm -rf tests/68000_test_vectors/680x0
|
|
55
|
+
python scripts/fetch_test_vectors.py --680x0-only
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
- name: The decoder against MAME 0.285's m68000.lst, word for word
|
|
59
|
+
run: python scripts/check_decoder_vs_mame.py
|
|
60
|
+
|
|
61
|
+
- name: The second corpus as a detector, every disagreement classified
|
|
62
|
+
run: |
|
|
63
|
+
python scripts/classify_680x0.py 2>/dev/null | tee classify.txt
|
|
64
|
+
grep -q "^787,660 of 1,000,060 cases agree" classify.txt
|
|
65
|
+
|
|
66
|
+
- name: Build the referees from their pinned sources
|
|
67
|
+
run: python validation/referees/build_referees.py
|
|
68
|
+
- name: Calibrate WinUAE's tester core against the gate
|
|
69
|
+
run: |
|
|
70
|
+
python validation/referees/calibrate.py winuae | tee winuae.txt
|
|
71
|
+
grep -q "^TOTAL winuae vs m68000: 308416/314988 judged cases agree" winuae.txt
|
|
72
|
+
- name: Calibrate Musashi against the gate
|
|
73
|
+
run: |
|
|
74
|
+
python validation/referees/calibrate.py musashi | tee musashi.txt
|
|
75
|
+
grep -q "^TOTAL musashi vs m68000: 257300/261894 judged cases agree" musashi.txt
|
|
76
|
+
- name: The open questions, put to the referees
|
|
77
|
+
run: python validation/referees/questions.py
|
|
78
|
+
|
|
79
|
+
- name: Coverage map of the gate and the suite
|
|
80
|
+
run: |
|
|
81
|
+
python scripts/coverage_report.py encodings | tee encodings.txt
|
|
82
|
+
grep -q "executed as a first word 38,019" encodings.txt
|
|
83
|
+
python scripts/coverage_report.py paths | tail -5
|
|
84
|
+
|
|
85
|
+
- name: Mutation score of the suite
|
|
86
|
+
run: |
|
|
87
|
+
python scripts/mutate.py run --jobs 2 --python "$(command -v python)" --out mutation.json
|
|
88
|
+
python scripts/mutate.py escalate mutation.json --jobs 2 --python "$(command -v python)"
|
|
89
|
+
python scripts/mutate.py report mutation.json | tee mutation.txt
|
|
90
|
+
grep -q "^Score: 174/176" mutation.txt
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# Build, check and publish m68000-python to PyPI with Trusted Publishing
|
|
2
|
+
# (OIDC): no stored token. PyPI must know this repository, this workflow
|
|
3
|
+
# file and the `pypi` environment as the project's publisher (Aubrey
|
|
4
|
+
# registers it before the first tag, as for z80-python).
|
|
5
|
+
#
|
|
6
|
+
# A pushed release tag (v0.1.0, not v0.1.0-rc1) builds, smoke-tests and
|
|
7
|
+
# uploads. Every pull request runs the same build, check and smoke test as
|
|
8
|
+
# a dry run, so a broken release is caught before the tag. Run it by hand
|
|
9
|
+
# (workflow_dispatch) for the same dry run on any branch; only a tag is
|
|
10
|
+
# ever uploaded.
|
|
11
|
+
name: Publish
|
|
12
|
+
|
|
13
|
+
on:
|
|
14
|
+
push:
|
|
15
|
+
tags:
|
|
16
|
+
- "v*"
|
|
17
|
+
- "!v*-rc*"
|
|
18
|
+
pull_request:
|
|
19
|
+
workflow_dispatch:
|
|
20
|
+
inputs:
|
|
21
|
+
dry_run:
|
|
22
|
+
description: "Build, smoke-test and check only; never upload"
|
|
23
|
+
type: boolean
|
|
24
|
+
default: true
|
|
25
|
+
|
|
26
|
+
jobs:
|
|
27
|
+
build:
|
|
28
|
+
runs-on: ubuntu-latest
|
|
29
|
+
steps:
|
|
30
|
+
- uses: actions/checkout@v4
|
|
31
|
+
- uses: actions/setup-python@v5
|
|
32
|
+
with:
|
|
33
|
+
python-version: "3.12"
|
|
34
|
+
- run: python -m pip install --upgrade pip build twine
|
|
35
|
+
- run: python -m build --wheel --sdist
|
|
36
|
+
- run: python -m twine check --strict dist/*
|
|
37
|
+
- name: A release tag must name the package version
|
|
38
|
+
if: startsWith(github.ref, 'refs/tags/v')
|
|
39
|
+
run: |
|
|
40
|
+
version=$(python -c "import tomllib; print(tomllib.load(open('pyproject.toml','rb'))['project']['version'])")
|
|
41
|
+
test "${GITHUB_REF_NAME}" = "v${version}" || {
|
|
42
|
+
echo "tag ${GITHUB_REF_NAME} does not match version ${version}"; exit 1; }
|
|
43
|
+
- run: python -m pip install dist/*.whl
|
|
44
|
+
- name: Smoke-test the installed wheel
|
|
45
|
+
working-directory: ${{ runner.temp }}
|
|
46
|
+
run: python ${{ github.workspace }}/scripts/smoke_installed_package.py
|
|
47
|
+
- uses: actions/upload-artifact@v4
|
|
48
|
+
with:
|
|
49
|
+
name: dist
|
|
50
|
+
path: dist/
|
|
51
|
+
|
|
52
|
+
publish:
|
|
53
|
+
needs: build
|
|
54
|
+
if: >-
|
|
55
|
+
startsWith(github.ref, 'refs/tags/v') &&
|
|
56
|
+
(github.event_name == 'push' || inputs.dry_run == false)
|
|
57
|
+
runs-on: ubuntu-latest
|
|
58
|
+
environment: pypi
|
|
59
|
+
permissions:
|
|
60
|
+
id-token: write
|
|
61
|
+
steps:
|
|
62
|
+
- uses: actions/download-artifact@v4
|
|
63
|
+
with:
|
|
64
|
+
name: dist
|
|
65
|
+
path: dist/
|
|
66
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Python and package builds
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.egg-info/
|
|
5
|
+
build/
|
|
6
|
+
dist/
|
|
7
|
+
|
|
8
|
+
# Virtual environments and tooling
|
|
9
|
+
.venv/
|
|
10
|
+
.venv-*/
|
|
11
|
+
venv/
|
|
12
|
+
.pytest_cache/
|
|
13
|
+
.ruff_cache/
|
|
14
|
+
.coverage
|
|
15
|
+
htmlcov/
|
|
16
|
+
|
|
17
|
+
# External validation inputs; fetch them locally, never commit them.
|
|
18
|
+
# Both SingleStepTests corpora land here (scripts/fetch_test_vectors.py).
|
|
19
|
+
tests/68000_test_vectors/
|
|
20
|
+
|
|
21
|
+
# MAME oracle runs: traces, error.log, nvram, cfg. Never commit ROMs or traces.
|
|
22
|
+
validation/mame_runs/
|
|
23
|
+
# MAME's m68000.lst, fetched at a pinned tag by scripts/check_decoder_vs_mame.py.
|
|
24
|
+
validation/mame/
|
|
25
|
+
*.trace
|
|
26
|
+
error.log
|
|
27
|
+
|
|
28
|
+
# Referee sources (third-party, fetched at pinned commits) and their builds:
|
|
29
|
+
# validation/referees/build_referees.py. Never commit either.
|
|
30
|
+
validation/referees/src/
|
|
31
|
+
validation/referees/build/
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to `m68000-python` are recorded here. The format follows
|
|
4
|
+
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project
|
|
5
|
+
follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
|
+
|
|
7
|
+
## [Unreleased]
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- **The conformance kit**, `m68000_python.conformance` and
|
|
12
|
+
`python -m m68000_python.conformance trace|diff`, in z80-python's shape:
|
|
13
|
+
a versioned JSON manifest fixes the machine (16 MiB of flat RAM, reset or
|
|
14
|
+
an initial state, the acknowledge answers, BERR ranges, TAS's write
|
|
15
|
+
cycle, replayed device windows, `ipl` and `reset` events, stop rules); the
|
|
16
|
+
reference writes its trace with every bus access, and `diff` compares
|
|
17
|
+
another core's trace in lockstep. `examples/conformance/` holds three
|
|
18
|
+
programs and the interrupt and STOP scenarios as manifests with their
|
|
19
|
+
reference traces; `validation/lockstep.py export` writes the MAME runs as
|
|
20
|
+
replay manifests. docs/conformance.md has the certification ladder for a
|
|
21
|
+
port.
|
|
22
|
+
|
|
23
|
+
### Fixed
|
|
24
|
+
|
|
25
|
+
- **The reset exception takes 40 clocks, not 42** (#3). `reset()` now
|
|
26
|
+
spends 14 internal clocks before reading the SSP vector, not 16, so it
|
|
27
|
+
returns 40, as UM Table 8-14 prints (40(6/0)), and every access of the
|
|
28
|
+
reset starts 2 clocks earlier. The 14 are measured: Nuked-MD's gate-level
|
|
29
|
+
68000 (built from die photographs) reads the SSP vector 14 clocks after
|
|
30
|
+
RESET is released, and the rest of the reset at the core's clocks. A host
|
|
31
|
+
whose timing counts from `reset()` sees every later clock 2 lower, which
|
|
32
|
+
moves the E-clock phase of autovectored interrupts. docs/claims.md moves
|
|
33
|
+
the reset total from "Undecidable here" to "Provisional".
|
|
34
|
+
|
|
35
|
+
## [0.1.0] — unreleased
|
|
36
|
+
|
|
37
|
+
The first release: the whole 68000 instruction set and exception model,
|
|
38
|
+
verified up a ladder of oracles ordered by tier, with a claim boundary for
|
|
39
|
+
what no available evidence settles.
|
|
40
|
+
|
|
41
|
+
### Breaking (relative to the development tree megadrive-python and sms-python built against)
|
|
42
|
+
|
|
43
|
+
- **Console numbers are decimal, or hexadecimal with `$` or `0x`.** The
|
|
44
|
+
console, `console.parse_number` and `python -m m68000_python`'s
|
|
45
|
+
addresses treated a bare number as hexadecimal and `#` as decimal; they
|
|
46
|
+
now follow the family's rule. Migration: write `break $1006` or
|
|
47
|
+
`break 0x1006`, `--pc 0x1000`, and drop the `#`.
|
|
48
|
+
- **`reset_devices` is a constructor keyword.** The RESET instruction's
|
|
49
|
+
pulse used to reach a `reset_devices` attribute found by `getattr`.
|
|
50
|
+
Migration: `M68000CPU(..., reset_devices=callback)`.
|
|
51
|
+
- **`set_pc` refuses an odd address and `CPUState` an odd `pc`** with a
|
|
52
|
+
`ValueError`: an instruction never lives at an odd address, and a jump to
|
|
53
|
+
one is an address error, not a start. Migration: none for a host that
|
|
54
|
+
starts programs at even addresses, which is every host.
|
|
55
|
+
|
|
56
|
+
### Added
|
|
57
|
+
|
|
58
|
+
- The core: every defined first word (45,815), the prefetch queue, address
|
|
59
|
+
and bus errors with the seven-word frame, the interrupt lifecycle with
|
|
60
|
+
the acknowledge cycle and the E-clock wait, trace, STOP, RESET, and
|
|
61
|
+
`step_clocks` for the clock at which each access ends within a step.
|
|
62
|
+
- The claim boundary (docs/claims.md): every behaviour labelled verified,
|
|
63
|
+
strong, provisional, contested, undecidable here or outside the contract,
|
|
64
|
+
by the independent lineages that support it.
|
|
65
|
+
- Oracles and referees: flamewing's hardware-captured BCD tables (T1),
|
|
66
|
+
WinUAE's CPU-tester core and Musashi built from pinned sources and run
|
|
67
|
+
over the gate (docs/referees.md), the SingleStepTests/m68000 gate with
|
|
68
|
+
every bus access compared and no case excluded, the MAME 0.285 lockstep
|
|
69
|
+
on 52.8 million instructions of real code, the 680x0 corpus as a
|
|
70
|
+
classified detector, and MAME's `m68000.lst` against the decoder.
|
|
71
|
+
- The suite measured: a coverage map over encodings, behavioural paths and
|
|
72
|
+
source lines (docs/coverage.md) and 176 seeded mutants of which 174 are
|
|
73
|
+
killed, the two survivors equivalent (docs/mutation.md).
|
|
74
|
+
- Every handler cites its manual page and names the corpus file, referee
|
|
75
|
+
run or hardware tables that pin what the manual leaves open;
|
|
76
|
+
`tests/test_readability.py` enforces it.
|
|
77
|
+
- Tooling in z80-python's shape: `CPUState`, a disassembler in MAME's
|
|
78
|
+
spelling (checked against 1,300 instructions of MAME's own disassembly
|
|
79
|
+
of real game code), `DebugSession` with breakpoints, watchpoints,
|
|
80
|
+
bus-access tracking and board targets, versioned JSON Lines traces with
|
|
81
|
+
first-divergence comparison of files and of live sessions,
|
|
82
|
+
`CommandDebugger`, and `python -m m68000_python` with `--zip` for
|
|
83
|
+
even/odd ROM pairs.
|
|
84
|
+
- Examples: `examples/minimal_m68000_host.py`, `examples/interrupt_host.py`
|
|
85
|
+
and a committed reference trace; a benchmark harness with four named
|
|
86
|
+
workloads and a same-process A/B of two revisions.
|
|
87
|
+
- CI on every push (CPython 3.11-3.14 and PyPy 3.11, the corpus gate on two
|
|
88
|
+
interpreters, a wheel build and installed-API smoke test) and a weekly
|
|
89
|
+
Oracles workflow that rebuilds the referees and fails when any recorded
|
|
90
|
+
number moves.
|
|
91
|
+
|
|
92
|
+
### Changed
|
|
93
|
+
|
|
94
|
+
- **9% faster on CPython, 37% on PyPy** (the `base` workload): a speed
|
|
95
|
+
ladder of five candidate rungs, one commit per rung, every oracle green
|
|
96
|
+
at each and measured with `benchmarks/compare_revisions.py`. Kept:
|
|
97
|
+
`MASK`/`MSB` as tuples (A) and the refills reading the program word
|
|
98
|
+
themselves (C); reverted or not adopted: the A7 byte step inlined (B),
|
|
99
|
+
flags computed inside `_add` (D), the function-code wrappers (E). The
|
|
100
|
+
table is in docs/validation.md, "Speed".
|
|
101
|
+
|
|
102
|
+
### Fixed
|
|
103
|
+
|
|
104
|
+
Each as a failing test first, then the fix:
|
|
105
|
+
|
|
106
|
+
- An address error during the reset sequence (an odd initial PC) let the
|
|
107
|
+
core's internal exception escape from `reset()`; it is a double bus fault
|
|
108
|
+
and the processor halts (UM 5.4.4). Found by the polish round's coverage
|
|
109
|
+
check of the refills, 2026-09-25.
|
|
110
|
+
|
|
111
|
+
In the verification rounds before this release:
|
|
112
|
+
|
|
113
|
+
- A traced illegal, line A/F or privileged instruction was followed by a
|
|
114
|
+
trace exception; UM 6.3.8, MAME's microcode and WinUAE say an
|
|
115
|
+
instruction that was not executed is not traced (8760315).
|
|
116
|
+
- A fault while TRAPV's trap was processed stacked the next opcode as IR;
|
|
117
|
+
WinUAE (run) and MAME's microcode keep TRAPV (e057279).
|
|
118
|
+
- A `BusError` raised on TAS's write half escaped `step()` instead of
|
|
119
|
+
becoming the bus-error exception (36862e5).
|
|
120
|
+
- The I/N bit of a fault during group 2 exception processing was set; WinUAE
|
|
121
|
+
(run) and MAME's microcode clear it (e395be9).
|
|
122
|
+
- The disassembler printed every undefined word as `illegal`; MAME prints
|
|
123
|
+
`dc.w $xxxx; ILLEGAL` for all but `$4AFC`, and so does the core's now.
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
Issues are the right place for bug reports, API discussion and proposed
|
|
4
|
+
changes; a disagreement with an oracle is an issue with the evidence
|
|
5
|
+
attached, not a pull request that changes the core.
|
|
6
|
+
|
|
7
|
+
For a pull request:
|
|
8
|
+
|
|
9
|
+
1. Keep the instruction core independent of machine and device policy; the
|
|
10
|
+
host owns everything the [README](README.md)'s boundary gives it.
|
|
11
|
+
2. **Never change the core against the gate.** The pinned SingleStepTests
|
|
12
|
+
corpus decides bus order, clocks and the stacked PC unless a source
|
|
13
|
+
closer to silicon says otherwise; a rule [claims.md](docs/claims.md)
|
|
14
|
+
lists as contested stays on the gate until real-hardware evidence
|
|
15
|
+
decides it. A change that makes the gate fail is wrong or needs a new
|
|
16
|
+
decision, in that order.
|
|
17
|
+
3. A core fix lands as a **failing test first**, in its own commit, then
|
|
18
|
+
the fix. Add a focused test for any behaviour change: in
|
|
19
|
+
`tests/test_coverage_gaps.py` when the manual decides it, in
|
|
20
|
+
`tests/test_referee_evidence.py` when a referee run does; and rerun the
|
|
21
|
+
whole gate.
|
|
22
|
+
4. A new or changed handler keeps the readability contract
|
|
23
|
+
(`tests/test_readability.py`): its docstring starts with the Motorola
|
|
24
|
+
name, ends with its sources in parentheses, and names what pins any rule
|
|
25
|
+
the manuals do not give. The vocabulary:
|
|
26
|
+
- sources: `PRM 4-116` (a page), `PRM Table 3-18`, `PRM 2.2` (a
|
|
27
|
+
section), `UM 6.3.6`, `UM Figure 6-7`, `UM Table 8-12` or
|
|
28
|
+
`UM Table 8-12: 20 clocks`, `UM Tables 8-2, 8-3`; several joined by
|
|
29
|
+
`; `;
|
|
30
|
+
- evidence: `SST MOVE.b/.w/.l` or `SST ILLEGAL_LINEA` (files of the
|
|
31
|
+
gate corpus), `WinUAE run` (docs/referees.md), `flamewing` (the T1
|
|
32
|
+
BCD tables).
|
|
33
|
+
5. Run `python -m pytest -q`, `python -m ruff check .` and
|
|
34
|
+
`python -m ruff format --check .` (CI runs all three, plus the examples
|
|
35
|
+
and a wheel build). With the corpus fetched, `pytest` includes the gate
|
|
36
|
+
and the `step_clocks` claim; without it they skip.
|
|
37
|
+
6. Every number in a document is regenerated by the command the document
|
|
38
|
+
names, never remembered; a claim names its oracle and the oracle's tier
|
|
39
|
+
in the sentence that makes it.
|
|
40
|
+
7. Public API and lifecycle changes go under the right heading in
|
|
41
|
+
`CHANGELOG.md` (**Breaking** with a one-line migration, before 1.0) and
|
|
42
|
+
into [docs/api-stability.md](docs/api-stability.md).
|
|
43
|
+
|
|
44
|
+
Do not commit the corpora, ROMs, MAME traces, referee sources or builds,
|
|
45
|
+
caches, generated package artifacts or credentials.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 alewman
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|