badline 0.6.0 → 0.7.0

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 (108) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +77 -0
  3. data/CLAUDE.md +10 -9
  4. data/README.md +135 -42
  5. data/doc/pinned-behaviour.md +131 -30
  6. data/exe/badline-ruby +28 -24
  7. data/lib/badline/address_bus/extra_sids.rb +30 -0
  8. data/lib/badline/address_bus/sid_slots.rb +42 -0
  9. data/lib/badline/address_bus.rb +15 -1
  10. data/lib/badline/audio/aiff.rb +2 -2
  11. data/lib/badline/audio/bare_player.rb +23 -12
  12. data/lib/badline/audio/cli.rb +167 -59
  13. data/lib/badline/audio/console.rb +2 -1
  14. data/lib/badline/audio/jukebox.rb +121 -36
  15. data/lib/badline/audio/machine_player.rb +25 -10
  16. data/lib/badline/audio/pcm_writer.rb +10 -6
  17. data/lib/badline/audio/playback.rb +6 -5
  18. data/lib/badline/audio/queued_tune.rb +89 -0
  19. data/lib/badline/audio/renderer.rb +57 -10
  20. data/lib/badline/audio/stereo.rb +97 -0
  21. data/lib/badline/audio/terminal.rb +39 -6
  22. data/lib/badline/audio/wav.rb +1 -1
  23. data/lib/badline/audio.rb +2 -3
  24. data/lib/badline/computer.rb +6 -3
  25. data/lib/badline/cpu/boundary.rb +54 -0
  26. data/lib/badline/cpu/core.rb +165 -0
  27. data/lib/badline/cpu.rb +3 -214
  28. data/lib/badline/drive1541/cpu.rb +29 -0
  29. data/lib/badline/drive1541.rb +1 -0
  30. data/lib/badline/ffi.rb +157 -0
  31. data/lib/badline/frontend/app.rb +225 -0
  32. data/lib/badline/frontend/audio_sink.rb +73 -0
  33. data/lib/badline/frontend/boot.rb +52 -0
  34. data/lib/badline/frontend/buttons.rb +90 -0
  35. data/lib/badline/frontend/controls.rb +163 -0
  36. data/lib/badline/frontend/drive_led.rb +51 -0
  37. data/lib/badline/frontend/filter_view.rb +96 -0
  38. data/lib/badline/frontend/frame_rate.rb +83 -0
  39. data/lib/badline/frontend/gamepads.rb +76 -0
  40. data/lib/badline/frontend/info_view.rb +120 -0
  41. data/lib/badline/frontend/keys.rb +48 -0
  42. data/lib/badline/frontend/pacer.rb +100 -0
  43. data/lib/badline/frontend/pad_port.rb +61 -0
  44. data/lib/badline/frontend/painter.rb +175 -0
  45. data/lib/badline/frontend/player_commands.rb +33 -0
  46. data/lib/badline/frontend/player_footer.rb +86 -0
  47. data/lib/badline/frontend/player_header.rb +60 -0
  48. data/lib/badline/frontend/player_screen.rb +124 -0
  49. data/lib/badline/frontend/player_state.rb +67 -0
  50. data/lib/badline/frontend/player_window.rb +228 -0
  51. data/lib/badline/frontend/scope.rb +42 -0
  52. data/lib/badline/frontend/screen.rb +65 -0
  53. data/lib/badline/frontend/screenshot.rb +22 -0
  54. data/lib/badline/frontend/sid_history.rb +175 -0
  55. data/lib/badline/frontend/sid_view.rb +140 -0
  56. data/lib/badline/frontend/snapshots.rb +60 -0
  57. data/lib/badline/frontend/sound.rb +153 -0
  58. data/lib/badline/frontend/timeline.rb +171 -0
  59. data/lib/badline/frontend/visualizer_view.rb +101 -0
  60. data/lib/badline/frontend.rb +32 -0
  61. data/lib/badline/media/queue.rb +158 -0
  62. data/lib/badline/media.rb +13 -8
  63. data/lib/badline/options/event.rb +94 -0
  64. data/lib/badline/options/help.rb +107 -0
  65. data/lib/badline/options/option.rb +29 -0
  66. data/lib/badline/options/table.rb +64 -0
  67. data/lib/badline/options/validation.rb +54 -0
  68. data/lib/badline/options.rb +204 -149
  69. data/lib/badline/reu/dma/saved_state.rb +50 -0
  70. data/lib/badline/reu/dma.rb +56 -47
  71. data/lib/badline/reu/saved_state.rb +2 -1
  72. data/lib/badline/reu.rb +19 -10
  73. data/lib/badline/sdl.rb +133 -51
  74. data/lib/badline/sid/catch_up.rb +11 -1
  75. data/lib/badline/sid/filter.rb +9 -0
  76. data/lib/badline/sid/voice.rb +6 -0
  77. data/lib/badline/sid/waveform/noise_writeback.rb +33 -11
  78. data/lib/badline/sid/waveform.rb +22 -8
  79. data/lib/badline/sid.rb +35 -3
  80. data/lib/badline/snapshot/state.rb +1 -1
  81. data/lib/badline/storage/hvsc.rb +41 -0
  82. data/lib/badline/storage/sid_file.rb +45 -25
  83. data/lib/badline/storage/song_lengths.rb +6 -29
  84. data/lib/badline/storage/stil/credit.rb +50 -0
  85. data/lib/badline/storage/stil.rb +126 -0
  86. data/lib/badline/storage.rb +8 -0
  87. data/lib/badline/version.rb +1 -1
  88. data/lib/badline/vic/bank.rb +21 -0
  89. data/lib/badline/vic.rb +15 -6
  90. metadata +49 -19
  91. data/.claude/hooks/session-start.sh +0 -57
  92. data/.claude/settings.json +0 -14
  93. data/lib/badline/audio/sdl_sink.rb +0 -70
  94. data/lib/badline/audio/stream.rb +0 -123
  95. data/lib/badline/gui/application.rb +0 -243
  96. data/lib/badline/gui/drive_led_pane.rb +0 -29
  97. data/lib/badline/gui/gamepads.rb +0 -104
  98. data/lib/badline/gui/joy_map.rb +0 -27
  99. data/lib/badline/gui/key_map.rb +0 -39
  100. data/lib/badline/gui/palette.rb +0 -35
  101. data/lib/badline/gui/pane.rb +0 -36
  102. data/lib/badline/gui/screen_pane.rb +0 -46
  103. data/lib/badline/gui/snapshots.rb +0 -67
  104. data/lib/badline/gui/window.rb +0 -61
  105. data/lib/badline/gui.rb +0 -14
  106. data/lib/badline/sdl/events.rb +0 -49
  107. data/lib/badline/sdl/functions.rb +0 -74
  108. data/vendor/.gitkeep +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b238dd5194dd5fc7783779eced6e4b718b73890e81d78566d12c26c08a379597
4
- data.tar.gz: 4a47225b0d7e2a055674ce9f4f61cd2bc1064413a2c4c8b6bf228ed99f4dde07
3
+ metadata.gz: 41235fb28f9f33c9a88333beec1c09cd9825d45875f24d22b8cb5f66f9528467
4
+ data.tar.gz: c214b37efce97c404dcd0838283e65e0f42aaa86a8376f18ef2323933b01b2f2
5
5
  SHA512:
6
- metadata.gz: c636911838f2d65ae88c6b4e940085790c54573c2cc6a9d97450b68f2319eb374f3330e75243487bd7538b56d5cb695f2fe085739ead20db06eb3f24b9169c45
7
- data.tar.gz: c48b8ecfe80b51a178a757246adba3ea267901db096ccb74c28dbc6482a9ff1f71193c26b4829e98744bcd6847f5fe3f073ff01275604d8383dd36dd91f50f69
6
+ metadata.gz: ab2c530282a35d8ac65c217d6901d46c6f844cde1d7c7ca143acb8ea8f05ac08d1da6b8ee290fb41bd016ce2079e308bfd5c5f338be18ed269be693612e07a50
7
+ data.tar.gz: 6c27c6d41e73e495bb27e536fae7424b5524c12b9c1d12cdb3bccc2efb3a73be6137e5209c4356394594d71ceaa205309989f193344003a3ed65245df0ec74fa
data/CHANGELOG.md CHANGED
@@ -1,5 +1,82 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.7.0](https://github.com/elektronaut/badline/compare/v0.6.1...v0.7.0) (2026-10-02)
4
+
5
+
6
+ ### ⚠ BREAKING CHANGES
7
+
8
+ * call a .sid tune's parts subtunes everywhere
9
+
10
+ ### Features
11
+
12
+ * add a badline sid subcommand that plays tunes and directories ([7dfc21b](https://github.com/elektronaut/badline/commit/7dfc21b5e4ba47cd37a4593da61e95bb1d2329ab))
13
+ * drop .sid files and folders on the SID player to queue them ([cc1f434](https://github.com/elektronaut/badline/commit/cc1f434b7404ded067ef43d1d3d5aa53d338de4c))
14
+ * end a subtune with no known length when it falls silent ([46c4f10](https://github.com/elektronaut/badline/commit/46c4f1064f6382872ec22989253bc9c159f78dcf))
15
+ * open a .sid tune, or no tune at all, in the SID player ([396c010](https://github.com/elektronaut/badline/commit/396c010ee626f95fbb71ced8bf3905909c470c8e))
16
+ * play 2SID and 3SID tunes in stereo in the SID player ([7c72eef](https://github.com/elektronaut/badline/commit/7c72eeff7e5e7b1c56286f9bf6030ae3408f7176))
17
+ * play a queue of tunes, with shuffle, loop and play-all subtunes ([d163106](https://github.com/elektronaut/badline/commit/d163106eea99d90f6ffe74fb91474a2aca4c64eb))
18
+ * play sid tunes in a window that shows what the SID is doing ([60bfd3f](https://github.com/elektronaut/badline/commit/60bfd3f75fe82d928afa374d257d3d21e5ac29ad))
19
+ * seek forward from where the subtune is, not from its start ([34059e8](https://github.com/elektronaut/badline/commit/34059e8f9b12cab44c94124e6455fef9c915d8eb))
20
+ * seek within a subtune, and let the slow warning lapse ([b98ac6e](https://github.com/elektronaut/badline/commit/b98ac6eb6f9c0ce088545f55cc5f2445dd38dd2d))
21
+ * show a tune's STIL entry in the SID player ([82d9dca](https://github.com/elektronaut/badline/commit/82d9dca26d7cefe853a6af41e50fc723a3e90170))
22
+ * show each voice's live output in the SID view ([c61a128](https://github.com/elektronaut/badline/commit/c61a1289983887c0175fca3dc4c37b470e96de92))
23
+ * show HVSC's STIL in the SID player ([bd55c0c](https://github.com/elektronaut/badline/commit/bd55c0c87695ba8cc84dbfd51ab2119a97eccded))
24
+ * show where a seek is headed on the SID player's bar ([1a84b37](https://github.com/elektronaut/badline/commit/1a84b3746a960dba28dc9c789b3d2cd1520d2f94))
25
+ * step subtunes and tunes on separate keys, add --all-subtunes ([eaef338](https://github.com/elektronaut/badline/commit/eaef3384e6205163e2a4b63f5aa8d9204fe5190f))
26
+ * switch a SID between the 6581 and 8580 while it runs ([5920391](https://github.com/elektronaut/badline/commit/592039123d7936b8d22c8fd5f144a9bc9a8f383b))
27
+
28
+
29
+ ### Bug Fixes
30
+
31
+ * build the native badline with a Spinel that keeps an array joined mid-expression alive ([d2c4eb2](https://github.com/elektronaut/badline/commit/d2c4eb20d6b80494fb2a20105c0532f3778b205c))
32
+ * build the native badline with a Spinel that reports render progress ([58524ea](https://github.com/elektronaut/badline/commit/58524eaf4881661e7e0e9dd38c32071545bf1fae))
33
+ * show non-ASCII text from a .sid header in the native badline ([959bb6f](https://github.com/elektronaut/badline/commit/959bb6f7d07de4c90ac50ae51ae63f3c66601a78))
34
+
35
+
36
+ ### Performance Improvements
37
+
38
+ * draw the SID player's traces in one call each ([afcae15](https://github.com/elektronaut/badline/commit/afcae15ed273aa7a6dd49cb31e0f7180c56feb75))
39
+
40
+ ## [0.6.1](https://github.com/elektronaut/badline/compare/v0.6.0...v0.6.1) (2026-09-30)
41
+
42
+
43
+ ### Features
44
+
45
+ * run both builds on one shared front end ([0e1ebfb](https://github.com/elektronaut/badline/commit/0e1ebfb07c4705b2e46c1bb44f1ea76f22370b24))
46
+ * run both builds on one shared front end ([3181d5f](https://github.com/elektronaut/badline/commit/3181d5f2ab73628d515ea88c42ddef4b11f74ac0))
47
+ * script key presses, media swaps and screenshots at given frames ([7a87069](https://github.com/elektronaut/badline/commit/7a87069d9b1491297e9a6ebd4fc0a860be3afd27))
48
+ * script key presses, media swaps and screenshots at given frames ([5554511](https://github.com/elektronaut/badline/commit/5554511868d7986950cac829c3341689c88a32a7))
49
+
50
+
51
+ ### Bug Fixes
52
+
53
+ * build the native badline with a Spinel that prints the headless player's display ([d2d3df3](https://github.com/elektronaut/badline/commit/d2d3df32c67c02c0e0f20308a03faca5430dfa26))
54
+ * build the native badline with a Spinel that prints the headless player's display ([8dccecb](https://github.com/elektronaut/badline/commit/8dccecb2a2b8d044162b149203ca8aaf75aa4957))
55
+ * build the native badline with a Spinel that renders .wav files ([bacfbea](https://github.com/elektronaut/badline/commit/bacfbea0c2d9af1c61c34aa09ff828ba767b2af3))
56
+ * build the native badline with a Spinel that renders .wav files ([afc4267](https://github.com/elektronaut/badline/commit/afc42670f8543c50e74268ccc81284e46838df48))
57
+ * end the run at a failed insert and say when there's nothing to eject ([f6b686d](https://github.com/elektronaut/badline/commit/f6b686d4094fbf526bffa37c7aa13dba0908bb71))
58
+ * key testbench results by test key, so a row listed twice gets its own line ([a0f4d55](https://github.com/elektronaut/badline/commit/a0f4d555f568c2e82b08971adc00fa4199c1eac4))
59
+ * leave .claude/ and vendor/ out of the gem ([c3ec867](https://github.com/elektronaut/badline/commit/c3ec867f04ee1840a01dfb0de2cce7cdd7aa30c6))
60
+ * save the REU swap's pending re-read in snapshots, from a module of its own ([3448ac8](https://github.com/elektronaut/badline/commit/3448ac8a8827587b01ea2da6101c1f1b2009bc88))
61
+ * show bank 3 for a cycle after a $dd00 bank swap on the 8565 ([2529eff](https://github.com/elektronaut/badline/commit/2529effb0c5fc3cf463e1d0e5b65e2321b3ed06d))
62
+ * show bank 3 for the cycle after a $dd00 write swaps the bank lines on the 8565 ([c9959f4](https://github.com/elektronaut/badline/commit/c9959f430ac7a95397d906e1c70013bbc25b5ca2))
63
+ * time the REU's swaps and fetches against the VIC, and take the bus from an RMW write ([60e48bd](https://github.com/elektronaut/badline/commit/60e48bd8531d130252101d6227ef296ed0b6395b))
64
+ * time the REU's swaps and fetches against the VIC, and take the bus from an RMW write ([eb045fa](https://github.com/elektronaut/badline/commit/eb045fa3d20e0a1abc9acdf68bfb1f9388d7ca57))
65
+ * write a fetch's first byte twice when BA falls as it starts ([35c4cf4](https://github.com/elektronaut/badline/commit/35c4cf419178bdf1b704f94c815dce259368fa26))
66
+ * write a fetch's first byte twice when BA falls as it starts ([1ab691a](https://github.com/elektronaut/badline/commit/1ab691a512b54d58670d5a2ae244f917c715bb25))
67
+ * write the 6581's pulse+noise lines only mid-shift ([95f84dd](https://github.com/elektronaut/badline/commit/95f84dd6e842bf76e562dd9867f24916039debd5))
68
+
69
+
70
+ ### Performance Improvements
71
+
72
+ * give the 1541's 6502 a class of its own ([df64d23](https://github.com/elektronaut/badline/commit/df64d231f97824f2a6dc7bd37a1e4ef4294dc8da))
73
+ * give the 1541's 6502 a class of its own ([8193a52](https://github.com/elektronaut/badline/commit/8193a52c642a8c176cc29591d99beffb91300620))
74
+
75
+
76
+ ### Miscellaneous Chores
77
+
78
+ * release 0.6.1 ([059cd50](https://github.com/elektronaut/badline/commit/059cd502dd356760179e5df823402891b99349bd))
79
+
3
80
  ## [0.6.0](https://github.com/elektronaut/badline/compare/v0.5.1...v0.6.0) (2026-09-29)
4
81
 
5
82
 
data/CLAUDE.md CHANGED
@@ -25,14 +25,18 @@ live in subdirectories:
25
25
  - **Media and host I/O**: `storage/` (disk, tape and cartridge image
26
26
  formats), `cartridge/` (mappers), `kernal_trap/` (the LOAD/SAVE and IEC
27
27
  traps that stand in for a drive), `media.rb` (attach and autostart),
28
- `datasette.rb`, and `gui/` and `input/` (SDL front end, controllers)
29
- - **Native**: `native/` holds the native `badline`, the core compiled with
30
- Spinel in an SDL2 window (`rake native:build`, `native/README.md`).
28
+ `datasette.rb`, and `input/` (the mouse and paddles)
29
+ - **Front end**: `frontend/`, the SDL window both builds run (app loop,
30
+ screen, pacer, sound, controls, gamepads, snapshots), in Spinel's
31
+ subset, over the one binding in `sdl.rb` (`ffi.rb` implements it on
32
+ CRuby)
33
+ - **Native**: `native/` holds the native `badline`, the core and the
34
+ front end compiled with Spinel (`rake native:build`, `native/README.md`).
31
35
  `spinel/` holds the Spinel test harnesses
32
36
 
33
37
  Timing-critical code lives in `cpu`, `interrupts`, `vic`, `cia` and `sid` and
34
38
  in the order `Computer#cycle!` clocks them. Changes there move the test
35
- suites below. `gui/` changes move none of them.
39
+ suites below. `frontend/` changes move none of them.
36
40
 
37
41
  Namespaced groups live in `lib/badline/<namespace>/`, with a sibling
38
42
  `lib/badline/<namespace>.rb` that requires the members, directly or through
@@ -157,8 +161,8 @@ the rows your change can't reach tell you nothing about it.
157
161
  `ruby --yjit bin/lorenz [image] [start_test] [max_cycles]` (resumes the
158
162
  chain at a named test, and `--stop-after NAME` ends it once that test is
159
163
  done). These take seconds to minutes
160
- - A pull request's CI is the verdict. It runs every suite but
161
- `testbench-drive` on the Spinel build, compared row by row against
164
+ - A pull request's CI is the verdict. It runs every suite on the Spinel
165
+ build, compared row by row against
162
166
  `test/baselines/`, and every job is a required check. A row that moved
163
167
  fails it. Re-record only those rows, in the same change:
164
168
  `rake "regression:record:<suite>[filter,...]"` (quote it, because zsh
@@ -174,9 +178,6 @@ the rows your change can't reach tell you nothing about it.
174
178
  - For a change whose reach you can't bound to a set of filters, such as
175
179
  reordering `Computer#cycle!` or changing the LOAD trap every suite loads
176
180
  through, push and let CI run the suites
177
- - `testbench-drive` needs the true drive, which the Spinel testbench
178
- harness doesn't have, so CI leaves it out. Run the rows your change can
179
- reach locally
180
181
  - The Regression workflow runs the suites on CRuby, started by hand from
181
182
  the Actions tab, for a person checking that CRuby and Spinel agree.
182
183
  Agents don't start it
data/README.md CHANGED
@@ -56,25 +56,64 @@ badline game.prg # Load and run a program
56
56
  badline game.d64 # Mount a disk image as device 8 and load it
57
57
  badline game.tap # Insert a tape and load it
58
58
  badline game.crt # Attach a cartridge
59
- badline tune.sid # Play a SID tune
59
+ badline tune.sid # Play a SID tune in the SID player
60
60
  badline game.vsf # Restore a snapshot, from badline or VICE
61
61
  badline ~/c64 # Mount a directory as device 8
62
62
  ```
63
63
 
64
- `badline-ruby` takes the same media and the same options, with the
65
- differences noted below.
66
-
67
64
  Programs, disk and tape images and SID tunes start automatically, and
68
65
  a cartridge starts itself. A mounted directory waits for you to `LOAD`
69
- from it. `--no-autostart` attaches the media and stops at `READY.`, so
70
- you can type the `LOAD` yourself. `--read-only` mounts a disk image
71
- write-protected, so the drive reports `26,WRITE PROTECT ON` for any
72
- write and the image file stays as it was. `--song N` picks a subtune of a
73
- `.sid` file, `--sid 8580` fits the newer SID and `--reu 512` plugs in a
74
- 512K RAM Expansion Unit. `--ntsc` runs an NTSC C64, with the 6567R8
75
- VIC-II, instead of a PAL one. `badline-ruby` also
76
- takes `--disable-jit`, which runs without YJIT, otherwise switched on at
77
- startup. `--help` lists the options.
66
+ from it.
67
+
68
+ `badline-ruby` takes the same media and the same options, with the
69
+ differences noted in the table. `--help` lists the options for either.
70
+
71
+ ### Options
72
+
73
+ | Option | Effect |
74
+ | --- | --- |
75
+ | `--no-autostart` | Attach the media and stop at `READY.`, so you can type the `LOAD` yourself |
76
+ | `--read-only` | Mount a disk image write-protected. The drive reports `26,WRITE PROTECT ON` for any write and the image file stays as it was |
77
+ | `--true-drive` | Put an emulated 1541 on device 8 instead of the KERNAL traps. See [Media](#media) |
78
+ | `-s`, `--subtune N` | Pick a subtune of a `.sid` file, counting from 1 |
79
+ | `--sid 6581`, `--sid 8580` | Fit the older or newer SID. `--sid auto`, the default, takes a `.sid` tune's own |
80
+ | `--reu SIZE` | Plug in a RAM Expansion Unit of `SIZE` K: 128, 256, 512 (a 1750) or up to 16384 |
81
+ | `--ntsc` | Run an NTSC C64, with the 6567R8 VIC-II, instead of a PAL one |
82
+ | `--no-sound` | Don't play the SID (`badline` only, where sound is on by default) |
83
+ | `--sound` | Play the SID (`badline-ruby`, where sound is off by default) |
84
+ | `--no-vsync` | Pace the window by a timer, or by the sound while it plays, instead of the display's vsync |
85
+ | `--verbose` | Print the display's refresh rate, the sound's sample rate and the game controllers found as the window opens, then the frame rate and the time each frame takes, once a second |
86
+ | `--disable-jit` | Run without YJIT, otherwise switched on at startup (`badline-ruby` only) |
87
+ | `--version` | Show the version and what built it (`badline` only) |
88
+ | `-h`, `--help` | List the options |
89
+
90
+ Both run the same window. `badline` plays the SID through the host's
91
+ audio device, and `F10` mutes and unmutes it. In `badline-ruby` the
92
+ machine runs below real time, so the sound stutters: it plays in bursts
93
+ with silent gaps between them, at the right pitch, and never slows the
94
+ emulation down.
95
+
96
+ ### Scripted runs
97
+
98
+ | Option | Effect |
99
+ | --- | --- |
100
+ | `--frames N` | Quit after N frames |
101
+ | `--unpaced` | Run as fast as it can, without vsync or pacing |
102
+ | `--at FRAME:EVENT` | Press keys, type, swap media or take a screenshot once `FRAME` frames have run |
103
+ | `--script FILE` | Run the events in `FILE`, one `FRAME:EVENT` a line |
104
+ | `--screenshot FILE` | Save the last frame as a `.bmp` |
105
+ | `--save-snapshot FILE` | Save the machine as a `.vsf` snapshot after the last frame |
106
+
107
+ ```sh
108
+ badline --unpaced --frames 12000 --true-drive disk1.d64 \
109
+ --at 3500:key=space --at 5600:insert=disk2.d64 \
110
+ --at 250,500,6000:screenshot=shot%05d.bmp
111
+ ```
112
+
113
+ `--help` lists the events, and [native/README.md](native/README.md#running)
114
+ describes them.
115
+
116
+ ### ROMs
78
117
 
79
118
  The KERNAL, BASIC and character ROMs come with the gem. To run other
80
119
  images, such as a patched KERNAL, point `BADLINE_ROM_PATH` at a
@@ -83,20 +122,6 @@ plus `eapi/eapi-am29f040-14` if you attach EasyFlash cartridges. From
83
122
  Ruby, `Badline.rom_path = dir` does the same before a
84
123
  `Badline::Computer` is built, and `nil` restores the bundled set.
85
124
 
86
- `badline` plays the SID through the host's audio device, and `F10`
87
- mutes and unmutes it. `--no-sound` turns it off. The window is paced by
88
- the display's vsync; `--no-vsync` paces it by a timer, or by the sound
89
- while it plays.
90
-
91
- `--verbose` prints the display's refresh rate, the sound's sample rate
92
- and the game controllers found as the window opens. In `badline` it also
93
- prints the frame rate and the time each frame takes, once a second.
94
-
95
- In `badline-ruby` sound is off by default, and `--sound` turns it on.
96
- The machine runs below real time there, so the sound stutters: it plays
97
- in bursts with silent gaps between them, at the right pitch, and never
98
- slows the emulation down.
99
-
100
125
  ## Media
101
126
 
102
127
  | Format | Handling |
@@ -201,39 +226,107 @@ out and switches the machine off and on.
201
226
 
202
227
  `badline-ruby --headless` plays a `.sid` tune on the host's audio
203
228
  device without opening the window, and `--audio-out` renders it to a
204
- 16-bit PCM file instead. The file's extension picks the format, `.wav`
229
+ 16-bit stereo PCM file instead. The file's extension picks the format, `.wav`
205
230
  or `.aiff`. The native `badline` has both modes too.
206
231
 
207
232
  ```sh
233
+ badline sid ~/C64Music/MUSICIANS/H/Hubbard_Rob # play every tune below a directory
234
+ badline sid tune.sid other.sid # play a queue of tunes
208
235
  badline-ruby --headless tune.sid # play, length from HVSC
209
236
  badline-ruby --headless -s 3 tune.sid # play the third subtune
237
+ badline-ruby --headless --all-subtunes tune.sid # play on through every subtune
210
238
  badline-ruby --seconds 180 tune.sid --audio-out out.aiff
211
239
  badline-ruby -s 3 --rate 48000 tune.sid --audio-out out.wav
212
240
  badline-ruby --headless --sid 8580 tune.sid
213
241
  badline-ruby --filter-chunk 1 tune.sid --audio-out out.wav # exact filter, slower
214
242
  ```
215
243
 
244
+ `badline sid FILE|DIR...` plays a queue of tunes in the SID player's
245
+ window, in the order given, and a directory adds every `.sid` tune below
246
+ it in path order. `badline sid --headless` plays the queue in the
247
+ terminal instead. `badline sid` on its own opens the window with an
248
+ empty queue. Dropping `.sid` files or folders on the window adds them
249
+ to the end of the queue, and an empty queue starts playing them. It
250
+ takes
251
+ the options below except `--audio-out`, and `--subtune` picks the first
252
+ tune's subtune. `--sid auto`, the default, fits each of a tune's SIDs
253
+ the model its header names. A tune written for 2 or 3 SIDs plays on as
254
+ many, at the addresses its header gives, in stereo: SID 1 on the left,
255
+ SID 2 on the right and SID 3 in the centre. A tune on one SID plays the
256
+ same on both channels. A file that isn't a tune is skipped.
257
+ `badline tune.sid` plays the tune in the SID player too, unless an
258
+ option of the emulator's window, such as `--ntsc` or `--reu`, asks for
259
+ the machine: then a tune for one SID runs on the emulated C64, started
260
+ through a small driver after boot.
261
+
216
262
  Both modes take the same options. The window's own, `--no-autostart`,
217
263
  `--read-only`, `--sound`, `--true-drive`, `--reu`, `--ntsc` and
218
- `--verbose`, don't apply to them. `--song` (or `-s`) picks the subtune, counting from 1 as HVSC
219
- does, and defaults to the tune's own start song. Playback asks the
264
+ `--verbose`, don't apply to them. `--subtune` (or `-s`) picks the subtune, counting from 1 as HVSC
265
+ does, and defaults to the tune's own start subtune. Playback asks the
220
266
  device for 44.1 kHz and takes whatever rate it offers, unless `--rate`
221
267
  says otherwise. Ctrl-C stops it.
222
268
 
223
- Played on a terminal, `--headless` shows the tune's name, author and
224
- release, the song number and the time played against the song's
225
- length. When a song reaches its length it goes on to the next, and it
226
- stops after the last. `n` or → skips to the next song, `p` or ← goes
227
- back one, space pauses and `q` quits. `--no-tui`, or output that isn't a
228
- terminal, gives plain progress output instead and plays just the one
229
- song, as `--audio-out` renders just the one.
269
+ Played on a terminal, `--headless` and `sid` show each tune's name,
270
+ author and release as it starts, then the tune's place in the queue,
271
+ the subtune number and the time played against the subtune's length. `--headless`
272
+ queues just the one tune. Each tune plays its own subtune: `--subtune`, or
273
+ the tune's start subtune. When that subtune ends the player goes on to the next
274
+ tune, and it stops after the last. `a`, or `--all-subtunes`, turns on
275
+ playing all subtunes, so that a subtune that ends goes on to the tune's next
276
+ subtune, and a tune stepped to starts on its first subtune.
277
+
278
+ → and ← step to the tune's next and previous subtune, stopping at its
279
+ first and last. `n` and `p` step to the next and previous tune. `s`
280
+ turns shuffle on and off, which plays the queue's tunes in a random
281
+ order, and `l` turns looping on and off, so that the end of the queue
282
+ goes on to its start. `,` and `.` seek 10 seconds back and forward
283
+ within the subtune, which plays it again from its start and runs silently
284
+ up to the point asked for. Space pauses and `q` quits. The status line
285
+ shows which modes are on, and they last until the player quits.
286
+
287
+ ![The SID player's visualizer playing Rob Hubbard's Delta](doc/images/sid-player.png)
288
+
289
+ The SID player's window is worked with the mouse. Its header shows the
290
+ tune's name, author and release, the tune the subtune covers at the
291
+ moment when HVSC's STIL credits one, following the times STIL gives, and
292
+ buttons that switch between three views and between the tune's own SID
293
+ model, the 6581 and the 8580, which changes the chips playing on the
294
+ spot. Its footer shows the time played on a bar you can click to seek,
295
+ and buttons that pause, step between tunes and between a tune's
296
+ subtunes, and turn shuffle, looping and all subtunes on and off. The
297
+ visualizer view shows each voice's note, and how far off it is in cents,
298
+ over a scope of the voice's output, and the mixed output below them. A
299
+ tune on more than one SID gets a row of voices for each SID, and the mix
300
+ splits into its left and right. The SID view shows one SID at a time,
301
+ and for a tune on more than one, a row of SID 1, 2 and 3 buttons at its
302
+ top picks which. It shows each voice's waveforms and the shape
303
+ they make, its control bits, pulse width, envelope settings, and the
304
+ envelope's level and stage, and the filter's modes, cutoff, resonance,
305
+ volume and its response on the chip playing. The INFO view shows the
306
+ tune's STIL entry and the subtune's, with a scroll bar, the mouse wheel
307
+ or the up and down keys for the long ones. The terminal's keys work in
308
+ the window too, along with Tab to switch views and `c` to step through
309
+ the SID models.
310
+
311
+ `--no-tui`, or output that isn't a terminal, gives plain progress
312
+ output instead and plays through the queue without the keys, while
313
+ `--audio-out` renders just the one subtune.
230
314
 
231
315
  A `.sid` file doesn't store its length, so `badline-ruby` looks the
232
316
  tune up by MD5 in HVSC's `Songlengths.md5`. It finds the database
233
317
  through `--songlengths`, in a `DOCUMENTS` directory in any of the
234
318
  tune's parent directories (the layout of an HVSC collection), or under
235
319
  `$HVSC_BASE/DOCUMENTS`. Without a database or `--seconds` it runs for
236
- 60 seconds.
320
+ 60 seconds, but a subtune that falls silent for 5 seconds before then
321
+ ends there, when played and when rendered alike. Silent means the
322
+ output holds within 16 steps of one level, since a 6581 idles at a DC
323
+ offset rather than at zero. A subtune with a known length plays to its
324
+ length whatever it sounds like.
325
+
326
+ A tune in an HVSC collection also gets its entry in HVSC's `STIL.txt`,
327
+ found the same way: comments, covers, and subtune names and composers.
328
+ The tune's own fields follow its header, and each subtune's print as it
329
+ starts.
237
330
 
238
331
  PSID tunes run on a CPU and RAM with only the SID clocked, at about
239
332
  twice real time, so they play smoothly. RSID tunes set up their own
@@ -250,9 +343,9 @@ without stuttering. See
250
343
 
251
344
  ## Input
252
345
 
253
- Keys map by their unshifted symbol, and Shift gives the C64's shifted
254
- character, not the host's: Shift-2 types `"`. These keys have no
255
- same-named host key:
346
+ Keys map by their position on a US keyboard, whatever the host's layout,
347
+ and Shift gives the C64's shifted character, not the host's: Shift-2
348
+ types `"`. These keys have no same-named host key:
256
349
 
257
350
  | C64 | Host |
258
351
  |-----|------|
@@ -275,7 +368,7 @@ window title shows the current mode:
275
368
  | Mode | What the host drives |
276
369
  |------|----------------------|
277
370
  | (none) | The keyboard |
278
- | `[JOY]` | Arrows and Space are joystick 2, `WASD` and Left Shift joystick 1 |
371
+ | `[JOY 2]` / `[JOY 1]` | Arrows and Space (or Right Ctrl) are the joystick named, `WASD` and Left Shift the other. `F9` swaps them |
279
372
  | `[MOUSE 1]` / `[MOUSE 2]` | A 1351 mouse in control port 1 or 2 |
280
373
  | `[PADDLE 1]` / `[PADDLE 2]` | A pair of paddles in control port 1 or 2 |
281
374
 
@@ -869,6 +869,18 @@ are the row's diff with the rule removed.
869
869
  fails with two.
870
870
  - Spec guard: *adds one extra half-pixel on the 8565* in
871
871
  [`vic_spec.rb`](../spec/badline/vic_spec.rb).
872
+ - **A `$dd00` write that swaps the bank lines shows bank 3 for a cycle.**
873
+ Where one bank line goes high as the other goes low, every VIC access in
874
+ the cycle after the write reads bank 3 (both lines low), and the new bank
875
+ from the cycle after that (`VIC::Bank#sample_lines`). A swap the DDR
876
+ makes, releasing a line to float high, shows nothing, and neither does a
877
+ write that moves one line or both the same way.
878
+ - Pinned by `fetchsplit` (154 → 16 px): every 8565 capture its readme
879
+ lists shows `3` in the first character after each `$dd00` swap, while
880
+ the 6569 reference shows none. `M1` also shows it after the DDR swaps;
881
+ `M7`, which the `-8565` reference is made from, doesn't.
882
+ - Spec guard: the `#sample_lines` group in
883
+ [`bank_spec.rb`](../spec/badline/vic/bank_spec.rb).
872
884
 
873
885
  What the 8565 references don't settle, and so what stays as it is:
874
886
 
@@ -882,12 +894,21 @@ What the 8565 references don't settle, and so what stays as it is:
882
894
  `vicii_reg_timing-ff` (7 px, the hi-res bitmap into text row) stay FAIL.
883
895
  No emulator in the testbench results passes `vicii_reg_timing-a5` or
884
896
  `-ff` on the 8565, and only Hoxs64 passes `modesplit`, against the
885
- `8565early` reference.
886
- - `fetchsplit` (154 px) stays FAIL: its readme says its 8565 artefacts
887
- differ from chip to chip and change as the machine warms up, and its
888
- `M1` and `M7` captures differ. The differences sit in the first character
889
- after a `$dd00` bank switch on some lines. Delaying every rising, or
890
- every falling, bank bit by a cycle on the 8565 makes it 656 or 752 px.
897
+ `8565early` reference. Listed by transition, the references disagree
898
+ on the same pixel: out of ECM+BMM into ECM text a foreground pixel 0 is
899
+ on time in `videomode-w` and black in `modesplit`; out of ECM into hi-res
900
+ text a background pixel 0 selecting `$d022` shows the new mode in the
901
+ `vicii_reg_timing` ECM row and the old one in `modesplit`; out of hi-res
902
+ bitmap into text a foreground pixel 0 is new in `-a5` and old in `-ff`,
903
+ with the same screen, colour and pixel values. Each test's position is
904
+ independent of where it starts: booting 0 to 20000 cycles longer before
905
+ attaching gives the same diff.
906
+ - `fetchsplit` (16 px) stays FAIL on the one swap the DDR makes that the
907
+ `-8565` reference shows as bank 3: the swap in column 34 of the `line1c`
908
+ rows, in the text and ECM blocks but not the bitmap one, while the same
909
+ swap in column 4 of `line1b` shows nothing. The readme says its 8565
910
+ artefacts differ from chip to chip and change as the machine warms up,
911
+ and its `M1` capture shows bank 3 after every DDR swap.
891
912
  - VICE's idle g-access also reads ECM from the `$d011` of the column
892
913
  before on the 8565. No testprog tells it apart, so the 8565 keeps the
893
914
  6569's idle access, without the 6569's `$38ff` read where a DMA delay
@@ -933,6 +954,22 @@ region moves the sprites. The positions live in `Region::Profile` and
933
954
  `testbench-ntsc` screenshots, VICE's 247 lines from line 28 running on
934
955
  into the next frame, see every line they show painted.
935
956
 
957
+ What the NTSC references don't settle, and so what stays as it is:
958
+
959
+ - `modesplit` on the 6567R56A (62 px) is compared against the PAL
960
+ reference, since there is no `-ntscold` one, and every pixel of its
961
+ diff sits in the label bar, where the test prints the line length and
962
+ line count it measured, and the reference shows the PAL ones.
963
+ - `modesplit` on the 6567R8 (458 px) matches the PAL reference on every
964
+ row but the label bar. Its `-ntsc` reference differs from the PAL one
965
+ in ways no 6567R8 rule here produces: light grey on the first pixel
966
+ after each `$d021` write (a grey dot, 20 px), the ECM split of section
967
+ 2 alone starting and ending a character later (most of the diff) while
968
+ sections 1 and 3 keep their PAL positions, and single pixels in
969
+ section 1. The
970
+ readme says it was matched against screenshots, and the one 6567R8
971
+ photograph in the testprogs is of revision R01.
972
+
936
973
  ## CIA 6526 timer pipeline
937
974
 
938
975
  - Start and stop go through two stages. A force load lands one tick after
@@ -1292,27 +1329,78 @@ and each was knocked out: removing it fails the rows named.
1292
1329
  The REC's timing against the VIC, each rule derived from the REU
1293
1330
  testprogs named.
1294
1331
 
1295
- - A requested transfer takes the bus on the CPU's next read cycle with BA
1296
- high, and moves its first byte there. Starting a cycle later moves every
1297
- `REU/xfertiming` row and `REU/reutiming/reutiming`.
1332
+ - A requested transfer takes the bus on the next cycle with BA high, and
1333
+ moves its first byte there. Starting a cycle later moves every
1334
+ `REU/xfertiming` row and `REU/reutiming/reutiming`. /DMA takes the bus
1335
+ from the CPU from the cycle after the request, even while the REC waits
1336
+ for BA, and RDY only halts the CPU on a read, so a write the CPU makes
1337
+ then goes nowhere (`AddressBus#cycle_cpu_off_bus`). That is how an `INC
1338
+ $FF00` starts an armed transfer without its second write reaching RAM.
1339
+ Pinned by `REU/rmw-trigger/rmwtrigger-ram` and `rmwtrigger-rom`.
1298
1340
  - After reading C64 memory the REC waits out every BA-low cycle. After
1299
1341
  writing it goes on through the first BA-low cycle and waits from the
1300
1342
  second, and a fetch or swap whose last write it waited out takes one
1301
1343
  more cycle before handing the bus back (`REU::DMA#note_ba`,
1302
1344
  `#wind_down`). Pinned by `REU/bonzai/spritetiming`, whose 45 bytes a
1303
1345
  line with eight sprites on is 63 less the 18 cycles this leaves.
1304
- - On the line whose raster matches sprite 0's Y, the REU doesn't see BA
1305
- fall on the first cycle of sprite 0's window (`VIC#reu_ba_low?`).
1306
- Without it `REU/bonzai/spritetiming` reads `$5a,$87` where a real REU
1307
- gives `$5b,$88`.
1346
+ - A fetch's last write doesn't go through the first BA-low cycle: the REC
1347
+ waits for BA to rise and writes it then. Pinned by `REU/reutiming2/d`
1348
+ and `d2`, and it takes `REU/reutiming2/c` from 6,403 px to 337.
1349
+ - A fetch requested on the last cycle before BA falls writes its first
1350
+ byte on the first BA-low cycle, without counting it, then waits for BA
1351
+ and writes the same byte again when it starts (`REU::DMA#write_ahead`).
1352
+ One requested on a BA-low cycle writes nothing until BA rises. Pinned by
1353
+ `REU/badoublewrite`, whose sixth transfer a frame, requested on the
1354
+ cycle before BA falls for raster `$63`, a bad line, holds `$d021` at its
1355
+ first byte for the whole line, where the program's text says "d021
1356
+ doublewrite on this line". The row captures its screen too early to
1357
+ show this (see below), but the frame after matches the reference to the
1358
+ pixel, and without the rule that line is 199 px off.
1359
+ - `REU/badoublewrite` writes `$D7FF` from its raster interrupt on the
1360
+ third transfer of the first frame it runs them in, at raster 77, so the
1361
+ screenshot taken at that write shows the frame before below it, with
1362
+ the text half printed and the background still blue. Its reference
1363
+ shows a whole frame of twelve transfers on black, which only a
1364
+ screenshot a frame or more later can. The exit is locked to the raster:
1365
+ starting the program anywhere up to a frame later leaves it on raster
1366
+ 77 with the same 58,880 px. VICE's own x64sc and x64 fail the row in every result the
1367
+ testbench keeps, r41951 to r45942, while Denise passes it from 2023.
1368
+ The row stays at 58,880 px.
1369
+ - `REU/reutiming2/c`'s reference shows its last transfer, from raster 250
1370
+ into 251 (`$fb`), waiting on raster 251 as the transfers above it wait
1371
+ on their bad lines: the border holds the transfer's byte `$11` up to
1372
+ x 336 and `$12` from x 337, where BA rises on a bad line. Raster 251 is
1373
+ past the bad line range, `$30`–`$f7`, and the program turns on no
1374
+ sprites, so nothing pulls BA there, and badline writes `$12` on the
1375
+ cycle after `$11`, before the line's visible part starts. The 8565 and
1376
+ 8565early references show the same wait. No emulator or FPGA core in
1377
+ the testbench's 59 kept results passes the row, VICE, the Ultimate 64
1378
+ and the Chameleon included, and the testlist marks it `warn:vicefail`.
1379
+ badline keeps the bad line range, and the row stays at 337 px, all of
1380
+ them on raster 251.
1381
+ - On the line whose raster matches sprite 0's Y, a REC that read or wrote
1382
+ on the cycle before doesn't see BA fall on the first cycle of sprite
1383
+ 0's window (`VIC#reu_ba_late?`). Without it `REU/bonzai/spritetiming`
1384
+ reads `$5a,$87` where a real REU gives `$5b,$88`. A swap's read on that
1385
+ cycle still sees BA low, and is held as the next rule says (pinned by
1386
+ `REU/reutiming2/e4-m2` and `e6-m2`).
1308
1387
  - A swap's read that falls on the first BA-low cycle, after its write, is
1309
1388
  held open while AEC stays high and takes the byte on the bus two cycles
1310
1389
  later. If it is the transfer's last byte it is read there instead, its
1311
1390
  write follows on the next cycle, and the REC hands the bus back
1312
1391
  (`REU::DMA#swap_read_on_ba`). Pinned by `REU/reutiming2/e5-m2`, `f3-m2`
1313
- and `f4-m2`, whose patterns were captured on a breadbin. The
1314
- `reutiming2` references without `-m2` read `$42` or a timer value there
1315
- instead, and still fail.
1392
+ and `f4-m2`, whose patterns were captured on a breadbin.
1393
+ - A swap's read made with BA high, when BA falls on the next cycle before
1394
+ its write, is made again when BA rises, and the write follows it
1395
+ (`REU::DMA#read_swap_again`). So is a held read when BA runs straight
1396
+ from a bad line's window into sprite 0's (`VIC#reu_ba_handed_on?`).
1397
+ Pinned by `REU/reutiming2/e4-m2`, `e6-m2`, `g3-m2` and `g4-m2`.
1398
+ - The `reutiming2` references without `-m2` read `$42`, the RAM under
1399
+ `$DC04`, on some of the cycles a swap's or stash's read starts with the
1400
+ VIC's DMA. The readme puts that down to a C64C board or an 8565, while
1401
+ the testlist runs those rows on the 6569, the same machine as the `-m2`
1402
+ rows, whose references read the timer there. The two sets can't both
1403
+ pass on one machine, and badline follows the breadbin `-m2` ones.
1316
1404
 
1317
1405
  ## SID oscillator
1318
1406
 
@@ -1340,7 +1428,8 @@ testprogs named.
1340
1428
  its output is latched instead. The cycle after that is phase 2: the
1341
1429
  latched output is written over the taps, then the register shifts.
1342
1430
  Phase 2 writes back by the test bit release rule below, with the old and
1343
- new waveform the same: noise combined with anything but pulse alone.
1431
+ new waveform the same: noise combined with anything but pulse alone. On
1432
+ the 6581, phase 2 also writes pulse+noise's lines (below).
1344
1433
  Setting the test bit drops a shift in flight.
1345
1434
  - Pinned by `SID/noisewriteback`'s `noise_writeback_test2` (both chips).
1346
1435
  It releases the test bit into noise+triangle, which pulls every tap low,
@@ -1364,6 +1453,9 @@ testprogs named.
1364
1453
  `$f8`/`$80` pairs set the test bit onto all four waveforms from noise
1365
1454
  alone, and zero the register only if this writes back. The release that
1366
1455
  follows goes to noise alone, which writes nothing back (below).
1456
+ - On the 6581, pulse+noise writes its `$00` read back here, as in a
1457
+ shift's phase 2 (below). Pinned by `SID/wf12nsr`'s pulse+noise row,
1458
+ which sets the test bit onto pulse+noise.
1367
1459
  - Spec guard: *as the test bit rises* in
1368
1460
  [`sid/waveform_spec.rb`](../spec/badline/sid/waveform_spec.rb).
1369
1461
  - The test bit does not clear the LFSR. It stalls it halfway through a
@@ -1445,9 +1537,10 @@ testprogs named.
1445
1537
  register and writes `$fc` back, the only pair among `$f8`, `$fc` and the
1446
1538
  full noise that passes `SID/wf12nsr`'s pulse+noise row. That `$fc` is what
1447
1539
  `SID/wb_testsuite`'s `C`→`9` and `C`→`E` rows need written at release.
1448
- The 6581 reads `$fc` (the readme of `SID/wf12nsr`, VICE bug #1037,
1449
- unscored) and writes nothing back: its `8`/`9`/`A`/`B`→`C` rows run
1450
- pulse+noise and leave the register alone.
1540
+ The 6581 reads `$00` of a full register, and pulls nothing down while
1541
+ the register holds. Its lines land only part way through a shift: in
1542
+ a shift's phase 2, and as the test bit rises onto pulse+noise. Both
1543
+ write the `$00` read, so every tap goes low.
1451
1544
  - Pinned by `SID/wf12nsr` (wf9/wfa on both chips, wfc on the 8580) and
1452
1545
  the `SID/wb_testsuite` rows above. Knock-outs: writing back the plain
1453
1546
  AND fails wf9 and wfa on both chips; on the 8580, reading and writing
@@ -1455,17 +1548,25 @@ testprogs named.
1455
1548
  fails wfc and the `C`→`9`/`C`→`E` rows; applying the lone-line rule to
1456
1549
  the read as well makes `noise_writeback_test2` read `$00` on both
1457
1550
  chips.
1458
- - Can't pass: the 6581's pulse+noise row of `SID/wf12nsr`. The program
1459
- never writes voice 3's pulse width, so the row depends on whatever width
1460
- the machine it was recorded on held. The 6581 reference reads `$00`
1461
- right after the release into pulse+noise at frequency `$ffff`, as if
1462
- the pulse was still low and grounding every line. After power-on or
1463
- reset the width is zero and the pulse is high. With the width set
1464
- before the program runs, the row passes from `$1a0` up and fails from
1465
- `$198` down. The 8580 program passes from a zero width. `SID/wf12nsr`'s `quicktest.prg` doesn't set the width
1466
- either. The row stays failing.
1551
+ - The 6581's pulse+noise is pinned by `SID/wf12nsr`'s wfc row and
1552
+ `SID/wb_testsuite`'s `8`→`C` and `C`→`C` rows. The wfc row releases the
1553
+ test bit into pulse+noise at frequency `$ffff` with the pulse width
1554
+ never written, so zero and the pulse high, and reads `$00` on the next
1555
+ instruction, before bit 19 first rises. That read is of a full
1556
+ register, so the `$00` is the read itself. Its noise run afterwards
1557
+ matches the other combined rows, whose taps were all pulled low. The
1558
+ `8`→`C` and `C`→`C` rows hold pulse+noise at frequency zero, where no
1559
+ shift comes, and leave the register alone. Knock-outs: writing the
1560
+ `$00` back every cycle and at release fails `8`→`C`, `C`→`C` and ten
1561
+ more 6581 rows (the recorded VICE r45942 results pass wfc and fail
1562
+ `8`→`C` and `C`→`C`); dropping the phase 2 write or the write as the
1563
+ test bit rises fails wfc. The readme
1564
+ of `SID/wf12nsr` has a warmed-up 6581 reading `$fc` with the test bit
1565
+ held (`quicktest.prg`, unscored), which this model doesn't reproduce.
1467
1566
  - Spec guard: *writes a lone line of a noise combination back low*, *with
1468
- pulse+noise on the 8580* and *as the test bit falls* in
1567
+ pulse+noise on the 8580*, *reads nothing of pulse+noise on the 6581*,
1568
+ *with pulse+noise on the 6581*, *as the test bit rises* and *as the
1569
+ test bit falls* in
1469
1570
  [`sid/waveform_spec.rb`](../spec/badline/sid/waveform_spec.rb).
1470
1571
  - The pulse comparator's output reaches the lines a cycle after the
1471
1572
  accumulator it compared, on both chips. Setting the test bit forces it