badline 0.5.0 → 0.6.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +24 -0
- data/CLAUDE.md +23 -23
- data/CONTRIBUTING.md +5 -0
- data/README.md +59 -1
- data/doc/pinned-behaviour.md +4 -2
- data/exe/badline-ruby +2 -1
- data/lib/badline/address_bus/saved_state.rb +72 -0
- data/lib/badline/address_bus.rb +1 -0
- data/lib/badline/audio/stream.rb +6 -0
- data/lib/badline/cartridge/action_replay.rb +15 -0
- data/lib/badline/cartridge/comal80.rb +8 -0
- data/lib/badline/cartridge/dinamic.rb +8 -0
- data/lib/badline/cartridge/easy_flash.rb +20 -1
- data/lib/badline/cartridge/epyx_fastload.rb +8 -0
- data/lib/badline/cartridge/final_cartridge3.rb +12 -0
- data/lib/badline/cartridge/flash.rb +41 -0
- data/lib/badline/cartridge/fun_play.rb +8 -0
- data/lib/badline/cartridge/g_mod2.rb +12 -0
- data/lib/badline/cartridge/game_system.rb +8 -0
- data/lib/badline/cartridge/geo_ram.rb +16 -0
- data/lib/badline/cartridge/magic_desk.rb +8 -0
- data/lib/badline/cartridge/ocean.rb +8 -0
- data/lib/badline/cartridge/pagefox.rb +12 -0
- data/lib/badline/cartridge/retro_replay.rb +44 -1
- data/lib/badline/cartridge/rgcd.rb +10 -0
- data/lib/badline/cartridge/super_games.rb +10 -0
- data/lib/badline/cartridge/zaxxon.rb +16 -0
- data/lib/badline/cartridge.rb +72 -0
- data/lib/badline/cia/interrupt_register.rb +17 -0
- data/lib/badline/cia/serial.rb +44 -0
- data/lib/badline/cia/state.rb +47 -0
- data/lib/badline/cia/timer.rb +33 -0
- data/lib/badline/cia.rb +24 -2
- data/lib/badline/computer/attachments.rb +132 -0
- data/lib/badline/computer/saved_state.rb +116 -0
- data/lib/badline/computer.rb +8 -31
- data/lib/badline/cpu/microcode.rb +8 -0
- data/lib/badline/cpu/saved_state.rb +115 -0
- data/lib/badline/cpu.rb +2 -0
- data/lib/badline/datasette.rb +27 -0
- data/lib/badline/drive1541/bus.rb +89 -13
- data/lib/badline/drive1541/disk.rb +7 -2
- data/lib/badline/drive1541/disk_state.rb +80 -0
- data/lib/badline/drive1541/idle.rb +9 -57
- data/lib/badline/drive1541/mechanism.rb +2 -0
- data/lib/badline/drive1541/mechanism_state.rb +67 -0
- data/lib/badline/drive1541/orbit.rb +81 -0
- data/lib/badline/drive1541/rotation.rb +10 -0
- data/lib/badline/drive1541/serial_port.rb +10 -0
- data/lib/badline/drive1541/sleep.rb +114 -0
- data/lib/badline/drive1541.rb +52 -2
- data/lib/badline/gui/application.rb +41 -14
- data/lib/badline/gui/gamepads.rb +7 -0
- data/lib/badline/gui/snapshots.rb +67 -0
- data/lib/badline/gui.rb +1 -0
- data/lib/badline/iec_bus.rb +16 -0
- data/lib/badline/kernal_trap/channel.rb +27 -0
- data/lib/badline/kernal_trap/drive/channels.rb +25 -0
- data/lib/badline/kernal_trap/drive/memory.rb +9 -0
- data/lib/badline/kernal_trap/drive/status.rb +8 -0
- data/lib/badline/kernal_trap/drive/write_file.rb +21 -0
- data/lib/badline/kernal_trap/drive.rb +19 -0
- data/lib/badline/kernal_trap/save.rb +8 -0
- data/lib/badline/kernal_trap/serial.rb +16 -0
- data/lib/badline/memory.rb +11 -0
- data/lib/badline/options.rb +3 -2
- data/lib/badline/ram_expansion/plus256k.rb +16 -0
- data/lib/badline/ram_expansion/plus60k.rb +12 -0
- data/lib/badline/ram_expansion/unexpanded.rb +6 -0
- data/lib/badline/reu/dma.rb +36 -0
- data/lib/badline/reu/ram.rb +15 -0
- data/lib/badline/reu/saved_state.rb +52 -0
- data/lib/badline/reu.rb +4 -0
- data/lib/badline/sdl/events.rb +2 -0
- data/lib/badline/sid/envelope/state.rb +68 -0
- data/lib/badline/sid/envelope.rb +7 -0
- data/lib/badline/sid/filter.rb +38 -0
- data/lib/badline/sid/voice.rb +10 -0
- data/lib/badline/sid/waveform/state.rb +66 -0
- data/lib/badline/sid/waveform.rb +2 -0
- data/lib/badline/sid.rb +34 -0
- data/lib/badline/snapshot/container.rb +117 -0
- data/lib/badline/snapshot/fields.rb +54 -0
- data/lib/badline/snapshot/image.rb +70 -0
- data/lib/badline/snapshot/machine_state.rb +77 -0
- data/lib/badline/snapshot/setup.rb +58 -0
- data/lib/badline/snapshot/state.rb +178 -0
- data/lib/badline/snapshot/vice/c64_mem.rb +53 -0
- data/lib/badline/snapshot/vice/cias.rb +89 -0
- data/lib/badline/snapshot/vice/cias_import.rb +70 -0
- data/lib/badline/snapshot/vice/main_cpu.rb +76 -0
- data/lib/badline/snapshot/vice/peripherals.rb +47 -0
- data/lib/badline/snapshot/vice/restore.rb +125 -0
- data/lib/badline/snapshot/vice/sid_extended.rb +96 -0
- data/lib/badline/snapshot/vice/sid_registers.rb +46 -0
- data/lib/badline/snapshot/vice/vicii.rb +119 -0
- data/lib/badline/snapshot/vice/vicii_import.rb +131 -0
- data/lib/badline/snapshot/vice.rb +74 -0
- data/lib/badline/snapshot.rb +52 -0
- data/lib/badline/storage/crt_file.rb +9 -3
- data/lib/badline/storage/d64_image.rb +2 -0
- data/lib/badline/storage/d71_image.rb +2 -0
- data/lib/badline/storage/d81_image.rb +2 -0
- data/lib/badline/storage/disk_image.rb +11 -2
- data/lib/badline/storage/host_directory.rb +5 -0
- data/lib/badline/storage/t64.rb +6 -0
- data/lib/badline/storage/tap.rb +10 -1
- data/lib/badline/storage/unavailable.rb +20 -0
- data/lib/badline/storage.rb +34 -0
- data/lib/badline/time_of_day.rb +56 -0
- data/lib/badline/version.rb +1 -1
- data/lib/badline/via/control_lines.rb +15 -0
- data/lib/badline/via/fast_forward.rb +15 -0
- data/lib/badline/via/interrupt_register.rb +9 -0
- data/lib/badline/via/shift_register.rb +13 -0
- data/lib/badline/via/state.rb +42 -0
- data/lib/badline/via/timer1.rb +19 -0
- data/lib/badline/via/timer2.rb +24 -0
- data/lib/badline/via.rb +2 -0
- data/lib/badline/vic/border_mask.rb +10 -0
- data/lib/badline/vic/collisions.rb +11 -0
- data/lib/badline/vic/color_patches.rb +11 -0
- data/lib/badline/vic/display_state.rb +29 -1
- data/lib/badline/vic/graphics_shifter.rb +26 -0
- data/lib/badline/vic/register_log.rb +18 -0
- data/lib/badline/vic/registers.rb +13 -2
- data/lib/badline/vic/sequencer.rb +12 -0
- data/lib/badline/vic/sequencer_state.rb +89 -0
- data/lib/badline/vic/sprite/internal_bus.rb +11 -0
- data/lib/badline/vic/sprite/saved_state.rb +64 -0
- data/lib/badline/vic/sprite.rb +2 -0
- data/lib/badline/vic/sprites.rb +3 -0
- data/lib/badline/vic/sprites_state.rb +57 -0
- data/lib/badline/vic.rb +94 -10
- data/lib/badline.rb +1 -0
- metadata +37 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b238dd5194dd5fc7783779eced6e4b718b73890e81d78566d12c26c08a379597
|
|
4
|
+
data.tar.gz: 4a47225b0d7e2a055674ce9f4f61cd2bc1064413a2c4c8b6bf228ed99f4dde07
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c636911838f2d65ae88c6b4e940085790c54573c2cc6a9d97450b68f2319eb374f3330e75243487bd7538b56d5cb695f2fe085739ead20db06eb3f24b9169c45
|
|
7
|
+
data.tar.gz: c48b8ecfe80b51a178a757246adba3ea267901db096ccb74c28dbc6482a9ff1f71193c26b4829e98744bcd6847f5fe3f073ff01275604d8383dd36dd91f50f69
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,29 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.6.0](https://github.com/elektronaut/badline/compare/v0.5.1...v0.6.0) (2026-09-29)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* save and restore the machine in VICE .vsf snapshots ([07c0a26](https://github.com/elektronaut/badline/commit/07c0a26d7e73595d5cd4a5bfa90b633ee01195be))
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Bug Fixes
|
|
12
|
+
|
|
13
|
+
* forget the drive's orbits after restoring it ([0238d81](https://github.com/elektronaut/badline/commit/0238d81ab12a0b0ff1e841661e10004ab3683cc1))
|
|
14
|
+
|
|
15
|
+
## [0.5.1](https://github.com/elektronaut/badline/compare/v0.5.0...v0.5.1) (2026-09-29)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
### Bug Fixes
|
|
19
|
+
|
|
20
|
+
* build the native badline with a Spinel that writes .d64 images through the true drive ([97f537a](https://github.com/elektronaut/badline/commit/97f537afe69b10ae044f83ccf4dd018402ebe1be))
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
### Performance Improvements
|
|
24
|
+
|
|
25
|
+
* sleep the idle true drive through its timer interrupts ([0482e43](https://github.com/elektronaut/badline/commit/0482e439cfb160ab69027b166b1059ed8fc9dfb3)), closes [#367](https://github.com/elektronaut/badline/issues/367)
|
|
26
|
+
|
|
3
27
|
## [0.5.0](https://github.com/elektronaut/badline/compare/v0.4.2...v0.5.0) (2026-09-29)
|
|
4
28
|
|
|
5
29
|
|
data/CLAUDE.md
CHANGED
|
@@ -48,6 +48,7 @@ requires only the namespace file.
|
|
|
48
48
|
| Wolfgang Lorenz suite | `bin/lorenz` | CPU, CIA, interrupts |
|
|
49
49
|
| VICE testbench | `bin/testbench <subtree>` | `VICII/`, `CIA/`, `interrupts/`, `CPU/`, cartridges with `--carts`, and the true 1541 drive with `--drive` |
|
|
50
50
|
| VICE SID testprogs | `bin/sidtests` | SID |
|
|
51
|
+
| Drive scenarios | `bin/drive_scenarios` | The true 1541 running the DOS ROM: save, format, the error channel, idle, write protect and autostart |
|
|
51
52
|
| CIA offline grids | `bundle exec rspec --tag slow spec/badline/cia` | CIA timers and shift register, against a bare CIA in about 2 min |
|
|
52
53
|
|
|
53
54
|
The CIA offline grids replay Lorenz's `cia1ta` and `cia1tb` sweeps (about
|
|
@@ -156,29 +157,29 @@ the rows your change can't reach tell you nothing about it.
|
|
|
156
157
|
`ruby --yjit bin/lorenz [image] [start_test] [max_cycles]` (resumes the
|
|
157
158
|
chain at a named test, and `--stop-after NAME` ends it once that test is
|
|
158
159
|
done). These take seconds to minutes
|
|
159
|
-
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
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
|
|
162
|
+
`test/baselines/`, and every job is a required check. A row that moved
|
|
163
|
+
fails it. Re-record only those rows, in the same change:
|
|
164
|
+
`rake "regression:record:<suite>[filter,...]"` (quote it, because zsh
|
|
165
|
+
globs the brackets). It runs only the matching tests on CRuby and
|
|
166
|
+
splices their rows into the baseline, and every other row keeps its
|
|
167
|
+
verdict. CI runs the same rows on Spinel, so the two have to agree.
|
|
163
168
|
`lorenz` chains itself, so it takes a stretch of the chain instead:
|
|
164
169
|
`rake "regression:record:lorenz[first,last]"` resumes just ahead of
|
|
165
170
|
`first`, stops after `last` and leaves the `(suite)` row alone.
|
|
166
171
|
`[first,(suite)]` runs on to the end of the chain and records the
|
|
167
|
-
`(suite)` row too.
|
|
168
|
-
|
|
169
|
-
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
- The one exception is a change whose reach you can't bound to a set of
|
|
179
|
-
filters, such as reordering `Computer#cycle!` or changing the LOAD trap
|
|
180
|
-
every suite loads through. Ask before running a full suite for it, and
|
|
181
|
-
don't start one on your own judgement
|
|
172
|
+
`(suite)` row too. Explain every moved row in the pull request. Never
|
|
173
|
+
re-record just to make a diff go away
|
|
174
|
+
- For a change whose reach you can't bound to a set of filters, such as
|
|
175
|
+
reordering `Computer#cycle!` or changing the LOAD trap every suite loads
|
|
176
|
+
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
|
+
- The Regression workflow runs the suites on CRuby, started by hand from
|
|
181
|
+
the Actions tab, for a person checking that CRuby and Spinel agree.
|
|
182
|
+
Agents don't start it
|
|
182
183
|
|
|
183
184
|
Pick the filters from the suites your change can move: VIC → `testbench`,
|
|
184
185
|
plus `testbench-vicii-new` (`bin/testbench --vicii-new`, the 8565) for
|
|
@@ -190,10 +191,9 @@ CPU, interrupts or timing → the matching `testbench-*` suite, plus
|
|
|
190
191
|
the 6526A) for anything the interrupt register or the CIA model reaches; cartridge mappers, banking or power-on state →
|
|
191
192
|
`testbench-carts`, plus `testbench-expansions` for banking; GEO-RAM, +60K,
|
|
192
193
|
+256K, the REU or the VIC's BA line the REU follows → `testbench-expansions`; the 1541 drive, VIA or IEC bus →
|
|
193
|
-
`testbench-drive` (`bin/testbench --drive`); SID → `sid`, plus `sid-8580` for anything the 8580
|
|
194
|
-
model reaches (`bin/sidtests --sid 8580`). Lorenz
|
|
195
|
-
|
|
196
|
-
exception is code whose rule in `doc/pinned-behaviour.md` names Lorenz
|
|
194
|
+
`testbench-drive` (`bin/testbench --drive`) and `drive-scenarios` (`bin/drive_scenarios <filter>`); SID → `sid`, plus `sid-8580` for anything the 8580
|
|
195
|
+
model reaches (`bin/sidtests --sid 8580`). Leave the Lorenz chain to CI,
|
|
196
|
+
unless your code has a rule in `doc/pinned-behaviour.md` that names Lorenz
|
|
197
197
|
tests: the interrupt polling, CPU port and CIA timer rules. Run just the
|
|
198
198
|
tests that rule names, one at a time, with
|
|
199
199
|
`ruby --yjit bin/lorenz --resume <test> --stop-after <test>`. A stretch
|
data/CONTRIBUTING.md
CHANGED
|
@@ -85,6 +85,11 @@ bundle exec rubocop
|
|
|
85
85
|
## Tests and commits
|
|
86
86
|
|
|
87
87
|
- Add tests for any behavior you change.
|
|
88
|
+
- CI runs rubocop, the specs and every regression suite on each pull
|
|
89
|
+
request, and fails on any suite row that differs from its baseline in
|
|
90
|
+
`test/baselines/`. When a change moves rows on purpose, re-record just
|
|
91
|
+
those rows as [test/baselines/README.md](test/baselines/README.md)
|
|
92
|
+
describes, and say why in the pull request.
|
|
88
93
|
- Write commit messages using
|
|
89
94
|
[Conventional Commits](https://www.conventionalcommits.org). The
|
|
90
95
|
changelog and releases are generated from them, so the `feat:` and
|
data/README.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
[](https://rubygems.org/gems/badline)
|
|
2
|
-
[](https://github.com/elektronaut/badline/actions/workflows/ci.yml)
|
|
3
3
|
|
|
4
4
|
# Badline
|
|
5
5
|
|
|
@@ -57,6 +57,7 @@ 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
59
|
badline tune.sid # Play a SID tune
|
|
60
|
+
badline game.vsf # Restore a snapshot, from badline or VICE
|
|
60
61
|
badline ~/c64 # Mount a directory as device 8
|
|
61
62
|
```
|
|
62
63
|
|
|
@@ -107,6 +108,7 @@ slows the emulation down.
|
|
|
107
108
|
| `.tap` | Inserted in the datasette with PLAY pressed, then `LOAD` and `RUN`. It loads at the speed of a real tape |
|
|
108
109
|
| `.crt` | The hardware types listed under [Cartridges](#whats-emulated). Other types are rejected |
|
|
109
110
|
| `.sid` | PSID and RSID tunes, started through a small driver after boot |
|
|
111
|
+
| `.vsf` | A snapshot of a running machine, badline's own or one VICE's x64sc saved. See [Snapshots](#snapshots) |
|
|
110
112
|
| 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` |
|
|
111
113
|
|
|
112
114
|
Apart from a `.g64`, which only a true drive can read, there is no 1541
|
|
@@ -139,6 +141,62 @@ and takes out a disk mounted through the traps, so `LOAD` and `SAVE`
|
|
|
139
141
|
reach the 1541 too. With the 1541 in device 8, a `.d64` goes in its
|
|
140
142
|
drive as well.
|
|
141
143
|
|
|
144
|
+
## Snapshots
|
|
145
|
+
|
|
146
|
+
In the window, `F11` saves the whole machine to a new
|
|
147
|
+
`badline-<date>-<time>.vsf` in the working directory, and `F12` goes
|
|
148
|
+
back to the snapshot last saved or opened, in a new machine built as the
|
|
149
|
+
saved one was. Both executables do this, and both open a `.vsf` given as
|
|
150
|
+
the media. A snapshot that fails to open leaves the machine running as
|
|
151
|
+
it was. From Ruby, `computer.save_snapshot(path)` saves,
|
|
152
|
+
`computer.restore_snapshot(path)` takes a machine back to a snapshot,
|
|
153
|
+
and `Badline::Snapshot.load(path)` builds a new machine as the saved one
|
|
154
|
+
was built and restores it. `computer.snapshot` and
|
|
155
|
+
`computer.restore(state)` do the same in memory, without a file. A
|
|
156
|
+
restore that fails leaves the machine as it was.
|
|
157
|
+
|
|
158
|
+
A snapshot holds the machine as it was on the cycle it was saved: the
|
|
159
|
+
chips down to the instruction step and the pixel pipeline, the RAM and
|
|
160
|
+
any +60K or +256K expansion, the cartridge with its RAM and flash, a
|
|
161
|
+
GEO-RAM, a true 1541 with its RAM, its VIAs and the disk under the head,
|
|
162
|
+
a disk or directory mounted through the traps with its open channels,
|
|
163
|
+
and the tape with its place on it. A restored machine runs on exactly
|
|
164
|
+
as the saved one would have. A directory, a tape and a true drive's
|
|
165
|
+
disk image open again from their paths when the snapshot is restored,
|
|
166
|
+
the disk with its tracks as the drive last saw them. A disk image mounted through the traps comes back
|
|
167
|
+
with its contents from the snapshot. What the host holds stays the host's: the keyboard, the
|
|
168
|
+
joysticks, the mouse and paddles, sound, and blocks given to `on_init`
|
|
169
|
+
that hadn't run yet, which a restore reports. A snapshot only restores
|
|
170
|
+
in the badline version that wrote it, into a machine with the same
|
|
171
|
+
chip models, RAM expansion and REU. `computer.snapshot` holds an REU's
|
|
172
|
+
RAM, registers and transfer too, but a machine with an REU doesn't save
|
|
173
|
+
to a file yet, as badline doesn't write VICE's REU module.
|
|
174
|
+
|
|
175
|
+
Snapshots use VICE's `.vsf` format. badline writes VICE's modules for
|
|
176
|
+
the CPU, RAM and CPU port, both CIAs, the SID and the VIC-II, plus the
|
|
177
|
+
ones x64sc needs to open the file, with nothing attached to the
|
|
178
|
+
cartridge, tape or user ports and no true drive. A `BADLINE` module,
|
|
179
|
+
which VICE skips, holds the whole machine, and badline restores its own
|
|
180
|
+
snapshots from it.
|
|
181
|
+
|
|
182
|
+
x64sc 3.10 opens badline's snapshots, taken at the end of the
|
|
183
|
+
instruction the CPU was in, as long as its VIC-II model matches
|
|
184
|
+
(`-model c64` for the default machine). It keeps its own drives and
|
|
185
|
+
leaves out the cartridge, the expansions and the tape. Snapshots x64sc
|
|
186
|
+
saves open in badline through the same modules: the CPU at its
|
|
187
|
+
instruction boundary, RAM, the CPU port, the CIAs' registers, timers and
|
|
188
|
+
clocks, the SID's registers and reSID voice state, and the VIC-II's
|
|
189
|
+
registers, beam position, counters and colour RAM. The VIC-II's pixel
|
|
190
|
+
pipeline starts empty. badline reads the modules x64sc 3.7 to 3.10
|
|
191
|
+
write, and VICE's development versions' `MAINC64CPU` and `VIC-IISC`. A module version
|
|
192
|
+
it doesn't know is left out, or fails the restore for the CPU and RAM.
|
|
193
|
+
It reports the modules it leaves out, such as the 1541 drives, the
|
|
194
|
+
cartridge, the datasette and the keyboard, and carries on without them.
|
|
195
|
+
An NTSC snapshot fails, as badline runs PAL only. Restored into a
|
|
196
|
+
running machine, a VICE snapshot fails for a machine built another way,
|
|
197
|
+
such as a C64C's snapshot in a C64, and otherwise takes the cartridge
|
|
198
|
+
out and switches the machine off and on.
|
|
199
|
+
|
|
142
200
|
## Playing and rendering SID tunes
|
|
143
201
|
|
|
144
202
|
`badline-ruby --headless` plays a `.sid` tune on the host's audio
|
data/doc/pinned-behaviour.md
CHANGED
|
@@ -1203,8 +1203,10 @@ and each was knocked out: removing it fails the rows named.
|
|
|
1203
1203
|
- Spec guard: *steps in from the stop with the first phase after the
|
|
1204
1204
|
bump* in
|
|
1205
1205
|
[`mechanism_spec.rb`](../spec/badline/drive1541/mechanism_spec.rb),
|
|
1206
|
-
and
|
|
1207
|
-
|
|
1206
|
+
and the `format/name-and-id`, `format/bam-free` and
|
|
1207
|
+
`format/trap-readable` rows of the `drive-scenarios` suite
|
|
1208
|
+
([`drive_scenarios.rb`](../test/drive_scenarios.rb)), which read the
|
|
1209
|
+
formatted disk back from its image.
|
|
1208
1210
|
- The disk turns at 300 rpm whatever bit rate VIA 2's PB5-6 select: a
|
|
1209
1211
|
turn is 200,000 drive cycles over every track and over a half track
|
|
1210
1212
|
without data. Each track's bits pass under the head at the rate they
|
data/exe/badline-ruby
CHANGED
|
@@ -55,7 +55,8 @@ begin
|
|
|
55
55
|
disk: { read_only: options.read_only? })
|
|
56
56
|
rescue Badline::Media::TrueDrive::Error, Badline::Storage::SIDFile::FormatError, Badline::Storage::T64::FormatError,
|
|
57
57
|
Badline::Storage::TAP::FormatError, Badline::Storage::CRTFile::FormatError,
|
|
58
|
-
Badline::Storage::G64Image::FormatError, Badline::Cartridge::UnsupportedTypeError
|
|
58
|
+
Badline::Storage::G64Image::FormatError, Badline::Cartridge::UnsupportedTypeError,
|
|
59
|
+
Badline::Snapshot::FormatError => e
|
|
59
60
|
warn "badline-ruby: #{options.media_path}: #{e.message}"
|
|
60
61
|
exit 1
|
|
61
62
|
end
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
class AddressBus
|
|
5
|
+
# The bus's state for a snapshot: the CPU port, the RAM, colour RAM and
|
|
6
|
+
# a RAM expansion, the cartridge, the datasette and the chips.
|
|
7
|
+
module SavedState
|
|
8
|
+
# The RAM expansion fitted, or an Unexpanded stand-in.
|
|
9
|
+
attr_reader :ram_expansion
|
|
10
|
+
|
|
11
|
+
# Takes the cartridge out of the expansion port, as a restore does for
|
|
12
|
+
# a state without one.
|
|
13
|
+
def detach_cartridge
|
|
14
|
+
@cartridge = nil
|
|
15
|
+
update_overlays!
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# The 6510 port's direction and output registers, and the charge left
|
|
19
|
+
# on its floating bits.
|
|
20
|
+
def port_state = [@port_ddr, @port_out, @port_floating]
|
|
21
|
+
|
|
22
|
+
# Sets the port as port_state reads it, as a snapshot restores it,
|
|
23
|
+
# leaving the RAM under $00 and $01 alone.
|
|
24
|
+
def restore_port(ddr, out, floating)
|
|
25
|
+
@port_ddr = ddr
|
|
26
|
+
@port_out = out
|
|
27
|
+
@port_floating = floating
|
|
28
|
+
update_port!
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# The machine's memory and everything on the bus but the keyboard, the
|
|
32
|
+
# joysticks and the pot devices, which the host holds. A cartridge is
|
|
33
|
+
# already in the port, built from its setup (Computer#load_state).
|
|
34
|
+
def save_state(out)
|
|
35
|
+
out.marker("BUS")
|
|
36
|
+
out.int(@port_ddr).int(@port_out).int(@port_floating)
|
|
37
|
+
@ram.save_state(out)
|
|
38
|
+
@color_ram.save_state(out)
|
|
39
|
+
@ram_expansion.save_state(out)
|
|
40
|
+
@cartridge&.save_state(out)
|
|
41
|
+
@datasette.save_state(out)
|
|
42
|
+
@vic.save_state(out)
|
|
43
|
+
@cia1.save_state(out)
|
|
44
|
+
@cia2.save_state(out)
|
|
45
|
+
@sid.save_state(out)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def load_state(input)
|
|
49
|
+
input.marker("BUS")
|
|
50
|
+
ddr = input.int
|
|
51
|
+
port_out = input.int
|
|
52
|
+
floating = input.int
|
|
53
|
+
@ram.load_state(input)
|
|
54
|
+
@color_ram.load_state(input)
|
|
55
|
+
@ram_expansion.load_state(input)
|
|
56
|
+
@cartridge&.load_state(input)
|
|
57
|
+
@datasette.load_state(input)
|
|
58
|
+
@port_ddr = ddr
|
|
59
|
+
@port_out = port_out
|
|
60
|
+
@port_floating = floating
|
|
61
|
+
@io_port.value = port_value
|
|
62
|
+
update_overlays!
|
|
63
|
+
@vic.load_state(input)
|
|
64
|
+
@cia1.load_state(input)
|
|
65
|
+
@cia2.load_state(input)
|
|
66
|
+
@sid.load_state(input)
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
include SavedState
|
|
71
|
+
end
|
|
72
|
+
end
|
data/lib/badline/address_bus.rb
CHANGED
data/lib/badline/audio/stream.rb
CHANGED
|
@@ -64,6 +64,21 @@ module Badline
|
|
|
64
64
|
|
|
65
65
|
private
|
|
66
66
|
|
|
67
|
+
def save_mapper(out)
|
|
68
|
+
save_windows(out, windows)
|
|
69
|
+
out.int(window_index(@rom, windows)).boolean(@active).boolean(@io2_ram).blob(@ram_data)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def load_mapper(input)
|
|
73
|
+
load_windows(input, windows)
|
|
74
|
+
@rom = window_at(input.int, windows)
|
|
75
|
+
@active = input.boolean?
|
|
76
|
+
@io2_ram = input.boolean?
|
|
77
|
+
input.blob_into(@ram_data)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def windows = [@through, @isolated, @contended, *@rom_banks, EMPTY_BANK]
|
|
81
|
+
|
|
67
82
|
def io2_peek(addr)
|
|
68
83
|
@io2_ram ? @ram_data[IO2_PAGE | (addr & 0xff)] : @rom.peek(addr)
|
|
69
84
|
end
|
|
@@ -33,6 +33,14 @@ module Badline
|
|
|
33
33
|
|
|
34
34
|
private
|
|
35
35
|
|
|
36
|
+
def save_mapper(out)
|
|
37
|
+
save_windows(out, [*@roml_banks, *@romh_banks, EMPTY_BANK])
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def load_mapper(input)
|
|
41
|
+
load_windows(input, [*@roml_banks, *@romh_banks, EMPTY_BANK])
|
|
42
|
+
end
|
|
43
|
+
|
|
36
44
|
def select(number, mode)
|
|
37
45
|
@roml = bank(@roml_banks, number)
|
|
38
46
|
@romh = bank(@romh_banks, number)
|
|
@@ -24,6 +24,14 @@ module Badline
|
|
|
24
24
|
|
|
25
25
|
private
|
|
26
26
|
|
|
27
|
+
def save_mapper(out)
|
|
28
|
+
save_windows(out, [*@banks, EMPTY_BANK])
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def load_mapper(input)
|
|
32
|
+
load_windows(input, [*@banks, EMPTY_BANK])
|
|
33
|
+
end
|
|
34
|
+
|
|
27
35
|
def install_chips(chips)
|
|
28
36
|
@banks = banks_from(chips).first
|
|
29
37
|
self.mode = :rom8k
|
|
@@ -78,9 +78,28 @@ module Badline
|
|
|
78
78
|
private
|
|
79
79
|
|
|
80
80
|
def select_bank
|
|
81
|
+
map_bank
|
|
82
|
+
changed!
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def map_bank
|
|
81
86
|
@roml = @low_flash.window(@bank * BANK_SIZE)
|
|
82
87
|
@romh = @high_flash.window(@bank * BANK_SIZE)
|
|
83
|
-
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def save_mapper(out)
|
|
91
|
+
out.int(@bank).int(@control).blob(@io_ram)
|
|
92
|
+
@low_flash.save_state(out)
|
|
93
|
+
@high_flash.save_state(out)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def load_mapper(input)
|
|
97
|
+
@bank = input.int
|
|
98
|
+
@control = input.int
|
|
99
|
+
input.blob_into(@io_ram)
|
|
100
|
+
@low_flash.load_state(input)
|
|
101
|
+
@high_flash.load_state(input)
|
|
102
|
+
map_bank
|
|
84
103
|
end
|
|
85
104
|
|
|
86
105
|
def apply_control
|
|
@@ -62,6 +62,18 @@ module Badline
|
|
|
62
62
|
|
|
63
63
|
private
|
|
64
64
|
|
|
65
|
+
def save_mapper(out)
|
|
66
|
+
save_windows(out, [*@roml_banks, *@romh_banks, EMPTY_BANK])
|
|
67
|
+
out.boolean(@register_visible).boolean(@phi1_ultimax)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def load_mapper(input)
|
|
71
|
+
load_windows(input, [*@roml_banks, *@romh_banks, EMPTY_BANK])
|
|
72
|
+
@io_bank = @roml
|
|
73
|
+
@register_visible = input.boolean?
|
|
74
|
+
@phi1_ultimax = input.boolean?
|
|
75
|
+
end
|
|
76
|
+
|
|
65
77
|
def select(number)
|
|
66
78
|
@io_bank = @roml = bank(@roml_banks, number)
|
|
67
79
|
@romh = bank(@romh_banks, number)
|
|
@@ -2,6 +2,37 @@
|
|
|
2
2
|
|
|
3
3
|
module Badline
|
|
4
4
|
class Cartridge
|
|
5
|
+
# A Flash chip's state, for a snapshot.
|
|
6
|
+
module FlashState
|
|
7
|
+
STATES = %i[read program_setup autoselect programming erase_window erasing suspended chip_erase
|
|
8
|
+
program_error].freeze
|
|
9
|
+
|
|
10
|
+
# The array, the command state, a timed operation's end on the
|
|
11
|
+
# machine's clock, and the windows handed out so far, which load_state
|
|
12
|
+
# builds anew.
|
|
13
|
+
def save_state(out)
|
|
14
|
+
out.blob(@data).int(STATES.index(@state)).int(STATES.index(@base_state)).int(@cycle).int(@toggle)
|
|
15
|
+
out.optional_int(@done_at).optional_int(@programmed).optional_int(@remaining)
|
|
16
|
+
out.boolean(!@erasing_sectors.nil?)
|
|
17
|
+
out.ints(@erasing_sectors) if @erasing_sectors
|
|
18
|
+
out.ints(@windows.keys).ints(@status_windows.keys)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def load_state(input)
|
|
22
|
+
input.blob_into(@data)
|
|
23
|
+
@state = STATES.fetch(input.int).to_sym
|
|
24
|
+
@base_state = STATES.fetch(input.int).to_sym
|
|
25
|
+
@cycle = input.int
|
|
26
|
+
@toggle = input.int
|
|
27
|
+
@done_at = input.optional_int
|
|
28
|
+
@programmed = input.optional_int
|
|
29
|
+
@remaining = input.optional_int
|
|
30
|
+
@erasing_sectors = nil
|
|
31
|
+
@erasing_sectors = input.ints if input.boolean?
|
|
32
|
+
rebuild_windows(input.ints, input.ints)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
5
36
|
# An AMD 29F0x0 flash chip, driven through the JEDEC command set in the
|
|
6
37
|
# AMD datasheets. Each command opens with two unlock cycles, $AA to the
|
|
7
38
|
# first unlock address and $55 to the second, and a third write picks
|
|
@@ -66,6 +97,8 @@ module Badline
|
|
|
66
97
|
alias [] peek
|
|
67
98
|
end
|
|
68
99
|
|
|
100
|
+
include FlashState
|
|
101
|
+
|
|
69
102
|
attr_reader :data, :state
|
|
70
103
|
attr_writer :clock
|
|
71
104
|
|
|
@@ -95,6 +128,14 @@ module Badline
|
|
|
95
128
|
end
|
|
96
129
|
end
|
|
97
130
|
|
|
131
|
+
# Hands out new windows at the bases given, as #window would have.
|
|
132
|
+
def rebuild_windows(bases, status_bases)
|
|
133
|
+
@windows = {}
|
|
134
|
+
@status_windows = {}
|
|
135
|
+
bases.each { |base| @windows[base] = Window.new(self, base) }
|
|
136
|
+
status_bases.each { |base| @status_windows[base] = StatusWindow.new(self, base) }
|
|
137
|
+
end
|
|
138
|
+
|
|
98
139
|
def array_mode?
|
|
99
140
|
@state == :read || @state == :program_setup
|
|
100
141
|
end
|
|
@@ -28,6 +28,14 @@ module Badline
|
|
|
28
28
|
|
|
29
29
|
private
|
|
30
30
|
|
|
31
|
+
def save_mapper(out)
|
|
32
|
+
save_windows(out, [*@banks, EMPTY_BANK])
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def load_mapper(input)
|
|
36
|
+
load_windows(input, [*@banks, EMPTY_BANK])
|
|
37
|
+
end
|
|
38
|
+
|
|
31
39
|
def install_chips(chips)
|
|
32
40
|
@banks = []
|
|
33
41
|
chips.each { |chip| @banks[self.class.bank_number(chip.bank)] = rom_bank(chip.data) }
|
|
@@ -47,6 +47,18 @@ module Badline
|
|
|
47
47
|
changed!
|
|
48
48
|
end
|
|
49
49
|
|
|
50
|
+
def save_mapper(out)
|
|
51
|
+
out.int(@bank).boolean(@flash_writes)
|
|
52
|
+
@flash.save_state(out)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def load_mapper(input)
|
|
56
|
+
@bank = input.int
|
|
57
|
+
@flash_writes = input.boolean?
|
|
58
|
+
@flash.load_state(input)
|
|
59
|
+
@roml = @flash.window(@bank * BANK_SIZE)
|
|
60
|
+
end
|
|
61
|
+
|
|
50
62
|
def install_chips(chips)
|
|
51
63
|
data = Array.new(BANKS * BANK_SIZE, 0xff)
|
|
52
64
|
chips.each do |chip|
|
|
@@ -25,6 +25,14 @@ module Badline
|
|
|
25
25
|
|
|
26
26
|
private
|
|
27
27
|
|
|
28
|
+
def save_mapper(out)
|
|
29
|
+
save_windows(out, [*@banks, EMPTY_BANK])
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def load_mapper(input)
|
|
33
|
+
load_windows(input, [*@banks, EMPTY_BANK])
|
|
34
|
+
end
|
|
35
|
+
|
|
28
36
|
def select(addr)
|
|
29
37
|
@roml = bank(@banks, addr & 0x3f)
|
|
30
38
|
changed!
|
|
@@ -52,8 +52,24 @@ module Badline
|
|
|
52
52
|
@block = @page = @window = 0
|
|
53
53
|
end
|
|
54
54
|
|
|
55
|
+
# A GEO-RAM is built from its size alone.
|
|
56
|
+
def save_setup(out)
|
|
57
|
+
out.int(GEO_RAM_SETUP).int(@size)
|
|
58
|
+
end
|
|
59
|
+
|
|
55
60
|
private
|
|
56
61
|
|
|
62
|
+
def save_mapper(out)
|
|
63
|
+
out.blob(@ram_data).int(@block).int(@page).int(@window)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def load_mapper(input)
|
|
67
|
+
input.blob_into(@ram_data)
|
|
68
|
+
@block = input.int
|
|
69
|
+
@page = input.int
|
|
70
|
+
@window = input.int
|
|
71
|
+
end
|
|
72
|
+
|
|
57
73
|
def install_chips(_chips)
|
|
58
74
|
@ram_data = Array.new(@size * 1024, 0)
|
|
59
75
|
reset
|
|
@@ -20,6 +20,14 @@ module Badline
|
|
|
20
20
|
|
|
21
21
|
private
|
|
22
22
|
|
|
23
|
+
def save_mapper(out)
|
|
24
|
+
save_windows(out, [*@banks, EMPTY_BANK])
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def load_mapper(input)
|
|
28
|
+
load_windows(input, [*@banks, EMPTY_BANK])
|
|
29
|
+
end
|
|
30
|
+
|
|
23
31
|
def install_chips(chips)
|
|
24
32
|
@banks = banks_from(chips).first
|
|
25
33
|
@bank_mask = (bank_mask(@banks) | 0x03) & 0x7f
|
|
@@ -19,6 +19,14 @@ module Badline
|
|
|
19
19
|
|
|
20
20
|
private
|
|
21
21
|
|
|
22
|
+
def save_mapper(out)
|
|
23
|
+
save_windows(out, [*@banks, EMPTY_BANK])
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def load_mapper(input)
|
|
27
|
+
load_windows(input, [*@banks, EMPTY_BANK])
|
|
28
|
+
end
|
|
29
|
+
|
|
22
30
|
# In 16K mode the selected bank shows through ROMH as well.
|
|
23
31
|
def select_bank(number)
|
|
24
32
|
@roml = bank(@banks, number)
|
|
@@ -29,6 +29,18 @@ module Badline
|
|
|
29
29
|
|
|
30
30
|
private
|
|
31
31
|
|
|
32
|
+
def save_mapper(out)
|
|
33
|
+
save_windows(out, windows)
|
|
34
|
+
@ram_banks.each { |ram| out.blob(ram.data) }
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def load_mapper(input)
|
|
38
|
+
load_windows(input, windows)
|
|
39
|
+
@ram_banks.each { |ram| input.blob_into(ram.data) }
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def windows = [*@roml_banks, *@romh_banks, *@ram_banks, @open_bus || EMPTY_BANK, EMPTY_BANK]
|
|
43
|
+
|
|
32
44
|
def select(value)
|
|
33
45
|
half = (value >> 1) & 0x01
|
|
34
46
|
chip = (value >> 2) & 0x03
|