badline 0.3.0 → 0.4.1

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 (91) hide show
  1. checksums.yaml +4 -4
  2. data/.claude/hooks/session-start.sh +57 -0
  3. data/.claude/settings.json +14 -0
  4. data/CHANGELOG.md +65 -0
  5. data/CLAUDE.md +45 -16
  6. data/CONTRIBUTING.md +95 -3
  7. data/README.md +111 -61
  8. data/SECURITY.md +9 -0
  9. data/doc/pinned-behaviour.md +275 -33
  10. data/exe/badline-ruby +59 -0
  11. data/lib/badline/address_bus.rb +22 -10
  12. data/lib/badline/audio/aiff.rb +8 -3
  13. data/lib/badline/audio/cli.rb +17 -12
  14. data/lib/badline/audio/console.rb +12 -58
  15. data/lib/badline/audio/playback.rb +3 -0
  16. data/lib/badline/audio/sdl_sink.rb +20 -52
  17. data/lib/badline/audio/terminal.rb +90 -0
  18. data/lib/badline/audio.rb +2 -2
  19. data/lib/badline/cartridge/geo_ram.rb +63 -0
  20. data/lib/badline/cartridge.rb +1 -0
  21. data/lib/badline/cia/interrupt_register.rb +34 -10
  22. data/lib/badline/cia.rb +15 -8
  23. data/lib/badline/computer.rb +28 -13
  24. data/lib/badline/gui/application.rb +33 -21
  25. data/lib/badline/gui/gamepads.rb +22 -22
  26. data/lib/badline/gui/joy_map.rb +1 -1
  27. data/lib/badline/gui/key_map.rb +6 -1
  28. data/lib/badline/gui/pane.rb +7 -6
  29. data/lib/badline/gui/screen_pane.rb +9 -13
  30. data/lib/badline/gui/window.rb +35 -20
  31. data/lib/badline/gui.rb +1 -1
  32. data/lib/badline/input/mouse1351.rb +21 -6
  33. data/lib/badline/input/paddles.rb +11 -6
  34. data/lib/badline/kernal_trap/channel.rb +3 -0
  35. data/lib/badline/kernal_trap/drive/block_commands.rb +33 -4
  36. data/lib/badline/kernal_trap/drive/status.rb +3 -0
  37. data/lib/badline/kernal_trap/drive/write_file.rb +41 -0
  38. data/lib/badline/kernal_trap/drive/writes.rb +92 -0
  39. data/lib/badline/kernal_trap/drive.rb +51 -51
  40. data/lib/badline/kernal_trap/file.rb +6 -9
  41. data/lib/badline/kernal_trap/save.rb +9 -4
  42. data/lib/badline/keyboard.rb +19 -3
  43. data/lib/badline/media.rb +42 -11
  44. data/lib/badline/options.rb +209 -0
  45. data/lib/badline/ram_expansion/banking.rb +30 -0
  46. data/lib/badline/ram_expansion/plus256k.rb +38 -0
  47. data/lib/badline/ram_expansion/plus60k.rb +31 -0
  48. data/lib/badline/ram_expansion/unexpanded.rb +22 -0
  49. data/lib/badline/ram_expansion.rb +21 -0
  50. data/lib/badline/region.rb +51 -0
  51. data/lib/badline/sdl/events.rb +47 -0
  52. data/lib/badline/sdl/functions.rb +73 -0
  53. data/lib/badline/sdl.rb +67 -0
  54. data/lib/badline/sid/waveform/noise_writeback.rb +32 -14
  55. data/lib/badline/sid/waveform.rb +1 -1
  56. data/lib/badline/sid.rb +3 -1
  57. data/lib/badline/storage/d64_image.rb +10 -0
  58. data/lib/badline/storage/d71_image.rb +12 -0
  59. data/lib/badline/storage/d81_image.rb +9 -0
  60. data/lib/badline/storage/disk_image/bam.rb +111 -0
  61. data/lib/badline/storage/disk_image/directory.rb +79 -0
  62. data/lib/badline/storage/disk_image/writing.rb +110 -0
  63. data/lib/badline/storage/disk_image.rb +27 -4
  64. data/lib/badline/storage/host_directory.rb +2 -1
  65. data/lib/badline/storage.rb +15 -0
  66. data/lib/badline/time_of_day.rb +4 -2
  67. data/lib/badline/version.rb +1 -1
  68. data/lib/badline/via/control_lines.rb +104 -0
  69. data/lib/badline/via/interrupt_register.rb +58 -0
  70. data/lib/badline/via/shift_register.rb +135 -0
  71. data/lib/badline/via/timer1.rb +82 -0
  72. data/lib/badline/via/timer2.rb +84 -0
  73. data/lib/badline/via.rb +262 -0
  74. data/lib/badline/vic/bank.rb +4 -3
  75. data/lib/badline/vic/color_patches.rb +6 -3
  76. data/lib/badline/vic/display_state.rb +4 -0
  77. data/lib/badline/vic/graphics_shifter.rb +42 -7
  78. data/lib/badline/vic/registers.rb +3 -0
  79. data/lib/badline/vic/sequencer.rb +18 -18
  80. data/lib/badline/vic/sequencer_output.rb +4 -4
  81. data/lib/badline/vic/sprite/internal_bus.rb +4 -0
  82. data/lib/badline/vic/sprite/shifter.rb +25 -3
  83. data/lib/badline/vic/sprite.rb +5 -4
  84. data/lib/badline/vic/sprites.rb +8 -3
  85. data/lib/badline/vic.rb +93 -44
  86. data/lib/badline.rb +4 -0
  87. data/packaging/homebrew/badline.rb +34 -0
  88. metadata +30 -20
  89. data/exe/badline +0 -78
  90. data/exe/badline-sid +0 -34
  91. data/lib/badline/audio/options.rb +0 -137
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9348bef9831893e0aefa4d6965e1ea99a7252fb9f8f71e311f6b93b849ee1faa
4
- data.tar.gz: '01029a95df00b30c92120e7f91c2e6dc9af247a927c41e64296a6285abdb9267'
3
+ metadata.gz: b8f0c7ca349b7139fbb2c891e8a134bec1d4653ee4ff93edd9ec4088bf5c1809
4
+ data.tar.gz: 245b97ff97f8b442b3ad389198560b096b704805f5e6925601b44c45255fc299
5
5
  SHA512:
6
- metadata.gz: 8944a5873ed411c9ab156a5cedf537f8d7db1b0a06625c567a24b2b33f05c8ac6b20b45edbbaf7049b91a72da7bc1a5fc35b0c3f24e7029fda86a671315bfef5
7
- data.tar.gz: bb10fd47802aadab93ed86916ce8de6a2d17e4163ecf723a0a6d4b45085eefadd2905e2eabbfbba39530f7d0e7d3be18f0e059a17bc6c71fb69aa592ea7204a3
6
+ metadata.gz: eba9f947b6a929f46eccf02146a13778e68b7e24e2a731f31722aba4e18d49c6f89b60e8eccaace777e1d258a09b71f08d86ae51749c8bb2e4ebcd830677d57d
7
+ data.tar.gz: 391cd6122a22940dab59dc5d32fb1b4918eb6ba444cc4113ced0003ae865fe31fadfbfbd9515063bb466f8c473d02422e7a03bb06fbbf6ef2f049ca2261851d5
@@ -0,0 +1,57 @@
1
+ #!/bin/bash
2
+ # Prepares a Claude Code on the web container to run `bundle exec rubocop`
3
+ # and `bundle exec rspec`. The regression suites need vendor/ and aren't set
4
+ # up here.
5
+ set -euo pipefail
6
+
7
+ if [ "${CLAUDE_CODE_REMOTE:-}" != "true" ]; then
8
+ exit 0
9
+ fi
10
+
11
+ cd "${CLAUDE_PROJECT_DIR:-$(dirname "$0")/../..}"
12
+
13
+ # The SDL specs open libSDL2 through Fiddle, so only the runtime library is
14
+ # needed, as in CI.
15
+ if ! ldconfig -p | grep 'libSDL2-2\.0\.so\.0' >/dev/null; then
16
+ export DEBIAN_FRONTEND=noninteractive
17
+ apt-get update -qq
18
+ apt-get install -y -qq libsdl2-2.0-0 >/dev/null
19
+ fi
20
+
21
+ # The image ships Ruby 3.3 and an rbenv. Install the newest Ruby 4.0.x, as
22
+ # CI's ruby-version "4.0" picks, from the prebuilt binaries setup-ruby uses,
23
+ # unless a 4.0 is installed already.
24
+ export RBENV_ROOT=/opt/rbenv
25
+ export PATH="$RBENV_ROOT/bin:$RBENV_ROOT/shims:$PATH"
26
+ ruby_version=$(rbenv versions --bare | grep -E '^4\.0\.[0-9]+$' | sort -V | tail -1 || true)
27
+ if [ -z "$ruby_version" ]; then
28
+ ruby_version=$(curl -fsSL https://raw.githubusercontent.com/ruby/setup-ruby/master/ruby-builder-versions.json |
29
+ ruby -rjson -e 'puts JSON.parse($stdin.read)["ruby"].grep(/\A4\.0\.\d+\z/).max_by { Gem::Version.new(_1) }')
30
+ # The binaries only run from the prefix they were built with, so unpack
31
+ # there and link it into rbenv.
32
+ . /etc/os-release
33
+ prefix="/opt/hostedtoolcache/Ruby/$ruby_version/x64"
34
+ mkdir -p "$prefix"
35
+ if curl -fsSL "https://github.com/ruby/ruby-builder/releases/download/ruby-$ruby_version/ruby-$ruby_version-ubuntu-$VERSION_ID-x64.tar.gz" |
36
+ tar -xz -C "$prefix" --strip-components=1; then
37
+ ln -sfn "$prefix" "$RBENV_ROOT/versions/$ruby_version"
38
+ else
39
+ # No binary for this Ubuntu release: build from source, which takes minutes.
40
+ rm -rf "$prefix"
41
+ git -C "$RBENV_ROOT/plugins/ruby-build" pull --ff-only -q || true
42
+ MAKE_OPTS="-j$(nproc)" rbenv install --skip-existing "$ruby_version"
43
+ fi
44
+ fi
45
+ rbenv global "$ruby_version"
46
+ rbenv rehash
47
+
48
+ # Put rbenv's shims ahead of /usr/local/bin/ruby for the session.
49
+ if [ -n "${CLAUDE_ENV_FILE:-}" ]; then
50
+ {
51
+ echo "export RBENV_ROOT=$RBENV_ROOT"
52
+ echo "export PATH=\"$RBENV_ROOT/bin:$RBENV_ROOT/shims:\$PATH\""
53
+ } >> "$CLAUDE_ENV_FILE"
54
+ fi
55
+
56
+ # Bundler switches to the version Gemfile.lock was bundled with.
57
+ bundle install --jobs "$(nproc)"
@@ -0,0 +1,14 @@
1
+ {
2
+ "hooks": {
3
+ "SessionStart": [
4
+ {
5
+ "hooks": [
6
+ {
7
+ "type": "command",
8
+ "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/session-start.sh"
9
+ }
10
+ ]
11
+ }
12
+ ]
13
+ }
14
+ }
data/CHANGELOG.md CHANGED
@@ -1,5 +1,70 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.4.1](https://github.com/elektronaut/badline/compare/v0.4.0...v0.4.1) (2026-09-28)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * build the Homebrew pack with Spinel d3182102 ([42b42d5](https://github.com/elektronaut/badline/commit/42b42d506270f16cfb033c7d538e75131f7c74c2))
9
+
10
+ ## [0.4.0](https://github.com/elektronaut/badline/compare/v0.3.0...v0.4.0) (2026-09-28)
11
+
12
+ ### Upgrading
13
+
14
+ * The gem's executable is now `badline-ruby`. The name `badline` now belongs to the native build, which installs with `brew install elektronaut/tap/badline` on macOS.
15
+ * `badline-sid` is gone. Play a tune with `badline-ruby --headless tune.sid` (or `badline --headless tune.sid`), and render one with `--audio-out out.wav`.
16
+
17
+ ### ⚠ BREAKING CHANGES
18
+
19
+ * the `badline-sid` executable is gone. Use `badline-ruby --headless tune.sid` to play a tune and `badline-ruby tune.sid --audio-out out.wav` to render one. badline-sid's `-o`/`--output` option and positional output file became `--audio-out`, which has no short form. Badline::Audio::Options is now Badline::Options.
20
+ * the gem's `badline` executable is now `badline-ruby`. There is no `badline` shim.
21
+
22
+ ### Features
23
+
24
+ * add a Homebrew formula for the native badline ([33c2e98](https://github.com/elektronaut/badline/commit/33c2e983a12275195383e33230137d461b1a9fd0))
25
+ * add mouse and paddle input to the native window ([f468c21](https://github.com/elektronaut/badline/commit/f468c2189b3b213561516a39ec0ffcedcb8c5b89))
26
+ * emulate GEO-RAM and run the testbench geo512k rows ([9a5395f](https://github.com/elektronaut/badline/commit/9a5395fd34ad42d21e08a3b498038850d790f253)), closes [#239](https://github.com/elektronaut/badline/issues/239)
27
+ * emulate the +60K and +256K RAM expansions ([9d60488](https://github.com/elektronaut/badline/commit/9d60488d01bd33c9d090f75f4765931f21911247)), closes [#240](https://github.com/elektronaut/badline/issues/240)
28
+ * fold badline-sid into badline-ruby ([443d155](https://github.com/elektronaut/badline/commit/443d1559bb5253d58f5378e7e212beda5b734bda))
29
+ * give the native badline badline-ruby's command-line options ([bd2ec0d](https://github.com/elektronaut/badline/commit/bd2ec0da269a95eccb37198dd3e03faf84651396))
30
+ * headless .sid options for the native badline ([4354e80](https://github.com/elektronaut/badline/commit/4354e80e5b61da9148a2077d1e0557ea23a833df))
31
+ * make the Spinel window the native badline executable ([54e8e71](https://github.com/elektronaut/badline/commit/54e8e7151e5ced11e536da6baaf779af7f4eae16))
32
+ * model the 6526A CIA ([18a2012](https://github.com/elektronaut/badline/commit/18a201253c4612bb2ae821e544c9e6f3a6e1fb8c))
33
+ * model the 8565 VIC-II ([e81b7fb](https://github.com/elektronaut/badline/commit/e81b7fb7053c8cca1b94dda66cda3357d2984aa1))
34
+ * mount disk images read-only ([c35b808](https://github.com/elektronaut/badline/commit/c35b80852da7ebcf92cbf7de9478d62ba9197602))
35
+ * pace the native window by vsync ([1fa4661](https://github.com/elektronaut/badline/commit/1fa4661b480cba2a79772096ee32327098719f1b))
36
+ * pack the native badline with spin pack (rake native:pack) ([1d2288b](https://github.com/elektronaut/badline/commit/1d2288bd4f9c6589a35a0f99f8f1b0baeaa8e69a))
37
+ * read SDL game controllers in the native window ([a80403c](https://github.com/elektronaut/badline/commit/a80403cefa5b75f4edbf9157631ff5eac9c234f9))
38
+ * rename the executable to badline-ruby ([6050d9d](https://github.com/elektronaut/badline/commit/6050d9db2376ada76af5068b1e9703162adb90b3))
39
+ * replace ruby-sdl2 with a Fiddle binding to libSDL2 ([f3a4b84](https://github.com/elektronaut/badline/commit/f3a4b8429330efaf2abfe2592e1df2fd0791a9ee)), closes [#253](https://github.com/elektronaut/badline/issues/253)
40
+ * run the C64/ and general/ testbench rows as testbench-general ([53316c0](https://github.com/elektronaut/badline/commit/53316c03cd87e9e72d92ff8a6a9e13b6d0eae807))
41
+ * run the Lorenz chain on a Spinel build with rake spinel:lorenz ([955d722](https://github.com/elektronaut/badline/commit/955d722a06865f6a704f14570d7b246963317551))
42
+ * run the SID testprogs on a Spinel build with rake spinel:sidtests ([ecff9ba](https://github.com/elektronaut/badline/commit/ecff9bafa8b3621454e540d92707c0c139c7f7fd))
43
+ * run the testbench suites on a Spinel build with rake spinel:testbench ([083911a](https://github.com/elektronaut/badline/commit/083911af7194c56992f529f67c4bf101a44fb450))
44
+ * turn sound on by default in the native badline ([c4f65cf](https://github.com/elektronaut/badline/commit/c4f65cfa73fcad49399226f238b8b5b46799ed30))
45
+ * type RESTORE, ↑, ← and cursor up/left from the host keyboard ([74623e3](https://github.com/elektronaut/badline/commit/74623e346d4ccad26f779d3f0c9f0c11426b96c3)), closes [#234](https://github.com/elektronaut/badline/issues/234)
46
+ * write to disk images and swap them at runtime ([a6eebf5](https://github.com/elektronaut/badline/commit/a6eebf5e304186c8512e5cda4f6514a92ac64395)), closes [#233](https://github.com/elektronaut/badline/issues/233)
47
+
48
+ ### Bug Fixes
49
+
50
+ * end a testbench run on the cycle the test writes its exit code ([58aa8c5](https://github.com/elektronaut/badline/commit/58aa8c52e639945a78cac40330e15a686081d8ec))
51
+ * keep pixel 0 black out of an invalid mode into hi-res text on the 8565 ([5a6ff96](https://github.com/elektronaut/badline/commit/5a6ff96192455b9f3ca9fd8904aa9bb4760e99cc))
52
+ * leave a PRG's end address in $AE/$AF on autostart ([d76ed18](https://github.com/elektronaut/badline/commit/d76ed1840850045ac7a1c1d3dc5c6d5d99685fdd))
53
+ * leave the VIC's phi1 byte in the RAM under the CPU port ([55fc2f5](https://github.com/elektronaut/badline/commit/55fc2f52cd0d0a0edeb56cde140e67fb1ab50077))
54
+ * make the mouse speed independent of the window size ([5cbabbf](https://github.com/elektronaut/badline/commit/5cbabbfade4e53c8aae265f2493466419ef67958)), closes [#280](https://github.com/elektronaut/badline/issues/280)
55
+ * make the VIC's collision registers read-only ([141526c](https://github.com/elektronaut/badline/commit/141526ce755bf47582a25e2c209cdc950ff81f0b))
56
+ * name the release tag in the native --version ([5654433](https://github.com/elektronaut/badline/commit/56544334b43ed992d0d54215a99a7be50fa74240))
57
+ * read unused bits of $D016 and $D018 as 1 ([00a816b](https://github.com/elektronaut/badline/commit/00a816b9bdbe88c7f838f941b2ef2dc5cd7db075)), closes [#237](https://github.com/elektronaut/badline/issues/237)
58
+ * say SDL2 is missing instead of raising from badline-ruby ([0f5ec9c](https://github.com/elektronaut/badline/commit/0f5ec9c7fabfa922ecb4c87339ba3ccc0f09bca0))
59
+ * SID test bit writeback for noiselfsrinit, F->8 and the 8580's C->F ([bdb063f](https://github.com/elektronaut/badline/commit/bdb063f1aba3f1714e26bc3e8301adf9bc33fb3e)), closes [#238](https://github.com/elektronaut/badline/issues/238)
60
+ * suggest the SDL2 runtime package when badline-ruby can't find it ([44a3375](https://github.com/elektronaut/badline/commit/44a3375973aafc0ec7aba672609517dd6571f85c))
61
+ * **vic:** read the idle byte at $38ff where a DMA delay starts ([f3892aa](https://github.com/elektronaut/badline/commit/f3892aa0ac45fbc493bccd0f5980dfe7fc659848)), closes [#235](https://github.com/elektronaut/badline/issues/235)
62
+
63
+ ### Performance Improvements
64
+
65
+ * **vic:** check the DMA-delay idle byte only when display state opens ([8d9b777](https://github.com/elektronaut/badline/commit/8d9b777b3554ed4d6bef67ac41c0a344c3b0a35b))
66
+
67
+
3
68
  ## [0.3.0](https://github.com/elektronaut/badline/compare/v0.2.1...v0.3.0) (2026-09-24)
4
69
 
5
70
  Sound is the headline: `badline --sound` plays the SID in the emulator window and paces emulation to the audio device. It is off by default, because the emulator still runs below real time on CRuby and the sound stutters there. This release also brings the virtual drive closer to a real 1541 (buffer channels in drive RAM, DOS command parsing, the BAM in buffer 4, `LOAD"*"`), cartridge banking fixes, and `Badline::Checkpoint` with `bin/machine_diff` for checking that a change leaves emulation alone.
data/CLAUDE.md CHANGED
@@ -18,18 +18,22 @@ live in subdirectories:
18
18
  modes, border and register timing
19
19
  - **CIA**: `cia.rb` and `cia/` (timers, serial), plus `time_of_day.rb`
20
20
  - **SID**: `sid.rb` and `sid/`. `audio/` plays and renders tunes for
21
- `exe/badline-sid`
21
+ `badline-ruby --headless` and `--audio-out`
22
22
  - **Media and host I/O**: `storage/` (disk, tape and cartridge image
23
23
  formats), `cartridge/` (mappers), `kernal_trap/` (the LOAD/SAVE and IEC
24
24
  traps that stand in for a drive), `media.rb` (attach and autostart),
25
25
  `datasette.rb`, and `gui/` and `input/` (SDL front end, controllers)
26
+ - **Native**: `native/` holds the native `badline`, the core compiled with
27
+ Spinel in an SDL2 window (`rake native:build`, `native/README.md`).
28
+ `spinel/` holds the Spinel test harnesses
26
29
 
27
30
  Timing-critical code lives in `cpu`, `interrupts`, `vic`, `cia` and `sid` and
28
31
  in the order `Computer#cycle!` clocks them. Changes there move the test
29
32
  suites below. `gui/` changes move none of them.
30
33
 
31
34
  Namespaced groups live in `lib/badline/<namespace>/`, with a sibling
32
- `lib/badline/<namespace>.rb` that requires the members. `lib/badline.rb`
35
+ `lib/badline/<namespace>.rb` that requires the members, directly or through
36
+ other members. `lib/badline.rb`
33
37
  requires only the namespace file.
34
38
 
35
39
  ### Oracles
@@ -60,7 +64,7 @@ and speed work.
60
64
 
61
65
  ## Driving the emulator headlessly
62
66
 
63
- `exe/badline <media>` opens the SDL window, which is no use for checking
67
+ `exe/badline-ruby <media>` opens the SDL window, which is no use for checking
64
68
  work. Drive a `Badline::Computer` from Ruby instead, as the `bin/` runners do:
65
69
 
66
70
  ```ruby
@@ -119,11 +123,12 @@ worktree and owns a different set of files.
119
123
 
120
124
  - `rake vendor:checkout` fetches SingleStepTests and VICE-testprogs into
121
125
  `vendor/`
122
- - `bundle exec rspec`: line coverage is about 95%. `spec/spec_helper.rb`
126
+ - `bundle exec rspec`: line coverage is about 97%. `spec/spec_helper.rb`
123
127
  fails a whole-suite run (every spec file, no filters) below 90%, while
124
128
  single-file and filtered runs skip the floor. `:slow` specs are excluded
125
129
  by default: run them with `bundle exec rspec --tag slow`
126
- - `rake test`: SingleStepTests. Run it for any CPU change
130
+ - `rake test`: SingleStepTests, plus the Minitest tests for the regression
131
+ runners in `test/`. Run it for any CPU change
127
132
 
128
133
  The headless suites are expensive. `bin/testbench` forks over 4 shards by
129
134
  default. `SHARDS=N rake regression:<suite>` or `bin/testbench --shards N`
@@ -131,7 +136,7 @@ overrides that, capped by the core count, but keep the default, because
131
136
  other worktrees share the machine. Whole runs at 4 shards on an M-series
132
137
  laptop take 15 min for `testbench` (`VICII/`), 11 for `testbench-cia`, 1 for
133
138
  `testbench-interrupts`, 37 for `testbench-irqdma`, 11 for
134
- `testbench-cpu` and 2 for `testbench-carts`. `sid` takes about 12 min. `lorenz` chains itself and takes
139
+ `testbench-cpu`, 2 for `testbench-carts` and 10 for `testbench-expansions`. `sid` takes about 12 min. `lorenz` chains itself and takes
135
140
  about 2.5 h on CI whole. `rake regression:lorenz-1` to `lorenz-4` run it as
136
141
  four stretches of about 40 min each, and they can run side by side.
137
142
  `test/baselines/README.md` has the full table. A killed `bin/testbench` run
@@ -164,17 +169,23 @@ the rows your change can't reach tell you nothing about it.
164
169
  nightly run. It also runs from the Actions tab on demand. It never runs
165
170
  on push or on pull requests, so one verdict can cover a day's merges.
166
171
  The `testbench-*` and `sid-8580` suites run from the Actions tab on
167
- demand
172
+ demand. The Spinel workflow runs the Spinel suites on pull requests
173
+ that touch emulation, harness or build paths. Its jobs aren't required
174
+ checks yet, and the CRuby Regression nightly runs as before
168
175
  - The one exception is a change whose reach you can't bound to a set of
169
176
  filters, such as reordering `Computer#cycle!` or changing the LOAD trap
170
177
  every suite loads through. Ask before running a full suite for it, and
171
178
  don't start one on your own judgement
172
179
 
173
- Pick the filters from the suites your change can move: VIC → `testbench`;
180
+ Pick the filters from the suites your change can move: VIC → `testbench`,
181
+ plus `testbench-vicii-new` (`bin/testbench --vicii-new`, the 8565) for
182
+ anything the VIC model reaches;
174
183
  CPU, interrupts or timing → the matching `testbench-*` suite, plus
175
184
  `rake test` for CPU; CIA → the slow CIA specs first, then the matching
176
- `testbench-cia` rows; cartridge mappers, banking or power-on state →
177
- `testbench-carts`; SID → `sid`, plus `sid-8580` for anything the 8580
185
+ `testbench-cia` rows, plus `testbench-cia-new` (`bin/testbench --cia-new`,
186
+ the 6526A) for anything the interrupt register or the CIA model reaches; cartridge mappers, banking or power-on state →
187
+ `testbench-carts`, plus `testbench-expansions` for banking; GEO-RAM, +60K
188
+ or +256K → `testbench-expansions`; SID → `sid`, plus `sid-8580` for anything the 8580
178
189
  model reaches (`bin/sidtests --sid 8580`). Lorenz isn't a per-change check:
179
190
  its full chain runs nightly, and the planner assigns any row it moves. The
180
191
  exception is code whose rule in `doc/pinned-behaviour.md` names Lorenz
@@ -207,17 +218,35 @@ you change code a pinned rule governs, re-derive the rule against its test
207
218
  and update the doc if the rule moves. A green suite alone isn't enough,
208
219
  because the suite can stay green while the rule breaks.
209
220
 
221
+ ## Issues and pull requests
222
+
223
+ Before filing an issue or opening a pull request, read CONTRIBUTING.md and follow it. Use the exact headings from its skeletons. Report only what you observed or verified, and don't include hypotheses about causes. Open an issue before writing non-trivial code; only changes with one obvious fix (typos, broken links, clear-cut fixes) go straight to a pull request.
224
+
225
+ ### Working from issues
226
+
227
+ Open issues are agreed work. An agent told to pick issues works this way:
228
+
229
+ - Pick an open issue without the `in-progress` label whose dependencies
230
+ (named in its body) have merged. Add the label and a comment saying you've
231
+ taken it before you start
232
+ - Work in a worktree from `origin/main` as described under *Working in
233
+ parallel*. The issue's text is your brief. If it turns out wrong or
234
+ blocked, comment on the issue and stop rather than widening the scope
235
+ - Open one pull request per issue with `Closes #N` in its body, and run
236
+ only the rows your change can reach. Picking an issue is permission to
237
+ commit, push and open that pull request
238
+ - Never merge, and never enable auto-merge. The planner session reviews
239
+ every pull request, answers through PR reviews, and merges
240
+ - Fix review findings on the same branch. If you drop an issue, remove
241
+ the label and say why in a comment
242
+
210
243
  ## Git
211
244
 
212
- - **Don't stage, commit, push or open a PR** unless your task says so in
213
- those words. Wanting a PR, a finished feature or green CI doesn't count
214
- as permission. Finish the work, report what changed, and stop. A dirty
215
- tree is the expected end state
216
245
  - Hunks are staged by hand during review, so a partially staged file is
217
246
  deliberate. Use `git diff HEAD` to see everything
218
247
  - Commit messages use Conventional Commits (`feat:`, `fix:`, `chore:`, …).
219
248
  release-please derives version bumps and the changelog from the prefixes
220
249
  - No `Co-Authored-By` or `Claude-Session` trailers, even where `git log`
221
250
  shows them
222
- - When a task explicitly asks for a PR, the branch is already from
223
- `origin/main` (see above). Open it with `gh pr create --base main`
251
+ - Open pull requests against `main`, from a branch cut from `origin/main`
252
+ (see above): `gh pr create --base main`
data/CONTRIBUTING.md CHANGED
@@ -1,16 +1,58 @@
1
1
  # Contributing
2
2
 
3
+ This guide covers bug reports, feature requests, and pull requests. It applies to everyone, including AI agents filing on someone's behalf.
4
+
5
+ In the GitHub web interface, issue forms guide you through the structure. If you file through the API or the `gh` CLI, the forms are bypassed: copy the matching skeleton from [Skeletons](#skeletons) and keep the headings exactly as written.
6
+
3
7
  Bug reports and pull requests are welcome on
4
8
  [GitHub](https://github.com/elektronaut/badline). Everyone participating
5
9
  is expected to follow the [code of conduct](CODE_OF_CONDUCT.md).
6
10
 
11
+ ## Principles
12
+
13
+ ### For all issues and pull requests
14
+
15
+ **Report only what you observed or verified.** Whoever investigates, human or agent, anchors on the claims in a report. Unverified claims cost more time than they save.
16
+
17
+ **No hypotheses about causes.** Diagnosis is the maintainers' job. They add theories as comments, where the author shows how much weight to give them. Speculation in a report misleads everyone who reads it.
18
+
19
+ **Keep it short.** Length hides the useful information. A long report usually means two reports, or padding.
20
+
21
+ **One concern per issue or pull request.** Mixed concerns can't be tracked, reviewed, or reverted independently.
22
+
23
+ **Use the exact headings.** Maintainers and their tools rely on them to process reports.
24
+
25
+ ### Bug reports
26
+
27
+ **Minimal, numbered steps you actually ran.** A reproduction nobody has run is speculation too.
28
+
29
+ **Raw output, not a paraphrase.** The details that matter are the ones a summary drops.
30
+
31
+ **Ruled out: negative results only.** "Still happens with caching disabled" narrows the search without steering it.
32
+
33
+ ### Feature requests
34
+
35
+ **Problem before solution.** A proposed solution constrains the design before anyone familiar with the code has weighed in.
36
+
37
+ ### Pull requests
38
+
39
+ **Open an issue before writing non-trivial code.** Code is cheap to write; choosing the right change is the expensive part. An issue lets maintainers weigh in on the approach before anything is built, while an unsolicited implementation anchors the discussion on one solution. Open a pull request directly only when there's one obvious way to make the change: typos, broken links, or a clear-cut fix. Reporting those costs more than fixing them. They don't need the template; a one-line description is ideal.
40
+
41
+ **Describe what changed, not what it might fix.** The diff shows how. Broad claims steer reviewers the same way hypotheses steer investigators.
42
+
43
+ ### Security
44
+
45
+ **Never report vulnerabilities publicly.** Report them privately as described in [SECURITY.md](SECURITY.md).
46
+
7
47
  ## Getting started
8
48
 
9
- Badline needs SDL2, which is available from most package managers.
49
+ Badline needs the SDL2 library, which is available from most package
50
+ managers. Building the native `badline` also needs the SDL2 headers
51
+ (`libsdl2-dev` on Debian/Ubuntu); see [native/README.md](native/README.md).
10
52
 
11
53
  ```sh
12
54
  brew install sdl2 # macOS
13
- apt install libsdl2-dev # Debian/Ubuntu
55
+ apt install libsdl2-2.0-0 # Debian/Ubuntu
14
56
  ```
15
57
 
16
58
  Install the dependencies and run the specs:
@@ -40,7 +82,7 @@ Check style before pushing:
40
82
  bundle exec rubocop
41
83
  ```
42
84
 
43
- ## Pull requests
85
+ ## Tests and commits
44
86
 
45
87
  - Add tests for any behavior you change.
46
88
  - Write commit messages using
@@ -49,3 +91,53 @@ bundle exec rubocop
49
91
  `fix:` prefixes decide what ends up in the next release.
50
92
  - Leave the version and `CHANGELOG.md` alone. Both are updated
51
93
  automatically when a release is cut.
94
+
95
+ ## Skeletons
96
+
97
+ Leave out optional sections you have nothing for.
98
+
99
+ ### Bug report
100
+
101
+ ```markdown
102
+ ### Expected behavior
103
+
104
+ <!-- What should happen. -->
105
+
106
+ ### Actual behavior
107
+
108
+ <!-- What happened instead, as observed. Include the error message, if any. -->
109
+
110
+ ### Steps to reproduce
111
+
112
+ 1.
113
+ 2.
114
+ 3.
115
+
116
+ ### Error output
117
+
118
+ <!-- Optional. Raw stack trace or log lines in a code block. -->
119
+
120
+ ### Ruled out
121
+
122
+ <!-- Optional. Negative results only, e.g. "Still happens with caching disabled." -->
123
+ ```
124
+
125
+ ### Feature request
126
+
127
+ ```markdown
128
+ ### Problem
129
+
130
+ <!-- What you're trying to do, and what's blocking you. -->
131
+
132
+ ### Current workaround
133
+
134
+ <!-- Optional. How you get around it today, if at all. -->
135
+
136
+ ### Proposed solution
137
+
138
+ <!-- Optional, and brief. -->
139
+ ```
140
+
141
+ ### Pull request
142
+
143
+ See [.github/pull_request_template.md](.github/pull_request_template.md).