badline 0.6.1 → 0.8.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 (78) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +56 -0
  3. data/CLAUDE.md +3 -1
  4. data/README.md +132 -247
  5. data/doc/library.md +62 -0
  6. data/doc/media.md +69 -0
  7. data/doc/sid-player.md +132 -0
  8. data/doc/snapshots.md +69 -0
  9. data/exe/badline-ruby +9 -1
  10. data/lib/badline/address_bus/extra_sids.rb +30 -0
  11. data/lib/badline/address_bus/sid_slots.rb +42 -0
  12. data/lib/badline/address_bus.rb +5 -0
  13. data/lib/badline/audio/aiff.rb +2 -2
  14. data/lib/badline/audio/bare_player.rb +23 -12
  15. data/lib/badline/audio/cli.rb +153 -58
  16. data/lib/badline/audio/jukebox.rb +121 -36
  17. data/lib/badline/audio/machine_player.rb +25 -10
  18. data/lib/badline/audio/pcm_writer.rb +10 -6
  19. data/lib/badline/audio/playback.rb +6 -5
  20. data/lib/badline/audio/queued_tune.rb +89 -0
  21. data/lib/badline/audio/renderer.rb +57 -10
  22. data/lib/badline/audio/stereo.rb +97 -0
  23. data/lib/badline/audio/terminal.rb +39 -6
  24. data/lib/badline/audio/wav.rb +1 -1
  25. data/lib/badline/audio.rb +2 -0
  26. data/lib/badline/cartridge/freezer.rb +2 -0
  27. data/lib/badline/cartridge.rb +3 -0
  28. data/lib/badline/frontend/app.rb +38 -35
  29. data/lib/badline/frontend/audio_sink.rb +6 -5
  30. data/lib/badline/frontend/boot.rb +9 -2
  31. data/lib/badline/frontend/buttons.rb +184 -0
  32. data/lib/badline/frontend/confirmation.rb +50 -0
  33. data/lib/badline/frontend/controls.rb +44 -32
  34. data/lib/badline/frontend/file_browser.rb +225 -0
  35. data/lib/badline/frontend/filter_view.rb +96 -0
  36. data/lib/badline/frontend/frame_report.rb +52 -0
  37. data/lib/badline/frontend/info_view.rb +120 -0
  38. data/lib/badline/frontend/menu_dialogs.rb +122 -0
  39. data/lib/badline/frontend/menu_media.rb +128 -0
  40. data/lib/badline/frontend/menu_pages.rb +198 -0
  41. data/lib/badline/frontend/painter.rb +185 -0
  42. data/lib/badline/frontend/pause_menu.rb +246 -0
  43. data/lib/badline/frontend/player_commands.rb +33 -0
  44. data/lib/badline/frontend/player_footer.rb +86 -0
  45. data/lib/badline/frontend/player_header.rb +60 -0
  46. data/lib/badline/frontend/player_screen.rb +124 -0
  47. data/lib/badline/frontend/player_state.rb +67 -0
  48. data/lib/badline/frontend/player_window.rb +228 -0
  49. data/lib/badline/frontend/scope.rb +42 -0
  50. data/lib/badline/frontend/sid_history.rb +175 -0
  51. data/lib/badline/frontend/sid_view.rb +140 -0
  52. data/lib/badline/frontend/snapshots.rb +1 -1
  53. data/lib/badline/frontend/sound.rb +0 -1
  54. data/lib/badline/frontend/timeline.rb +5 -1
  55. data/lib/badline/frontend/visualizer_view.rb +101 -0
  56. data/lib/badline/frontend.rb +21 -0
  57. data/lib/badline/media/disk_set.rb +108 -0
  58. data/lib/badline/media/queue.rb +158 -0
  59. data/lib/badline/media.rb +14 -8
  60. data/lib/badline/options/help.rb +34 -5
  61. data/lib/badline/options/option.rb +1 -1
  62. data/lib/badline/options/table.rb +5 -3
  63. data/lib/badline/options/validation.rb +54 -0
  64. data/lib/badline/options.rb +65 -46
  65. data/lib/badline/sdl.rb +16 -6
  66. data/lib/badline/sid/catch_up.rb +11 -1
  67. data/lib/badline/sid/filter.rb +9 -0
  68. data/lib/badline/sid/voice.rb +6 -0
  69. data/lib/badline/sid/waveform.rb +20 -5
  70. data/lib/badline/sid.rb +35 -3
  71. data/lib/badline/storage/hvsc.rb +41 -0
  72. data/lib/badline/storage/sid_file.rb +45 -25
  73. data/lib/badline/storage/song_lengths.rb +6 -29
  74. data/lib/badline/storage/stil/credit.rb +50 -0
  75. data/lib/badline/storage/stil.rb +126 -0
  76. data/lib/badline/storage.rb +8 -0
  77. data/lib/badline/version.rb +1 -1
  78. metadata +36 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d667997df6e72a7efd6d8789896402ef09b757c49ac0e1890f9041a22d37d83b
4
- data.tar.gz: ca5e42cebc1e7c1b4879062fab85bd97961242213f6701db86fa57717b270e8d
3
+ metadata.gz: feaedb95b99aa7efed205645c112426a2649ad7a08c975708d085bf69a7e9555
4
+ data.tar.gz: b284281a2e1256f0fe94abf5174990ac8dc8a98cd7035f1cd53aebd3ccbefbd6
5
5
  SHA512:
6
- metadata.gz: 8965b452eb8836de285dc76e7d8220a3b302b81341ee277ab9738ea334e5d27f108b14e1b162b3fe28e88f7aa2fba1e1e30e4dd5fe2b4f7c579643c45e820070
7
- data.tar.gz: 6603b0599428aef7a8bd0b1b5b99300281d02dbde1bd327895dc055a30ce16f2bdfc758b37661bca18eca256b0e98e5080926426dabbf0997c6a96762d7e3793
6
+ metadata.gz: 870ff9cb5c9a9d65c8e9dd8dee6cc87a45871c1a07c79c4b9dc69a692600c3167f92b1debca0f860ab96b783a81bb608276542322b17e305acb82e95c9fc560b
7
+ data.tar.gz: 7c12bf1fd073b2b0e58e41195d67316f0c518a0f5b8c013ded19f025b48456fea738e5ad3df26a796c84151638e8c0449b5bf14b7d013fcb876c61db5d2d9469
data/CHANGELOG.md CHANGED
@@ -1,5 +1,61 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.8.0](https://github.com/elektronaut/badline/compare/v0.7.0...v0.8.0) (2026-10-03)
4
+
5
+
6
+ ### ⚠ BREAKING CHANGES
7
+
8
+ * F9 opens the pause menu instead of swapping the joysticks, which the Ports page does now, and Tab no longer steps through the mouse and paddle modes.
9
+ * --read-only is gone, and disks are write-protected by default. Pass --writable to let the machine write to a disk image.
10
+
11
+ ### Features
12
+
13
+ * find the disks of a set by their names ([b2a4ba4](https://github.com/elektronaut/badline/commit/b2a4ba47bb4c9d4175130104c4dd2d3d2d5e570a))
14
+ * pause the machine in a menu on F9 ([8b2568c](https://github.com/elektronaut/badline/commit/8b2568c8ebe894c3da56d404217189dc7b27db84))
15
+ * put disks in write-protected unless --writable ([aed9fa1](https://github.com/elektronaut/badline/commit/aed9fa173007c85a5b9c19840e011d34f5b6b615))
16
+
17
+
18
+ ### Bug Fixes
19
+
20
+ * stop printing that the window runs below real time ([86a57a6](https://github.com/elektronaut/badline/commit/86a57a635189a8e46e285773ce62ecbf1dd5adde))
21
+
22
+ ## [0.7.0](https://github.com/elektronaut/badline/compare/v0.6.1...v0.7.0) (2026-10-02)
23
+
24
+
25
+ ### ⚠ BREAKING CHANGES
26
+
27
+ * call a .sid tune's parts subtunes everywhere
28
+
29
+ ### Features
30
+
31
+ * add a badline sid subcommand that plays tunes and directories ([7dfc21b](https://github.com/elektronaut/badline/commit/7dfc21b5e4ba47cd37a4593da61e95bb1d2329ab))
32
+ * drop .sid files and folders on the SID player to queue them ([cc1f434](https://github.com/elektronaut/badline/commit/cc1f434b7404ded067ef43d1d3d5aa53d338de4c))
33
+ * end a subtune with no known length when it falls silent ([46c4f10](https://github.com/elektronaut/badline/commit/46c4f1064f6382872ec22989253bc9c159f78dcf))
34
+ * open a .sid tune, or no tune at all, in the SID player ([396c010](https://github.com/elektronaut/badline/commit/396c010ee626f95fbb71ced8bf3905909c470c8e))
35
+ * play 2SID and 3SID tunes in stereo in the SID player ([7c72eef](https://github.com/elektronaut/badline/commit/7c72eeff7e5e7b1c56286f9bf6030ae3408f7176))
36
+ * play a queue of tunes, with shuffle, loop and play-all subtunes ([d163106](https://github.com/elektronaut/badline/commit/d163106eea99d90f6ffe74fb91474a2aca4c64eb))
37
+ * play sid tunes in a window that shows what the SID is doing ([60bfd3f](https://github.com/elektronaut/badline/commit/60bfd3f75fe82d928afa374d257d3d21e5ac29ad))
38
+ * seek forward from where the subtune is, not from its start ([34059e8](https://github.com/elektronaut/badline/commit/34059e8f9b12cab44c94124e6455fef9c915d8eb))
39
+ * seek within a subtune, and let the slow warning lapse ([b98ac6e](https://github.com/elektronaut/badline/commit/b98ac6eb6f9c0ce088545f55cc5f2445dd38dd2d))
40
+ * show a tune's STIL entry in the SID player ([82d9dca](https://github.com/elektronaut/badline/commit/82d9dca26d7cefe853a6af41e50fc723a3e90170))
41
+ * show each voice's live output in the SID view ([c61a128](https://github.com/elektronaut/badline/commit/c61a1289983887c0175fca3dc4c37b470e96de92))
42
+ * show HVSC's STIL in the SID player ([bd55c0c](https://github.com/elektronaut/badline/commit/bd55c0c87695ba8cc84dbfd51ab2119a97eccded))
43
+ * show where a seek is headed on the SID player's bar ([1a84b37](https://github.com/elektronaut/badline/commit/1a84b3746a960dba28dc9c789b3d2cd1520d2f94))
44
+ * step subtunes and tunes on separate keys, add --all-subtunes ([eaef338](https://github.com/elektronaut/badline/commit/eaef3384e6205163e2a4b63f5aa8d9204fe5190f))
45
+ * switch a SID between the 6581 and 8580 while it runs ([5920391](https://github.com/elektronaut/badline/commit/592039123d7936b8d22c8fd5f144a9bc9a8f383b))
46
+
47
+
48
+ ### Bug Fixes
49
+
50
+ * build the native badline with a Spinel that keeps an array joined mid-expression alive ([d2c4eb2](https://github.com/elektronaut/badline/commit/d2c4eb20d6b80494fb2a20105c0532f3778b205c))
51
+ * build the native badline with a Spinel that reports render progress ([58524ea](https://github.com/elektronaut/badline/commit/58524eaf4881661e7e0e9dd38c32071545bf1fae))
52
+ * show non-ASCII text from a .sid header in the native badline ([959bb6f](https://github.com/elektronaut/badline/commit/959bb6f7d07de4c90ac50ae51ae63f3c66601a78))
53
+
54
+
55
+ ### Performance Improvements
56
+
57
+ * draw the SID player's traces in one call each ([afcae15](https://github.com/elektronaut/badline/commit/afcae15ed273aa7a6dd49cb31e0f7180c56feb75))
58
+
3
59
  ## [0.6.1](https://github.com/elektronaut/badline/compare/v0.6.0...v0.6.1) (2026-09-30)
4
60
 
5
61
 
data/CLAUDE.md CHANGED
@@ -164,7 +164,9 @@ the rows your change can't reach tell you nothing about it.
164
164
  - A pull request's CI is the verdict. It runs every suite on the Spinel
165
165
  build, compared row by row against
166
166
  `test/baselines/`, and every job is a required check. A row that moved
167
- fails it. Re-record only those rows, in the same change:
167
+ fails it. A pull request that changes only docs, or only a release's
168
+ version bump, skips the specs and suites; pushes to main always run
169
+ them. Re-record only those rows, in the same change:
168
170
  `rake "regression:record:<suite>[filter,...]"` (quote it, because zsh
169
171
  globs the brackets). It runs only the matching tests on CRuby and
170
172
  splices their rows into the baseline, and every other row keeps its
data/README.md CHANGED
@@ -56,46 +56,47 @@ 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.
78
-
79
- The KERNAL, BASIC and character ROMs come with the gem. To run other
80
- images, such as a patched KERNAL, point `BADLINE_ROM_PATH` at a
81
- directory that holds `kernal.rom`, `basic.rom` and `character.rom`,
82
- plus `eapi/eapi-am29f040-14` if you attach EasyFlash cartridges. From
83
- Ruby, `Badline.rom_path = dir` does the same before a
84
- `Badline::Computer` is built, and `nil` restores the bundled set.
85
-
86
- Both run the same window. `badline` plays the SID through the host's
87
- audio device, and `F10` mutes and unmutes it. `--no-sound` turns it
88
- off. The window is paced by the display's vsync; `--no-vsync` paces it
89
- by a timer, or by the sound 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, then the frame rate
93
- and the time each frame takes, once a second.
94
-
95
- For a scripted run, `--frames N` quits after N frames, `--unpaced` runs
96
- as fast as it can and `--at FRAME:EVENT` presses keys, types, swaps
97
- media and takes screenshots once that many frames have run. `--script
98
- FILE` takes one `FRAME:EVENT` a line:
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
+ | `--writable` | Let the machine write to disk images. Without it, a disk goes in 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
+ ### Scripted runs
91
+
92
+ | Option | Effect |
93
+ | --- | --- |
94
+ | `--frames N` | Quit after N frames |
95
+ | `--unpaced` | Run as fast as it can, without vsync or pacing |
96
+ | `--at FRAME:EVENT` | Press keys, type, swap media or take a screenshot once `FRAME` frames have run |
97
+ | `--script FILE` | Run the events in `FILE`, one `FRAME:EVENT` a line |
98
+ | `--screenshot FILE` | Save the last frame as a `.bmp` |
99
+ | `--save-snapshot FILE` | Save the machine as a `.vsf` snapshot after the last frame |
99
100
 
100
101
  ```sh
101
102
  badline --unpaced --frames 12000 --true-drive disk1.d64 \
@@ -106,161 +107,57 @@ badline --unpaced --frames 12000 --true-drive disk1.d64 \
106
107
  `--help` lists the events, and [native/README.md](native/README.md#running)
107
108
  describes them.
108
109
 
109
- In `badline-ruby` sound is off by default, and `--sound` turns it on.
110
- The machine runs below real time there, so the sound stutters: it plays
111
- in bursts with silent gaps between them, at the right pitch, and never
112
- slows the emulation down.
110
+ Other KERNAL, BASIC or character ROMs, and using badline as a Ruby
111
+ library, are covered in [doc/library.md](doc/library.md).
113
112
 
114
113
  ## Media
115
114
 
116
115
  | Format | Handling |
117
116
  |--------|----------|
118
117
  | `.prg`, `.p00` | Loaded into memory after boot. A program at the BASIC start (`$0801`) is `RUN`, anything else is left for you to `SYS` |
119
- | `.d64`, `.d71`, `.d81` | Mounted read-write as device 8, then `LOAD"*",8,1` and `RUN`. Writes go straight back to the image file. With `--read-only`, or an image the host can't write, it acts as a write-protected disk |
120
- | `.g64` | Put in a true 1541, which is plugged in as device 8 for it, then `LOAD"*",8,1` and `RUN`. The image holds the disk's raw GCR, half tracks and all, so copy protection and fast loaders that read it work. Tracks the drive writes go back to the image file. With `--read-only`, or an image the host can't write, it acts as a write-protected disk |
118
+ | `.d64`, `.d71`, `.d81` | Mounted as device 8, then `LOAD"*",8,1` and `RUN`. Write-protected unless `--writable` |
119
+ | `.g64` | Put in a true 1541, then `LOAD"*",8,1` and `RUN`. It holds the disk's raw GCR, so copy protection that reads it works. Write-protected unless `--writable` |
121
120
  | `.t64` | Mounted read-only as device 8 and loaded like a disk image. The files load by name, and no tape is involved |
122
121
  | `.tap` | Inserted in the datasette with PLAY pressed, then `LOAD` and `RUN`. It loads at the speed of a real tape |
123
122
  | `.crt` | The hardware types listed under [Cartridges](#whats-emulated). Other types are rejected |
124
123
  | `.sid` | PSID and RSID tunes, started through a small driver after boot |
125
124
  | `.vsf` | A snapshot of a running machine, badline's own or one VICE's x64sc saved. See [Snapshots](#snapshots) |
126
- | A directory | Mounted read-write as device 8. It serves the `.prg` and `.p00` files in it and the contents of any `.t64`, and `SAVE` writes a new `.prg` |
127
-
128
- Apart from a `.g64`, which only a true drive can read, there is no 1541
129
- unless `--true-drive` asks for one. Device 8 works by trapping the
130
- KERNAL's `LOAD` and `SAVE` routines and its serial bus primitives, so
131
- files open by name through `OPEN` and `CHRIN` as well. `LOAD"$",8` lists
132
- the directory of any medium mounted there, as a 1541 does. The command
133
- channel answers `I`, `B-P` and `U1` block reads, which covers loaders
134
- that read blocks directly. On a disk image it also takes `SAVE` (with
135
- `@0:` to replace a file), files opened for writing or appending, `S` to
136
- scratch, `U2` and `B-W` block writes, and `B-A` and `B-F`. Loaders that
137
- upload their own code to the drive with `M-W` and `M-E`, and copy
138
- protection that reads raw GCR, won't work there, but do on a true
139
- drive.
140
-
141
- `--true-drive` puts an emulated 1541 on device 8 instead, running its
142
- own DOS ROM on its own 6502 and talking to the machine over the serial
143
- bus. It reads `.d64` and `.g64` images, which it autostarts with the
144
- same `LOAD"*",8,1` and `RUN`, but not `.d71`, `.d81` or `.t64` images
145
- or directories. `--read-only` puts the disk in write-protected. Loading
146
- runs at the speed of a real 1541, and the drive's red LED lights in the
147
- bottom right corner of the border. Both executables take it.
148
-
149
- `Badline::Media.insert_disk(computer, path)` swaps the disk image or
150
- directory in device 8 while the machine runs, for software that asks for
151
- another disk. It takes `read_only: true`, and `Badline::Media.attach`
152
- takes `disk: { read_only: true }`, to mount a disk image write-protected,
153
- as the test harnesses under `bin/` do. A `.g64` goes in the true 1541,
154
- and takes out a disk mounted through the traps, so `LOAD` and `SAVE`
155
- reach the 1541 too. With the 1541 in device 8, a `.d64` goes in its
156
- drive as well.
125
+ | A directory | Mounted as device 8. It serves the `.prg` and `.p00` files in it and the contents of any `.t64`, and `SAVE` writes a new `.prg` |
126
+
127
+ Without `--true-drive`, device 8 is no drive at all but traps on the
128
+ KERNAL's disk routines. Loading is instant, but fast loaders and copy
129
+ protection that run code on the drive need `--true-drive`, which puts
130
+ an emulated 1541 there, running its own DOS at a real 1541's speed. Its
131
+ red LED lights in the bottom right corner of the border.
132
+ [doc/media.md](doc/media.md) has the details, and how to attach media
133
+ from Ruby.
157
134
 
158
135
  ## Snapshots
159
136
 
160
- In the window, `F11` saves the whole machine to a new
161
- `badline-<date>-<time>.vsf` in the working directory, and `F12` goes
162
- back to the snapshot last saved or opened, in a new machine built as the
163
- saved one was. Both executables do this, and both open a `.vsf` given as
164
- the media. A snapshot that fails to open leaves the machine running as
165
- it was. From Ruby, `computer.save_snapshot(path)` saves,
166
- `computer.restore_snapshot(path)` takes a machine back to a snapshot,
167
- and `Badline::Snapshot.load(path)` builds a new machine as the saved one
168
- was built and restores it. `computer.snapshot` and
169
- `computer.restore(state)` do the same in memory, without a file. A
170
- restore that fails leaves the machine as it was.
171
-
172
- A snapshot holds the machine as it was on the cycle it was saved: the
173
- chips down to the instruction step and the pixel pipeline, the RAM and
174
- any +60K or +256K expansion, the cartridge with its RAM and flash, a
175
- GEO-RAM, a true 1541 with its RAM, its VIAs and the disk under the head,
176
- a disk or directory mounted through the traps with its open channels,
177
- and the tape with its place on it. A restored machine runs on exactly
178
- as the saved one would have. A directory, a tape and a true drive's
179
- disk image open again from their paths when the snapshot is restored,
180
- the disk with its tracks as the drive last saw them. A disk image mounted through the traps comes back
181
- with its contents from the snapshot. What the host holds stays the host's: the keyboard, the
182
- joysticks, the mouse and paddles, sound, and blocks given to `on_init`
183
- that hadn't run yet, which a restore reports. A snapshot only restores
184
- in the badline version that wrote it, into a machine with the same
185
- chip models, RAM expansion and REU. `computer.snapshot` holds an REU's
186
- RAM, registers and transfer too, but a machine with an REU doesn't save
187
- to a file yet, as badline doesn't write VICE's REU module.
188
-
189
- Snapshots use VICE's `.vsf` format. badline writes VICE's modules for
190
- the CPU, RAM and CPU port, both CIAs, the SID and the VIC-II, plus the
191
- ones x64sc needs to open the file, with nothing attached to the
192
- cartridge, tape or user ports and no true drive. A `BADLINE` module,
193
- which VICE skips, holds the whole machine, and badline restores its own
194
- snapshots from it.
195
-
196
- x64sc 3.10 opens badline's snapshots, taken at the end of the
197
- instruction the CPU was in, as long as its VIC-II model matches
198
- (`-model c64` for the default machine). It keeps its own drives and
199
- leaves out the cartridge, the expansions and the tape. Snapshots x64sc
200
- saves open in badline through the same modules: the CPU at its
201
- instruction boundary, RAM, the CPU port, the CIAs' registers, timers and
202
- clocks, the SID's registers and reSID voice state, and the VIC-II's
203
- registers, beam position, counters and colour RAM. The VIC-II's pixel
204
- pipeline starts empty. badline reads the modules x64sc 3.7 to 3.10
205
- write, and VICE's development versions' `MAINC64CPU` and `VIC-IISC`. A module version
206
- it doesn't know is left out, or fails the restore for the CPU and RAM.
207
- It reports the modules it leaves out, such as the 1541 drives, the
208
- cartridge, the datasette and the keyboard, and carries on without them.
209
- An NTSC snapshot fails, as badline runs PAL only. Restored into a
210
- running machine, a VICE snapshot fails for a machine built another way,
211
- such as a C64C's snapshot in a C64, and otherwise takes the cartridge
212
- out and switches the machine off and on.
137
+ `F11` saves the whole machine to a new `badline-<date>-<time>.vsf` in
138
+ the working directory, and `F12` goes back to the snapshot last saved
139
+ or opened. Snapshots use VICE's `.vsf` format: badline opens those
140
+ x64sc saves, and x64sc opens badline's. See
141
+ [doc/snapshots.md](doc/snapshots.md) for what they hold and how far the
142
+ two agree.
213
143
 
214
144
  ## Playing and rendering SID tunes
215
145
 
216
- `badline-ruby --headless` plays a `.sid` tune on the host's audio
217
- device without opening the window, and `--audio-out` renders it to a
218
- 16-bit PCM file instead. The file's extension picks the format, `.wav`
219
- or `.aiff`. The native `badline` has both modes too.
220
-
221
146
  ```sh
222
- badline-ruby --headless tune.sid # play, length from HVSC
223
- badline-ruby --headless -s 3 tune.sid # play the third subtune
224
- badline-ruby --seconds 180 tune.sid --audio-out out.aiff
225
- badline-ruby -s 3 --rate 48000 tune.sid --audio-out out.wav
226
- badline-ruby --headless --sid 8580 tune.sid
227
- badline-ruby --filter-chunk 1 tune.sid --audio-out out.wav # exact filter, slower
147
+ badline tune.sid # play a tune in the SID player
148
+ badline sid ~/C64Music/MUSICIANS/H/Hubbard_Rob # play every tune below a directory
149
+ badline sid --headless tune.sid other.sid # play a queue in the terminal
150
+ badline --seconds 180 tune.sid --audio-out out.wav # render to a .wav or .aiff
228
151
  ```
229
152
 
230
- Both modes take the same options. The window's own, `--no-autostart`,
231
- `--read-only`, `--sound`, `--true-drive`, `--reu`, `--ntsc` and
232
- `--verbose`, don't apply to them. `--song` (or `-s`) picks the subtune, counting from 1 as HVSC
233
- does, and defaults to the tune's own start song. Playback asks the
234
- device for 44.1 kHz and takes whatever rate it offers, unless `--rate`
235
- says otherwise. Ctrl-C stops it.
236
-
237
- Played on a terminal, `--headless` shows the tune's name, author and
238
- release, the song number and the time played against the song's
239
- length. When a song reaches its length it goes on to the next, and it
240
- stops after the last. `n` or → skips to the next song, `p` or ← goes
241
- back one, space pauses and `q` quits. `--no-tui`, or output that isn't a
242
- terminal, gives plain progress output instead and plays just the one
243
- song, as `--audio-out` renders just the one.
244
-
245
- A `.sid` file doesn't store its length, so `badline-ruby` looks the
246
- tune up by MD5 in HVSC's `Songlengths.md5`. It finds the database
247
- through `--songlengths`, in a `DOCUMENTS` directory in any of the
248
- tune's parent directories (the layout of an HVSC collection), or under
249
- `$HVSC_BASE/DOCUMENTS`. Without a database or `--seconds` it runs for
250
- 60 seconds.
251
-
252
- PSID tunes run on a CPU and RAM with only the SID clocked, at about
253
- twice real time, so they play smoothly. RSID tunes set up their own
254
- interrupts, so they boot a full C64 first and run at about half real
255
- time. They render fine but stutter when played, and `badline-ruby` says
256
- so when it falls behind. The filter steps four cycles at a time;
257
- `--filter-chunk 1` steps it every cycle, which is exact and takes about
258
- twice as long. `badline-ruby --help` lists the options.
259
-
260
- The native `badline` takes the same options and runs the same code, so
261
- it renders the same file sample for sample, and it plays RSID tunes
262
- without stuttering. See
263
- [native/README.md](native/README.md#without-the-window).
153
+ The SID player plays a queue of tunes, looking up each subtune's length
154
+ and STIL entry in an HVSC collection. Its window shows each voice's
155
+ note and output, the SID's registers, envelopes and filter, and the
156
+ tune's STIL entry, and is worked with the mouse. Tunes for 2 or 3 SIDs
157
+ play in stereo. [doc/sid-player.md](doc/sid-player.md) covers the
158
+ options, the keys and the window.
159
+
160
+ ![The SID player's visualizer playing Rob Hubbard's Delta](doc/images/sid-player.png)
264
161
 
265
162
  ## Input
266
163
 
@@ -283,109 +180,97 @@ types `"`. These keys have no same-named host key:
283
180
  | `+` / `*` | Keypad `+` / Keypad `*` |
284
181
  | `RESTORE` | `Page Up` |
285
182
 
286
- `Tab` steps through the input modes and `Shift-Tab` steps back. The
287
- window title shows the current mode:
183
+ `Tab` switches the keys between the C64 keyboard and the joysticks. The
184
+ window title shows where they go, and any device plugged into a control
185
+ port:
288
186
 
289
- | Mode | What the host drives |
290
- |------|----------------------|
187
+ | Title | What the host drives |
188
+ |-------|----------------------|
291
189
  | (none) | The keyboard |
292
- | `[JOY 2]` / `[JOY 1]` | Arrows and Space (or Right Ctrl) are the joystick named, `WASD` and Left Shift the other. `F9` swaps them |
190
+ | `[JOY 2]` / `[JOY 1]` | Arrows and Space (or Right Ctrl) are the joystick named, `WASD` and Left Shift the other. SWAP JOYSTICKS on the pause menu's Ports page swaps them |
293
191
  | `[MOUSE 1]` / `[MOUSE 2]` | A 1351 mouse in control port 1 or 2 |
294
192
  | `[PADDLE 1]` / `[PADDLE 2]` | A pair of paddles in control port 1 or 2 |
295
193
 
296
- The mouse and paddle modes capture the host mouse until you `Tab` out of
297
- them. Moving it moves the 1351 or turns the two paddle knobs, and the
298
- left and right buttons are the 1351's buttons, or the fire buttons of
299
- paddles A and B. Games differ in which port they read, which is why each
300
- device has a mode per port.
194
+ A 1351 mouse or a pair of paddles plugs into either port on the pause
195
+ menu's Ports page, and stays there whichever way `Tab` sends the keys.
196
+ While one is plugged in it holds the host mouse, and opening the pause
197
+ menu lets go. Moving it moves the 1351 or turns the two paddle knobs,
198
+ and the left and right buttons are the 1351's buttons, or the fire
199
+ buttons of paddles A and B. Games differ in which port they read.
301
200
 
302
201
  Game controllers work in every mode. The first one is joystick 2 and
303
202
  the second is joystick 1. The D-pad and left stick steer, the face and
304
203
  shoulder buttons fire, and controllers can be connected or removed while
305
204
  the emulator runs.
306
205
 
307
- Control port 1's fire line is also the VIC-II's light pen input, so
308
- joystick 1's fire button and the 1351's left button in port 1 latch the
309
- light pen registers.
310
-
311
206
  `F10` mutes and unmutes the sound, and the window title shows `[MUTED]`
312
207
  while it's off.
313
208
 
209
+ ## The pause menu
210
+
211
+ `F9` pauses the machine and opens a menu over the frozen picture, and
212
+ `F9` or `Esc` closes it again. On a Mac, hold `Fn` for `F9`, unless the
213
+ function keys are set to work as standard function keys. Up and Down move
214
+ through the rows, Right goes into a page and Left back out, and Return or
215
+ Space presses a row. A row with choices steps to the next. The mouse works
216
+ too.
217
+
218
+ | Page | What it holds |
219
+ |------|---------------|
220
+ | Drive 8 | The disk: insert, eject, the previous or next disk of its set, and whether it's writable |
221
+ | Datasette | The tape: insert, eject, PLAY and REWIND |
222
+ | Expansion port | The cartridge: insert, remove, and its freeze button if it has one |
223
+ | Ports | The device in each control port, and where the keys go |
224
+ | Sound | Mute, and the SID's model |
225
+ | Power | Reset, power cycle and quit |
226
+
227
+ INSERT opens a file browser. A disk's set comes from the names in its
228
+ folder: `Disk 1`, `Side B`, `d2`, TOSEC's `(Disk 1 of 2)`, or a trailing
229
+ `_1`, `_2` when the first of the set is there. WRITABLE starts as
230
+ `--writable` sets it and stays as set for the next disk. Quick open starts
231
+ a file in a new machine, as the command line does. It asks first, as
232
+ inserting or removing a cartridge does, since each power cycles the
233
+ machine.
234
+
235
+ A disk or tape dropped on the window goes into its drive. A cartridge,
236
+ program or `.sid` dropped on it opens the menu to ask first.
237
+
314
238
  ## What's emulated
315
239
 
316
240
  - **6510**: every opcode, documented and undocumented, with per-cycle
317
241
  bus behaviour checked against the
318
242
  [65x02 single step tests](https://github.com/SingleStepTests/65x02).
319
- `JAM` opcodes halt the CPU until reset.
320
- - **Memory**: banking through the 6510 port, including the cartridge
321
- `EXROM`/`GAME` lines and Ultimax mode. The +60K and +256K RAM
322
- expansions fit with `Badline::Computer.new(ram_expansion: :plus60k)` or
323
- `:plus256k`, banked through their register at `$D100`.
324
- - **VIC-II** (PAL 6569): the five standard graphics modes and the
325
- invalid ones, sprites with multicolour, expansion, priority and
326
- pixel-level collisions, raster interrupts, bad lines, sprite DMA, the
327
- border, VIC banks and the light pen.
328
- `Badline::Computer.new(vic_model: :mos8565)` fits the C64C's 8565
329
- instead, with its grey dots on colour register writes and its own
330
- timing for mode splits, sprite multicolour splits and the light pen.
331
- `Badline::Computer.new(region: Badline::Region::NTSC)` builds an NTSC
332
- machine instead: the 6567R8's 65 cycles by 263 lines at 1,022,727 Hz,
333
- with its later sprite fetches and its X counter, and TOD clocks on
334
- 60 Hz mains. `Badline::Region::NTSC_OLD` is the first NTSC C64s'
335
- 6567R56A, 64 cycles by 262 lines. The stock KERNAL tells them from PAL
336
- by the raster, so every region boots the same ROMs. `--ntsc` picks the
337
- 6567R8.
338
- - **CIA 1 and 2**: timers, time-of-day clocks with alarms, the serial
339
- shift register, interrupts, the keyboard matrix with its ghost keys,
340
- the control ports and the paddle multiplexer. The machine has the
341
- original 6526s; `Badline::Computer.new(cia_model: :mos6526a)` fits the
342
- C64C's 6526As instead, whose interrupt register timing differs.
343
- - **SID**: the 6581 and the 8580, with oscillators, ring modulation and
344
- sync, the envelope generator including the ADSR delay bug, the filter,
345
- and the RC network on the board that removes the DC offset from the
346
- output. The machine has a 6581 unless a `.sid` tune asks for an 8580
347
- in its header, and `--sid 6581` or `--sid 8580` overrides either.
348
- - **REU**: the 1700, 1764 and 1750 RAM Expansion Units, and the bigger
349
- units up to 16M built on the same REC chip, with DMA timed against the
350
- VIC's bad lines and sprites. `--reu SIZE` plugs one in, and
351
- `Badline::Computer.new(reu: 512)` does the same from Ruby. An
352
- REU beside a cartridge loses I/O 2 to the cartridge.
353
- - **Datasette**: `.tap` playback into CIA 1's FLAG line, with the motor
354
- and sense lines on the 6510 port.
243
+ - **Memory**: banking through the 6510 port, the cartridge lines and
244
+ Ultimax mode, and the +60K and +256K RAM expansions.
245
+ - **VIC-II**: the PAL 6569, the C64C's 8565, and NTSC's 6567R8 (`--ntsc`)
246
+ and 6567R56A: every graphics mode, sprites, collisions, bad lines,
247
+ sprite DMA, the border and the light pen.
248
+ - **CIA 1 and 2**: the 6526 and the C64C's 6526A, with timers,
249
+ time-of-day clocks, the serial shift register, the keyboard matrix
250
+ and the control ports.
251
+ - **SID**: the 6581 and the 8580, filter and all.
252
+ - **REU**: the 1700, 1764 and 1750 and bigger units up to 16M
253
+ (`--reu`), with DMA timed against the VIC's bad lines and sprites.
254
+ - **Datasette**: `.tap` playback.
255
+ - **1541**: an emulated drive running its own DOS (`--true-drive`).
355
256
  - **Cartridges**: standard 8K, 16K and Ultimax, Simons' BASIC, Ocean,
356
- Fun Play / Power Play, Super Games, Epyx FastLoad, Westermann Learning,
357
- Rex Utility, C64 Game System / System 3, Dinamic, Zaxxon / Super Zaxxon,
358
- Magic Desk, Comal-80, EasyFlash, Mach 5, Pagefox, RGCD and GMod2, and
359
- the freezers Action Replay (v4.2 to v6), Atomic Power / Nordic Power,
360
- Retro Replay / Nordic Replay, Final Cartridge III / III+ and the KCS
361
- Power Cartridge.
362
- EasyFlash and GMod2 flash takes writes through the chip's command set
363
- (program, sector and chip erase, autoselect), so games and EAPI can
364
- save to it. An EasyFlash image's EAPI is swapped for a bundled copy of
365
- the Am29F040 EAPI on attach, as VICE does. The writes stay in memory
366
- and are lost when the emulator quits: the `.crt` file is never
367
- overwritten. The Retro Replay's flash
368
- works the same way in flash mode, which the flash jumper enables:
369
- `Media.attach(computer, path, cartridge: { flash_jumper: true })`, with
370
- `bank_jumper: true` to run from the second 64K of a 128K image. The
371
- GMod2 EEPROM and the Retro Replay clock port aren't there.
372
- - **GEO-RAM**: 64K to 4M of RAM seen through the `$DE00` page, with the
373
- `$DFFE`/`$DFFF` page and block registers. It takes the expansion port,
374
- so it can't sit alongside a cartridge:
375
- `computer.attach_cartridge(Badline::Cartridge::GeoRAM.new(size: 512))`.
376
- Its contents are lost when the emulator quits.
257
+ Fun Play / Power Play, Super Games, Epyx FastLoad, Westermann
258
+ Learning, Rex Utility, C64 Game System / System 3, Dinamic, Zaxxon /
259
+ Super Zaxxon, Magic Desk, Comal-80, EasyFlash, Mach 5, Pagefox, RGCD,
260
+ GMod2 and GEO-RAM, and the freezers Action Replay (v4.2 to v6), Atomic
261
+ Power / Nordic Power, Retro Replay / Nordic Replay, Final Cartridge III
262
+ / III+ and the KCS Power Cartridge. Flash writes stay in memory.
263
+
264
+ Machines other than the default, and the cartridges' jumpers, are
265
+ built from Ruby: see [doc/library.md](doc/library.md).
377
266
 
378
267
  Known gaps:
379
268
 
380
- - `badline-ruby` runs below real time, so its live audio stutters.
381
- `badline` plays smoothly, and so do PSID tunes under
382
- `badline-ruby --headless`.
269
+ - `badline-ruby` runs below real time, so its sound, which `--sound`
270
+ turns on, stutters: it plays in bursts with silent gaps between them.
383
271
  - Without `--true-drive`, fast loaders and anything else that runs code
384
- on the drive won't work (see [Media](#media)). The command channel
385
- doesn't rename, copy, format or validate disks.
272
+ on the drive won't work.
386
273
  - No PAL-N (Drean) machine.
387
- - The emulator window has no freeze button yet, so a freezer cartridge
388
- runs its menu but can't freeze a program.
389
274
 
390
275
  ## Contributing
391
276
 
data/doc/library.md ADDED
@@ -0,0 +1,62 @@
1
+ # Using badline from Ruby
2
+
3
+ The gem is also a library, which runs a `Badline::Computer` from Ruby
4
+ without a window.
5
+
6
+ ```ruby
7
+ require "badline"
8
+
9
+ computer = Badline::Computer.new
10
+ Badline::Media.attach(computer, "game.d64") # attaches and autostarts
11
+ computer.on_init { computer.type_text("print 6*7\r") } # once the machine has booted
12
+ 3_500_000.times { computer.cycle! }
13
+ computer.address_bus.peek(0x0400) # screen RAM starts at $0400
14
+ ```
15
+
16
+ [media.md](media.md) covers attaching and swapping media, and
17
+ [snapshots.md](snapshots.md) saving and restoring the machine.
18
+
19
+ ## Building a machine
20
+
21
+ `Badline::Computer.new` builds the default machine: a PAL C64 with the
22
+ 6569 VIC-II, 6526 CIAs and a 6581 SID. Its keywords build others:
23
+
24
+ - `vic_model: :mos8565` fits the C64C's 8565, with its grey dots on
25
+ colour register writes and its own timing for mode splits, sprite
26
+ multicolour splits and the light pen.
27
+ - `region: Badline::Region::NTSC` builds an NTSC machine: the 6567R8's
28
+ 65 cycles by 263 lines at 1,022,727 Hz, with its later sprite fetches
29
+ and its X counter, and TOD clocks on 60 Hz mains.
30
+ `Badline::Region::NTSC_OLD` is the first NTSC C64s' 6567R56A, 64 cycles
31
+ by 262 lines. The stock KERNAL tells them from PAL by the raster, so
32
+ every region boots the same ROMs.
33
+ - `cia_model: :mos6526a` fits the C64C's 6526As, whose interrupt
34
+ register timing differs.
35
+ - `sid_model: :mos8580` fits the 8580.
36
+ - `ram_expansion: :plus60k` or `:plus256k` fits the +60K or +256K RAM
37
+ expansion, banked through its register at `$D100`.
38
+ - `reu: 512` plugs in a RAM Expansion Unit of that many K.
39
+
40
+ ## Cartridges
41
+
42
+ `Media.attach(computer, path, cartridge: { flash_jumper: true })` sets
43
+ the Retro Replay's flash jumper, which makes its flash take writes, and
44
+ `bank_jumper: true` runs it from the second 64K of a 128K image.
45
+ EasyFlash and GMod2 flash take writes through the chip's command set
46
+ (program, sector and chip erase, autoselect), so games and EAPI can save
47
+ to it. An EasyFlash image's EAPI is swapped for a bundled copy of the
48
+ Am29F040 EAPI on attach, as VICE does. Flash writes stay in memory and
49
+ are lost when the emulator quits: the `.crt` file is never overwritten.
50
+
51
+ A GEO-RAM takes the expansion port, so it can't sit alongside a
52
+ cartridge: `computer.attach_cartridge(Badline::Cartridge::GeoRAM.new(size: 512))`.
53
+ Its contents are lost when the emulator quits.
54
+
55
+ ## ROMs
56
+
57
+ The KERNAL, BASIC and character ROMs come with the gem. To run other
58
+ images, such as a patched KERNAL, point `BADLINE_ROM_PATH` at a
59
+ directory that holds `kernal.rom`, `basic.rom` and `character.rom`, plus
60
+ `eapi/eapi-am29f040-14` if you attach EasyFlash cartridges.
61
+ `Badline.rom_path = dir` does the same before a `Badline::Computer` is
62
+ built, and `nil` restores the bundled set.