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.
Files changed (153) hide show
  1. m68000_python-0.1.0/.github/workflows/ci.yml +67 -0
  2. m68000_python-0.1.0/.github/workflows/oracles.yml +90 -0
  3. m68000_python-0.1.0/.github/workflows/publish.yml +66 -0
  4. m68000_python-0.1.0/.gitignore +31 -0
  5. m68000_python-0.1.0/CHANGELOG.md +123 -0
  6. m68000_python-0.1.0/CONTRIBUTING.md +45 -0
  7. m68000_python-0.1.0/LICENSE +21 -0
  8. m68000_python-0.1.0/PKG-INFO +379 -0
  9. m68000_python-0.1.0/README.md +329 -0
  10. m68000_python-0.1.0/SECURITY.md +13 -0
  11. m68000_python-0.1.0/benchmarks/compare_revisions.py +110 -0
  12. m68000_python-0.1.0/benchmarks/m68000_core_benchmark.py +246 -0
  13. m68000_python-0.1.0/docs/README.md +38 -0
  14. m68000_python-0.1.0/docs/ai-assisted-development.md +41 -0
  15. m68000_python-0.1.0/docs/api-stability.md +82 -0
  16. m68000_python-0.1.0/docs/claims.md +257 -0
  17. m68000_python-0.1.0/docs/conformance.md +202 -0
  18. m68000_python-0.1.0/docs/coverage.md +293 -0
  19. m68000_python-0.1.0/docs/cpu-state.md +52 -0
  20. m68000_python-0.1.0/docs/debug-session.md +122 -0
  21. m68000_python-0.1.0/docs/disassembly.md +61 -0
  22. m68000_python-0.1.0/docs/handoff-polish.md +428 -0
  23. m68000_python-0.1.0/docs/history/handoff-brief.md +207 -0
  24. m68000_python-0.1.0/docs/history/worklog.md +501 -0
  25. m68000_python-0.1.0/docs/interrupt-lifecycle.md +134 -0
  26. m68000_python-0.1.0/docs/mame-oracle.md +160 -0
  27. m68000_python-0.1.0/docs/mutation.md +231 -0
  28. m68000_python-0.1.0/docs/referees.md +290 -0
  29. m68000_python-0.1.0/docs/releases/0.1.0.md +75 -0
  30. m68000_python-0.1.0/docs/start-here.md +466 -0
  31. m68000_python-0.1.0/docs/timing.md +357 -0
  32. m68000_python-0.1.0/docs/trace-comparison.md +79 -0
  33. m68000_python-0.1.0/docs/trace-schema.md +118 -0
  34. m68000_python-0.1.0/docs/undocumented-behavior.md +380 -0
  35. m68000_python-0.1.0/docs/validation.md +564 -0
  36. m68000_python-0.1.0/examples/conformance/build.py +524 -0
  37. m68000_python-0.1.0/examples/conformance/exceptions.json +41 -0
  38. m68000_python-0.1.0/examples/conformance/exceptions.jsonl +49 -0
  39. m68000_python-0.1.0/examples/conformance/flags-and-branches.json +23 -0
  40. m68000_python-0.1.0/examples/conformance/flags-and-branches.jsonl +48 -0
  41. m68000_python-0.1.0/examples/conformance/interrupts/acknowledge-spurious.json +42 -0
  42. m68000_python-0.1.0/examples/conformance/interrupts/acknowledge-spurious.jsonl +9 -0
  43. m68000_python-0.1.0/examples/conformance/interrupts/acknowledge-uninitialized-vector-15.json +42 -0
  44. m68000_python-0.1.0/examples/conformance/interrupts/acknowledge-uninitialized-vector-15.jsonl +9 -0
  45. m68000_python-0.1.0/examples/conformance/interrupts/acknowledge-vectored.json +42 -0
  46. m68000_python-0.1.0/examples/conformance/interrupts/acknowledge-vectored.jsonl +9 -0
  47. m68000_python-0.1.0/examples/conformance/interrupts/autovector-e-clock-phase-0.json +40 -0
  48. m68000_python-0.1.0/examples/conformance/interrupts/autovector-e-clock-phase-0.jsonl +7 -0
  49. m68000_python-0.1.0/examples/conformance/interrupts/autovector-e-clock-phase-1.json +40 -0
  50. m68000_python-0.1.0/examples/conformance/interrupts/autovector-e-clock-phase-1.jsonl +7 -0
  51. m68000_python-0.1.0/examples/conformance/interrupts/autovector-e-clock-phase-2.json +40 -0
  52. m68000_python-0.1.0/examples/conformance/interrupts/autovector-e-clock-phase-2.jsonl +7 -0
  53. m68000_python-0.1.0/examples/conformance/interrupts/autovector-e-clock-phase-3.json +40 -0
  54. m68000_python-0.1.0/examples/conformance/interrupts/autovector-e-clock-phase-3.jsonl +7 -0
  55. m68000_python-0.1.0/examples/conformance/interrupts/autovector-e-clock-phase-4.json +40 -0
  56. m68000_python-0.1.0/examples/conformance/interrupts/autovector-e-clock-phase-4.jsonl +7 -0
  57. m68000_python-0.1.0/examples/conformance/interrupts/autovector-e-clock-phase-5.json +40 -0
  58. m68000_python-0.1.0/examples/conformance/interrupts/autovector-e-clock-phase-5.jsonl +7 -0
  59. m68000_python-0.1.0/examples/conformance/interrupts/autovector-e-clock-phase-6.json +40 -0
  60. m68000_python-0.1.0/examples/conformance/interrupts/autovector-e-clock-phase-6.jsonl +7 -0
  61. m68000_python-0.1.0/examples/conformance/interrupts/autovector-e-clock-phase-7.json +40 -0
  62. m68000_python-0.1.0/examples/conformance/interrupts/autovector-e-clock-phase-7.jsonl +7 -0
  63. m68000_python-0.1.0/examples/conformance/interrupts/autovector-e-clock-phase-8.json +40 -0
  64. m68000_python-0.1.0/examples/conformance/interrupts/autovector-e-clock-phase-8.jsonl +7 -0
  65. m68000_python-0.1.0/examples/conformance/interrupts/autovector-e-clock-phase-9.json +40 -0
  66. m68000_python-0.1.0/examples/conformance/interrupts/autovector-e-clock-phase-9.jsonl +7 -0
  67. m68000_python-0.1.0/examples/conformance/interrupts/level-above-mask-taken.json +39 -0
  68. m68000_python-0.1.0/examples/conformance/interrupts/level-above-mask-taken.jsonl +11 -0
  69. m68000_python-0.1.0/examples/conformance/interrupts/level-at-mask-held-until-mask-lowered.json +39 -0
  70. m68000_python-0.1.0/examples/conformance/interrupts/level-at-mask-held-until-mask-lowered.jsonl +7 -0
  71. m68000_python-0.1.0/examples/conformance/interrupts/level-seven-edge-ignores-mask.json +44 -0
  72. m68000_python-0.1.0/examples/conformance/interrupts/level-seven-edge-ignores-mask.jsonl +17 -0
  73. m68000_python-0.1.0/examples/conformance/interrupts/stop-in-user-mode-privilege-violation.json +28 -0
  74. m68000_python-0.1.0/examples/conformance/interrupts/stop-in-user-mode-privilege-violation.jsonl +7 -0
  75. m68000_python-0.1.0/examples/conformance/interrupts/stop-waits-for-level-above-new-mask.json +44 -0
  76. m68000_python-0.1.0/examples/conformance/interrupts/stop-waits-for-level-above-new-mask.jsonl +8 -0
  77. m68000_python-0.1.0/examples/conformance/interrupts/trace-before-pending-interrupt.json +39 -0
  78. m68000_python-0.1.0/examples/conformance/interrupts/trace-before-pending-interrupt.jsonl +9 -0
  79. m68000_python-0.1.0/examples/conformance/interrupts/traced-stop-takes-trace.json +28 -0
  80. m68000_python-0.1.0/examples/conformance/interrupts/traced-stop-takes-trace.jsonl +7 -0
  81. m68000_python-0.1.0/examples/conformance/tas-drop.json +23 -0
  82. m68000_python-0.1.0/examples/conformance/tas-drop.jsonl +6 -0
  83. m68000_python-0.1.0/examples/interrupt_host.py +64 -0
  84. m68000_python-0.1.0/examples/minimal_m68000_host.py +36 -0
  85. m68000_python-0.1.0/examples/reference_trace.jsonl +15 -0
  86. m68000_python-0.1.0/examples/reference_trace.py +59 -0
  87. m68000_python-0.1.0/pyproject.toml +69 -0
  88. m68000_python-0.1.0/scripts/check_decoder_vs_mame.py +122 -0
  89. m68000_python-0.1.0/scripts/classify_680x0.py +125 -0
  90. m68000_python-0.1.0/scripts/coverage_report.py +1140 -0
  91. m68000_python-0.1.0/scripts/fetch_test_vectors.py +225 -0
  92. m68000_python-0.1.0/scripts/mutate.py +1338 -0
  93. m68000_python-0.1.0/scripts/run_680x0.py +78 -0
  94. m68000_python-0.1.0/scripts/run_corpus.py +67 -0
  95. m68000_python-0.1.0/scripts/smoke_installed_package.py +38 -0
  96. m68000_python-0.1.0/src/m68000_python/__init__.py +73 -0
  97. m68000_python-0.1.0/src/m68000_python/__main__.py +112 -0
  98. m68000_python-0.1.0/src/m68000_python/_alu.py +656 -0
  99. m68000_python-0.1.0/src/m68000_python/_bcd.py +137 -0
  100. m68000_python-0.1.0/src/m68000_python/_bits.py +100 -0
  101. m68000_python-0.1.0/src/m68000_python/_control.py +208 -0
  102. m68000_python-0.1.0/src/m68000_python/_core.py +545 -0
  103. m68000_python-0.1.0/src/m68000_python/_dispatch.py +243 -0
  104. m68000_python-0.1.0/src/m68000_python/_ea.py +177 -0
  105. m68000_python-0.1.0/src/m68000_python/_flags.py +152 -0
  106. m68000_python-0.1.0/src/m68000_python/_loads.py +405 -0
  107. m68000_python-0.1.0/src/m68000_python/_shifts.py +168 -0
  108. m68000_python-0.1.0/src/m68000_python/_system.py +314 -0
  109. m68000_python-0.1.0/src/m68000_python/conformance.py +796 -0
  110. m68000_python-0.1.0/src/m68000_python/console.py +254 -0
  111. m68000_python-0.1.0/src/m68000_python/cpu.py +373 -0
  112. m68000_python-0.1.0/src/m68000_python/debug.py +361 -0
  113. m68000_python-0.1.0/src/m68000_python/disasm.py +354 -0
  114. m68000_python-0.1.0/src/m68000_python/py.typed +0 -0
  115. m68000_python-0.1.0/src/m68000_python/state.py +76 -0
  116. m68000_python-0.1.0/src/m68000_python/trace.py +296 -0
  117. m68000_python-0.1.0/tests/conftest.py +61 -0
  118. m68000_python-0.1.0/tests/corpus.py +194 -0
  119. m68000_python-0.1.0/tests/disasm_mame.txt +1300 -0
  120. m68000_python-0.1.0/tests/harness.py +147 -0
  121. m68000_python-0.1.0/tests/harness_680x0.py +89 -0
  122. m68000_python-0.1.0/tests/test_bcd.py +98 -0
  123. m68000_python-0.1.0/tests/test_benchmark.py +26 -0
  124. m68000_python-0.1.0/tests/test_command_debugger.py +197 -0
  125. m68000_python-0.1.0/tests/test_conformance.py +315 -0
  126. m68000_python-0.1.0/tests/test_corpus.py +49 -0
  127. m68000_python-0.1.0/tests/test_coverage_gaps.py +1299 -0
  128. m68000_python-0.1.0/tests/test_debug_session.py +294 -0
  129. m68000_python-0.1.0/tests/test_disasm.py +127 -0
  130. m68000_python-0.1.0/tests/test_dispatch.py +52 -0
  131. m68000_python-0.1.0/tests/test_interrupts.py +210 -0
  132. m68000_python-0.1.0/tests/test_main.py +89 -0
  133. m68000_python-0.1.0/tests/test_mutation_survivors.py +140 -0
  134. m68000_python-0.1.0/tests/test_public_api.py +219 -0
  135. m68000_python-0.1.0/tests/test_readability.py +318 -0
  136. m68000_python-0.1.0/tests/test_referee_evidence.py +294 -0
  137. m68000_python-0.1.0/tests/test_register_renaming.py +349 -0
  138. m68000_python-0.1.0/tests/test_step_clocks.py +119 -0
  139. m68000_python-0.1.0/tests/test_trace_comparison.py +295 -0
  140. m68000_python-0.1.0/validation/disasm_vs_mame.py +79 -0
  141. m68000_python-0.1.0/validation/lockstep.lua +11 -0
  142. m68000_python-0.1.0/validation/lockstep.py +579 -0
  143. m68000_python-0.1.0/validation/mame_trace.py +107 -0
  144. m68000_python-0.1.0/validation/referees/build_referees.py +249 -0
  145. m68000_python-0.1.0/validation/referees/bus_errors.py +190 -0
  146. m68000_python-0.1.0/validation/referees/calibrate.py +183 -0
  147. m68000_python-0.1.0/validation/referees/musashi_referee.c +172 -0
  148. m68000_python-0.1.0/validation/referees/questions.py +429 -0
  149. m68000_python-0.1.0/validation/referees/referee.py +504 -0
  150. m68000_python-0.1.0/validation/referees/rerun_680x0.py +214 -0
  151. m68000_python-0.1.0/validation/referees/winuae_referee.cpp +498 -0
  152. m68000_python-0.1.0/validation/referees/winuae_shim.cpp +21 -0
  153. 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.