badline 0.4.2 → 0.5.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 +25 -0
- data/CLAUDE.md +10 -5
- data/README.md +54 -23
- data/doc/pinned-behaviour.md +221 -0
- data/exe/badline-ruby +7 -4
- data/lib/badline/address_bus/ultimax_pages.rb +38 -0
- data/lib/badline/address_bus.rb +19 -29
- data/lib/badline/audio/jukebox.rb +6 -4
- data/lib/badline/audio/machine_player.rb +10 -6
- data/lib/badline/cartridge/kcs_power.rb +3 -3
- data/lib/badline/cia.rb +5 -1
- data/lib/badline/computer.rb +86 -7
- data/lib/badline/cpu.rb +39 -0
- data/lib/badline/drive1541/bus.rb +134 -0
- data/lib/badline/drive1541/disk.rb +256 -0
- data/lib/badline/drive1541/disk_via.rb +33 -0
- data/lib/badline/drive1541/gcr.rb +57 -0
- data/lib/badline/drive1541/idle.rb +184 -0
- data/lib/badline/drive1541/mechanism.rb +277 -0
- data/lib/badline/drive1541/rotation.rb +111 -0
- data/lib/badline/drive1541/sector_reader.rb +76 -0
- data/lib/badline/drive1541/serial_port.rb +67 -0
- data/lib/badline/drive1541/track.rb +98 -0
- data/lib/badline/drive1541.rb +218 -0
- data/lib/badline/gui/application.rb +15 -3
- data/lib/badline/gui/drive_led_pane.rb +29 -0
- data/lib/badline/gui.rb +1 -0
- data/lib/badline/iec_bus.rb +93 -0
- data/lib/badline/kernal_trap/channel.rb +9 -1
- data/lib/badline/kernal_trap/serial.rb +9 -3
- data/lib/badline/media/true_drive.rb +54 -0
- data/lib/badline/media.rb +22 -1
- data/lib/badline/options.rb +24 -4
- data/lib/badline/region.rb +35 -10
- data/lib/badline/reu/dma.rb +228 -0
- data/lib/badline/reu/ram.rb +65 -0
- data/lib/badline/reu/trigger.rb +32 -0
- data/lib/badline/reu.rb +276 -0
- data/lib/badline/roms/README +10 -0
- data/lib/badline/roms/dos1541.rom +0 -0
- data/lib/badline/sdl/functions.rb +1 -0
- data/lib/badline/storage/d64_image.rb +18 -9
- data/lib/badline/storage/d81_image.rb +1 -0
- data/lib/badline/storage/disk_image/catalog.rb +52 -0
- data/lib/badline/storage/disk_image/writing.rb +16 -0
- data/lib/badline/storage/disk_image.rb +2 -0
- data/lib/badline/storage/g64_image.rb +222 -0
- data/lib/badline/storage/host_directory.rb +21 -1
- data/lib/badline/storage/listing.rb +118 -0
- data/lib/badline/storage/t64.rb +14 -0
- data/lib/badline/storage.rb +2 -0
- data/lib/badline/traps.rb +4 -0
- data/lib/badline/version.rb +1 -1
- data/lib/badline/via/control_lines.rb +3 -0
- data/lib/badline/via/fast_forward.rb +38 -0
- data/lib/badline/via/shift_register.rb +8 -3
- data/lib/badline/via/timer1.rb +30 -0
- data/lib/badline/via/timer2.rb +25 -0
- data/lib/badline/via.rb +2 -0
- data/lib/badline/vic/display_state.rb +7 -7
- data/lib/badline/vic/sprite/internal_bus.rb +11 -7
- data/lib/badline/vic/sprite/shifter.rb +10 -15
- data/lib/badline/vic/sprite/timing.rb +60 -0
- data/lib/badline/vic/sprite.rb +14 -12
- data/lib/badline/vic/sprites.rb +2 -2
- data/lib/badline/vic.rb +86 -39
- data/lib/badline.rb +3 -0
- metadata +26 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0c6abfa72fe3a5e5f8e88efe9d53e04b4e6eaeb355550ef5ac23c47c1a0916fc
|
|
4
|
+
data.tar.gz: e15d996a7121eb244ba6ad7a8870694389f59ab8139977b2cf7b9d344f35bbe7
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d6fdecfc6b65d6b67d296fafb91e1cbdad7920e0326f146c5bae4bfed08fdd3fad9b4b38007a7f3bdb6a859ff5f1428ff7d94f573e13b68dfdb2e31c5c15e9fe
|
|
7
|
+
data.tar.gz: 6843561cca4725f4d6a93cff613ea9b0a9db78d1d45aebee9c4ba7573c49205b0162c4431d4cb78b81e12ded0e84388866017e88b94e065790934e2aaa0c4e8e
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,30 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.5.0](https://github.com/elektronaut/badline/compare/v0.4.2...v0.5.0) (2026-09-29)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* add a --true-drive option ([3df4183](https://github.com/elektronaut/badline/commit/3df4183dec298ff6b386a6b6a1c12fb04ce3cca0))
|
|
9
|
+
* run the 1541 as its own machine ([9a4bafa](https://github.com/elektronaut/badline/commit/9a4bafab4c53fc7dc479a8c78527e8711d96c8cf))
|
|
10
|
+
* join the C64 and the 1541 over the IEC serial bus ([7671045](https://github.com/elektronaut/badline/commit/7671045bd47fdf363eb9a57faae3c463835c7544))
|
|
11
|
+
* read D64 disks through the 1541's GCR mechanism ([4c812fa](https://github.com/elektronaut/badline/commit/4c812fa25bad92c5c18219fc22c7a0ed804fe159))
|
|
12
|
+
* write and format disks through the 1541 ([22a2a8b](https://github.com/elektronaut/badline/commit/22a2a8be6b9f84b9fc4d7e214603165462c7ddef))
|
|
13
|
+
* read and write .g64 disk images ([884ac19](https://github.com/elektronaut/badline/commit/884ac19baeb83468444e0631c2fd5840bdffc327))
|
|
14
|
+
* take .g64 images in the native badline ([8d9ab92](https://github.com/elektronaut/badline/commit/8d9ab9285555eaa0681a3113290af872a79d9f0b))
|
|
15
|
+
* skip the 1541's cycles while it idles ([d8502cd](https://github.com/elektronaut/badline/commit/d8502cd589aed3ab7590cf2467b9ea57a76330fc))
|
|
16
|
+
* list a mounted disk with LOAD"$",8 through the KERNAL traps ([c634e18](https://github.com/elektronaut/badline/commit/c634e18e3aa8e1b5ba3096d9b6b4ec9d41afa77b)), closes [#324](https://github.com/elektronaut/badline/issues/324)
|
|
17
|
+
* emulate the REU and run the testbench REU rows ([3d5d69e](https://github.com/elektronaut/badline/commit/3d5d69e7570b9961bfd823adf00c2924c65d45e9)), closes [#232](https://github.com/elektronaut/badline/issues/232)
|
|
18
|
+
* run NTSC machines and the testbench's NTSC rows ([e01ebf1](https://github.com/elektronaut/badline/commit/e01ebf17a03d606ff4a71afdb89506af962300e6)), closes [#306](https://github.com/elektronaut/badline/issues/306)
|
|
19
|
+
* power RAM on in a $00/$ff pattern ([59822c3](https://github.com/elektronaut/badline/commit/59822c3dc2956cd871a6e218cac213cfb38f6bad)), closes [#319](https://github.com/elektronaut/badline/issues/319)
|
|
20
|
+
* go on to the next subtune when one ends in the SID player ([0b2f2a2](https://github.com/elektronaut/badline/commit/0b2f2a209f3cb47d8117126f80217e4cc9030342)), closes [#359](https://github.com/elektronaut/badline/issues/359)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
### Bug Fixes
|
|
24
|
+
|
|
25
|
+
* keep the KCS Power Cartridge's RAM apart from the C64's ([c538b43](https://github.com/elektronaut/badline/commit/c538b43e6794cbeedcc4056fb71b38598525bfa9))
|
|
26
|
+
* write stdout as it goes, so errors appear in order ([42ccb02](https://github.com/elektronaut/badline/commit/42ccb026d6fe92c1ec5b67ab6f561084c5b154b2)), closes [#369](https://github.com/elektronaut/badline/issues/369)
|
|
27
|
+
|
|
3
28
|
## [0.4.2](https://github.com/elektronaut/badline/compare/v0.4.1...v0.4.2) (2026-09-28)
|
|
4
29
|
|
|
5
30
|
### Features
|
data/CLAUDE.md
CHANGED
|
@@ -17,6 +17,9 @@ live in subdirectories:
|
|
|
17
17
|
- **VIC-II**: `vic.rb` and `vic/`, covering the sequencer, sprites, graphics
|
|
18
18
|
modes, border and register timing
|
|
19
19
|
- **CIA**: `cia.rb` and `cia/` (timers, serial), plus `time_of_day.rb`
|
|
20
|
+
- **REU**: `reu.rb` and `reu/`, the RAM Expansion Unit's registers and
|
|
21
|
+
its DMA, which `Computer#cycle!` clocks in place of the CPU while it
|
|
22
|
+
holds the bus
|
|
20
23
|
- **SID**: `sid.rb` and `sid/`. `audio/` plays and renders tunes for
|
|
21
24
|
`badline-ruby --headless` and `--audio-out`
|
|
22
25
|
- **Media and host I/O**: `storage/` (disk, tape and cartridge image
|
|
@@ -43,7 +46,7 @@ requires only the namespace file.
|
|
|
43
46
|
| rspec (`spec/`) | `bundle exec rspec` | Unit behaviour, all of `lib/` |
|
|
44
47
|
| SingleStepTests 65x02 | `rake test` (100 sampled cases per opcode) | CPU, per-cycle bus traces |
|
|
45
48
|
| Wolfgang Lorenz suite | `bin/lorenz` | CPU, CIA, interrupts |
|
|
46
|
-
| VICE testbench | `bin/testbench <subtree>` | `VICII/`, `CIA/`, `interrupts/`, `CPU/`, and
|
|
49
|
+
| VICE testbench | `bin/testbench <subtree>` | `VICII/`, `CIA/`, `interrupts/`, `CPU/`, cartridges with `--carts`, and the true 1541 drive with `--drive` |
|
|
47
50
|
| VICE SID testprogs | `bin/sidtests` | SID |
|
|
48
51
|
| CIA offline grids | `bundle exec rspec --tag slow spec/badline/cia` | CIA timers and shift register, against a bare CIA in about 2 min |
|
|
49
52
|
|
|
@@ -136,7 +139,7 @@ overrides that, capped by the core count, but keep the default, because
|
|
|
136
139
|
other worktrees share the machine. Whole runs at 4 shards on an M-series
|
|
137
140
|
laptop take 15 min for `testbench` (`VICII/`), 11 for `testbench-cia`, 1 for
|
|
138
141
|
`testbench-interrupts`, 37 for `testbench-irqdma`, 11 for
|
|
139
|
-
`testbench-cpu`, 2 for `testbench-carts` and
|
|
142
|
+
`testbench-cpu`, 2 for `testbench-carts` and about 25 for `testbench-expansions`. `sid` takes about 12 min. `lorenz` chains itself and takes
|
|
140
143
|
about 2.5 h on CI whole. `rake regression:lorenz-1` to `lorenz-4` run it as
|
|
141
144
|
four stretches of about 40 min each, and they can run side by side.
|
|
142
145
|
`test/baselines/README.md` has the full table. A killed `bin/testbench` run
|
|
@@ -179,13 +182,15 @@ the rows your change can't reach tell you nothing about it.
|
|
|
179
182
|
|
|
180
183
|
Pick the filters from the suites your change can move: VIC → `testbench`,
|
|
181
184
|
plus `testbench-vicii-new` (`bin/testbench --vicii-new`, the 8565) for
|
|
182
|
-
anything the VIC model reaches
|
|
185
|
+
anything the VIC model reaches, and `testbench-ntsc` (`bin/testbench --ntsc`,
|
|
186
|
+
the 6567R8 and 6567R56A) for anything a region's timing reaches;
|
|
183
187
|
CPU, interrupts or timing → the matching `testbench-*` suite, plus
|
|
184
188
|
`rake test` for CPU; CIA → the slow CIA specs first, then the matching
|
|
185
189
|
`testbench-cia` rows, plus `testbench-cia-new` (`bin/testbench --cia-new`,
|
|
186
190
|
the 6526A) for anything the interrupt register or the CIA model reaches; cartridge mappers, banking or power-on state →
|
|
187
|
-
`testbench-carts`, plus `testbench-expansions` for banking; GEO-RAM, +60K
|
|
188
|
-
|
|
191
|
+
`testbench-carts`, plus `testbench-expansions` for banking; GEO-RAM, +60K,
|
|
192
|
+
+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
|
|
189
194
|
model reaches (`bin/sidtests --sid 8580`). Lorenz isn't a per-change check:
|
|
190
195
|
its full chain runs nightly, and the planner assigns any row it moves. The
|
|
191
196
|
exception is code whose rule in `doc/pinned-behaviour.md` names Lorenz
|
data/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
# Badline
|
|
5
5
|
|
|
6
6
|
Badline is a Commodore 64 emulator written in Ruby. It emulates a PAL
|
|
7
|
-
machine one clock cycle at a time, stepping the 6510, the VIC-II, both
|
|
7
|
+
or NTSC machine one clock cycle at a time, stepping the 6510, the VIC-II, both
|
|
8
8
|
CIAs and the SID together, so raster timing, bad lines and sprite DMA
|
|
9
9
|
are modelled at the cycle level.
|
|
10
10
|
|
|
@@ -69,7 +69,9 @@ from it. `--no-autostart` attaches the media and stops at `READY.`, so
|
|
|
69
69
|
you can type the `LOAD` yourself. `--read-only` mounts a disk image
|
|
70
70
|
write-protected, so the drive reports `26,WRITE PROTECT ON` for any
|
|
71
71
|
write and the image file stays as it was. `--song N` picks a subtune of a
|
|
72
|
-
`.sid` file
|
|
72
|
+
`.sid` file, `--sid 8580` fits the newer SID and `--reu 512` plugs in a
|
|
73
|
+
512K RAM Expansion Unit. `--ntsc` runs an NTSC C64, with the 6567R8
|
|
74
|
+
VIC-II, instead of a PAL one. `badline-ruby` also
|
|
73
75
|
takes `--disable-jit`, which runs without YJIT, otherwise switched on at
|
|
74
76
|
startup. `--help` lists the options.
|
|
75
77
|
|
|
@@ -100,27 +102,42 @@ slows the emulation down.
|
|
|
100
102
|
|--------|----------|
|
|
101
103
|
| `.prg`, `.p00` | Loaded into memory after boot. A program at the BASIC start (`$0801`) is `RUN`, anything else is left for you to `SYS` |
|
|
102
104
|
| `.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 |
|
|
105
|
+
| `.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 |
|
|
103
106
|
| `.t64` | Mounted read-only as device 8 and loaded like a disk image. The files load by name, and no tape is involved |
|
|
104
107
|
| `.tap` | Inserted in the datasette with PLAY pressed, then `LOAD` and `RUN`. It loads at the speed of a real tape |
|
|
105
108
|
| `.crt` | The hardware types listed under [Cartridges](#whats-emulated). Other types are rejected |
|
|
106
109
|
| `.sid` | PSID and RSID tunes, started through a small driver after boot |
|
|
107
110
|
| 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` |
|
|
108
111
|
|
|
109
|
-
|
|
110
|
-
`
|
|
111
|
-
|
|
112
|
-
`
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
112
|
+
Apart from a `.g64`, which only a true drive can read, there is no 1541
|
|
113
|
+
unless `--true-drive` asks for one. Device 8 works by trapping the
|
|
114
|
+
KERNAL's `LOAD` and `SAVE` routines and its serial bus primitives, so
|
|
115
|
+
files open by name through `OPEN` and `CHRIN` as well. `LOAD"$",8` lists
|
|
116
|
+
the directory of any medium mounted there, as a 1541 does. The command
|
|
117
|
+
channel answers `I`, `B-P` and `U1` block reads, which covers loaders
|
|
118
|
+
that read blocks directly. On a disk image it also takes `SAVE` (with
|
|
119
|
+
`@0:` to replace a file), files opened for writing or appending, `S` to
|
|
120
|
+
scratch, `U2` and `B-W` block writes, and `B-A` and `B-F`. Loaders that
|
|
121
|
+
upload their own code to the drive with `M-W` and `M-E`, and copy
|
|
122
|
+
protection that reads raw GCR, won't work there, but do on a true
|
|
123
|
+
drive.
|
|
124
|
+
|
|
125
|
+
`--true-drive` puts an emulated 1541 on device 8 instead, running its
|
|
126
|
+
own DOS ROM on its own 6502 and talking to the machine over the serial
|
|
127
|
+
bus. It reads `.d64` and `.g64` images, which it autostarts with the
|
|
128
|
+
same `LOAD"*",8,1` and `RUN`, but not `.d71`, `.d81` or `.t64` images
|
|
129
|
+
or directories. `--read-only` puts the disk in write-protected. Loading
|
|
130
|
+
runs at the speed of a real 1541, and the drive's red LED lights in the
|
|
131
|
+
bottom right corner of the border. Both executables take it.
|
|
118
132
|
|
|
119
133
|
`Badline::Media.insert_disk(computer, path)` swaps the disk image or
|
|
120
134
|
directory in device 8 while the machine runs, for software that asks for
|
|
121
135
|
another disk. It takes `read_only: true`, and `Badline::Media.attach`
|
|
122
136
|
takes `disk: { read_only: true }`, to mount a disk image write-protected,
|
|
123
|
-
as the test harnesses under `bin/` do.
|
|
137
|
+
as the test harnesses under `bin/` do. A `.g64` goes in the true 1541,
|
|
138
|
+
and takes out a disk mounted through the traps, so `LOAD` and `SAVE`
|
|
139
|
+
reach the 1541 too. With the 1541 in device 8, a `.d64` goes in its
|
|
140
|
+
drive as well.
|
|
124
141
|
|
|
125
142
|
## Playing and rendering SID tunes
|
|
126
143
|
|
|
@@ -139,16 +156,19 @@ badline-ruby --filter-chunk 1 tune.sid --audio-out out.wav # exact filter, slo
|
|
|
139
156
|
```
|
|
140
157
|
|
|
141
158
|
Both modes take the same options. The window's own, `--no-autostart`,
|
|
142
|
-
`--sound
|
|
143
|
-
subtune, counting from 1 as HVSC
|
|
144
|
-
|
|
145
|
-
offers, unless `--rate`
|
|
159
|
+
`--read-only`, `--sound`, `--true-drive`, `--reu`, `--ntsc` and
|
|
160
|
+
`--verbose`, don't apply to them. `--song` (or `-s`) picks the subtune, counting from 1 as HVSC
|
|
161
|
+
does, and defaults to the tune's own start song. Playback asks the
|
|
162
|
+
device for 44.1 kHz and takes whatever rate it offers, unless `--rate`
|
|
163
|
+
says otherwise. Ctrl-C stops it.
|
|
146
164
|
|
|
147
165
|
Played on a terminal, `--headless` shows the tune's name, author and
|
|
148
166
|
release, the song number and the time played against the song's
|
|
149
|
-
length.
|
|
150
|
-
|
|
151
|
-
|
|
167
|
+
length. When a song reaches its length it goes on to the next, and it
|
|
168
|
+
stops after the last. `n` or → skips to the next song, `p` or ← goes
|
|
169
|
+
back one, space pauses and `q` quits. `--no-tui`, or output that isn't a
|
|
170
|
+
terminal, gives plain progress output instead and plays just the one
|
|
171
|
+
song, as `--audio-out` renders just the one.
|
|
152
172
|
|
|
153
173
|
A `.sid` file doesn't store its length, so `badline-ruby` looks the
|
|
154
174
|
tune up by MD5 in HVSC's `Songlengths.md5`. It finds the database
|
|
@@ -236,6 +256,13 @@ while it's off.
|
|
|
236
256
|
`Badline::Computer.new(vic_model: :mos8565)` fits the C64C's 8565
|
|
237
257
|
instead, with its grey dots on colour register writes and its own
|
|
238
258
|
timing for mode splits, sprite multicolour splits and the light pen.
|
|
259
|
+
`Badline::Computer.new(region: Badline::Region::NTSC)` builds an NTSC
|
|
260
|
+
machine instead: the 6567R8's 65 cycles by 263 lines at 1,022,727 Hz,
|
|
261
|
+
with its later sprite fetches and its X counter, and TOD clocks on
|
|
262
|
+
60 Hz mains. `Badline::Region::NTSC_OLD` is the first NTSC C64s'
|
|
263
|
+
6567R56A, 64 cycles by 262 lines. The stock KERNAL tells them from PAL
|
|
264
|
+
by the raster, so every region boots the same ROMs. `--ntsc` picks the
|
|
265
|
+
6567R8.
|
|
239
266
|
- **CIA 1 and 2**: timers, time-of-day clocks with alarms, the serial
|
|
240
267
|
shift register, interrupts, the keyboard matrix with its ghost keys,
|
|
241
268
|
the control ports and the paddle multiplexer. The machine has the
|
|
@@ -246,6 +273,11 @@ while it's off.
|
|
|
246
273
|
and the RC network on the board that removes the DC offset from the
|
|
247
274
|
output. The machine has a 6581 unless a `.sid` tune asks for an 8580
|
|
248
275
|
in its header, and `--sid 6581` or `--sid 8580` overrides either.
|
|
276
|
+
- **REU**: the 1700, 1764 and 1750 RAM Expansion Units, and the bigger
|
|
277
|
+
units up to 16M built on the same REC chip, with DMA timed against the
|
|
278
|
+
VIC's bad lines and sprites. `--reu SIZE` plugs one in, and
|
|
279
|
+
`Badline::Computer.new(reu: 512)` does the same from Ruby. An
|
|
280
|
+
REU beside a cartridge loses I/O 2 to the cartridge.
|
|
249
281
|
- **Datasette**: `.tap` playback into CIA 1's FLAG line, with the motor
|
|
250
282
|
and sense lines on the 6510 port.
|
|
251
283
|
- **Cartridges**: standard 8K, 16K and Ultimax, Simons' BASIC, Ocean,
|
|
@@ -276,11 +308,10 @@ Known gaps:
|
|
|
276
308
|
- `badline-ruby` runs below real time, so its live audio stutters.
|
|
277
309
|
`badline` plays smoothly, and so do PSID tunes under
|
|
278
310
|
`badline-ruby --headless`.
|
|
279
|
-
-
|
|
311
|
+
- Without `--true-drive`, fast loaders and anything else that runs code
|
|
280
312
|
on the drive won't work (see [Media](#media)). The command channel
|
|
281
|
-
doesn't rename, copy, format or validate disks
|
|
282
|
-
|
|
283
|
-
- No NTSC machine and no REU.
|
|
313
|
+
doesn't rename, copy, format or validate disks.
|
|
314
|
+
- No PAL-N (Drean) machine.
|
|
284
315
|
- The emulator window has no freeze button yet, so a freezer cartridge
|
|
285
316
|
runs its menu but can't freeze a program.
|
|
286
317
|
|
data/doc/pinned-behaviour.md
CHANGED
|
@@ -25,10 +25,15 @@ only catches the rows that happen to move.
|
|
|
25
25
|
- [VIC phi1 bus](#vic-phi1-bus)
|
|
26
26
|
- [VIC light pen](#vic-light-pen)
|
|
27
27
|
- [VIC-II 8565](#vic-ii-8565)
|
|
28
|
+
- [VIC-II NTSC](#vic-ii-ntsc)
|
|
28
29
|
- [CIA 6526 timer pipeline](#cia-6526-timer-pipeline)
|
|
29
30
|
- [CIA 6526A interrupt register](#cia-6526a-interrupt-register)
|
|
30
31
|
- [CIA serial shift register](#cia-serial-shift-register)
|
|
31
32
|
- [6510 I/O port](#6510-io-port)
|
|
33
|
+
- [RAM power-on pattern](#ram-power-on-pattern)
|
|
34
|
+
- [1541 serial port](#1541-serial-port)
|
|
35
|
+
- [1541 disk mechanism](#1541-disk-mechanism)
|
|
36
|
+
- [REU DMA](#reu-dma)
|
|
32
37
|
- [SID oscillator](#sid-oscillator)
|
|
33
38
|
- [SID register writes](#sid-register-writes)
|
|
34
39
|
- [SID data bus](#sid-data-bus)
|
|
@@ -888,6 +893,46 @@ What the 8565 references don't settle, and so what stays as it is:
|
|
|
888
893
|
6569's idle access, without the 6569's `$38ff` read where a DMA delay
|
|
889
894
|
starts.
|
|
890
895
|
|
|
896
|
+
## VIC-II NTSC
|
|
897
|
+
|
|
898
|
+
The 6567R8 (`Region::NTSC`) and the 6567R56A (`Region::NTSC_OLD`) run
|
|
899
|
+
every VIC rule above, on a line of 65 or 64 cycles, except where the
|
|
900
|
+
region moves the sprites. The positions live in `Region::Profile` and
|
|
901
|
+
`VIC::Sprite::Timing`.
|
|
902
|
+
|
|
903
|
+
- **Sprite fetches.** Sprite 0's p-access runs in cycle 59 instead of
|
|
904
|
+
58, and everything that hangs off the fetches moves with it: the BA
|
|
905
|
+
windows (`VIC#layout_columns`), the DMA compares (columns 54 and 55
|
|
906
|
+
instead of 53 and 54), the phi1 accesses (`VIC#phi1_address`, where the
|
|
907
|
+
6567R8 idles in cycle 10), the column whose BA a late DMA start misses
|
|
908
|
+
(`Sprite::Timing#ba_column`), the reload pixels
|
|
909
|
+
(`Sprite::Timing#reload_x`) and the idle-bus row of sprites 3-7
|
|
910
|
+
(`Sprite::InternalBus#row`). On the 6567R8 sprite 3's fetch then falls
|
|
911
|
+
in the last two cycles of the line, so it reloads with the next line's
|
|
912
|
+
row as sprites 0-2 do. The expansion flip-flop still toggles in column
|
|
913
|
+
54.
|
|
914
|
+
- Pinned by `spritesteal_ntsc` and `_ntscold`, `spritex/testsuite_ntsc`
|
|
915
|
+
and `_ntscold`, `phi1timing_ntsc` and `_ntscold`, and
|
|
916
|
+
`spriteenable1_ntsc` and `2_ntsc` and their `_ntscold` twins (235 and
|
|
917
|
+
458 px), all in `testbench-ntsc`: with sprite 0 back in cycle 58 each
|
|
918
|
+
of them fails, and every other row of the sprite, phi1, dmadelay, vsp
|
|
919
|
+
and screenpos subsets passes as before.
|
|
920
|
+
- Spec guard: *sprite DMA cycle stealing on NTSC* and *#phi1_data on
|
|
921
|
+
NTSC* in [`vic_spec.rb`](../spec/badline/vic_spec.rb), and
|
|
922
|
+
[`timing_spec.rb`](../spec/badline/vic/sprite/timing_spec.rb).
|
|
923
|
+
- **Display compare and X counter.** The 6567R8 turns a sprite's display
|
|
924
|
+
on and off in cycle 59 (`sprite_display_cycle`), where the 6567R56A
|
|
925
|
+
keeps the 6569's cycle 58. Its X counter holds for 8 pixels
|
|
926
|
+
(`x_hold`), reading $184-$187 three times, so it reaches $1ff on a
|
|
927
|
+
520-pixel line. The 6567R56A's runs straight through 512 pixels.
|
|
928
|
+
Neither is pinned yet: moving the 6567R8's display compare to 58, the
|
|
929
|
+
6567R56A's to 59, or running the 6567R8's counter straight through and
|
|
930
|
+
wrapping it at 512 fails no row of those subsets.
|
|
931
|
+
- **Blanking and crop.** The blanked lines are the ones VICE's NTSC view
|
|
932
|
+
leaves out, 12-27 on the 6567R8 and 13-27 on the 6567R56A, so the
|
|
933
|
+
`testbench-ntsc` screenshots, VICE's 247 lines from line 28 running on
|
|
934
|
+
into the next frame, see every line they show painted.
|
|
935
|
+
|
|
891
936
|
## CIA 6526 timer pipeline
|
|
892
937
|
|
|
893
938
|
- Start and stop go through two stages. A force load lands one tick after
|
|
@@ -1091,6 +1136,182 @@ and each was knocked out: removing it fails the rows named.
|
|
|
1091
1136
|
- Spec guard: *when a program writes to the port* in
|
|
1092
1137
|
[`address_bus_spec.rb`](../spec/badline/address_bus_spec.rb).
|
|
1093
1138
|
|
|
1139
|
+
## RAM power-on pattern
|
|
1140
|
+
|
|
1141
|
+
- RAM powers on in runs of `$00,$00,$ff,$ff,$ff,$ff,$00,$00`, inverted in
|
|
1142
|
+
`$4000-$7fff` and `$c000-$ffff`, on every power cycle. The RAM under
|
|
1143
|
+
`$00`/`$01` follows the pattern like the rest. It is the pattern of a
|
|
1144
|
+
C64C (ASSY 250469 R4) in `C64/raminitpattern/readme.txt`
|
|
1145
|
+
(`-raminitstartvalue 0 -raminitvalueinvert 4 -raminitvalueoffset 2
|
|
1146
|
+
-raminitpatterninvert 16384 -raminitpatterninvertvalue 255`), without
|
|
1147
|
+
that machine's occasional random bytes. `AddressBus::RAM_POWER_ON`
|
|
1148
|
+
holds it.
|
|
1149
|
+
- Pinned by `C64/raminitpattern`: `cyberloadtest` fails when
|
|
1150
|
+
`$f379-$f478` holds one value, `darkstarbbstest` when the first ten
|
|
1151
|
+
bytes of one of the pages `$4000`, `$5000` … `$9000` do, `platoontest`
|
|
1152
|
+
when `$1000-$10ff` holds five equal bytes in a row and 140 or more
|
|
1153
|
+
bytes of one value, and `typicaltest`
|
|
1154
|
+
checks `$3fff` against a table of values the Typical demo survives.
|
|
1155
|
+
All-`$00` fails the first three, and each other pattern the readme
|
|
1156
|
+
lists fails at least one of the four.
|
|
1157
|
+
- Spec guard: *RAM at power-on* in
|
|
1158
|
+
[`address_bus_spec.rb`](../spec/badline/address_bus_spec.rb).
|
|
1159
|
+
- The RAM expansions' extra banks still power on at `$00`.
|
|
1160
|
+
|
|
1161
|
+
## 1541 serial port
|
|
1162
|
+
|
|
1163
|
+
- The drive sees the C64's side of the serial bus a host cycle late. The
|
|
1164
|
+
drive runs after the C64 in each host cycle, so `SerialPort` reads the
|
|
1165
|
+
C64's lines as `latch_host` took them at the end of the previous host
|
|
1166
|
+
cycle, both on VIA 1's port B (DATA IN, CLK IN, ATN IN) and on CA1,
|
|
1167
|
+
which ATN reaches. A CIA 2 write lands for the drive's cycles of the
|
|
1168
|
+
next host cycle, the way the CIA's pins change at the end of the cycle
|
|
1169
|
+
that writes them. The drive's own lines, and what the C64 reads back,
|
|
1170
|
+
stay live.
|
|
1171
|
+
- The 2-bit loader that uploads the drive tests' code times its transfer
|
|
1172
|
+
from an ATN edge, and seeing that edge a cycle early loses the first bit
|
|
1173
|
+
pair.
|
|
1174
|
+
- Pinned by the `testbench-drive` rows below. Each was checked by having
|
|
1175
|
+
the drive read the live lines (`@bus.atn_low?` and `@bus.low_lines`
|
|
1176
|
+
with no argument) and rerunning the rows:
|
|
1177
|
+
- Fail without the latch (`exit=$ff`): `drive/selftest`,
|
|
1178
|
+
`drive/diskid/diskid1`, `drive/interrupts/timera`, all six
|
|
1179
|
+
`drive/scanner` rows, both `drive/openbus` rows and
|
|
1180
|
+
`drive/viavarious/via1`, `via3a` and `via10`.
|
|
1181
|
+
- Pass either way: `drive/defaults`, `drive/rpm/rpm1` and `rpm2`,
|
|
1182
|
+
`drive/skew/skew1` and `drive/iecdelay/iec-bus-delay-auto`.
|
|
1183
|
+
- Spec guard: *sees an assertion from the host cycle after the one it
|
|
1184
|
+
lands in* and *shows the drive the C64's CLK from the host cycle after
|
|
1185
|
+
the write* in [`iec_bus_spec.rb`](../spec/badline/iec_bus_spec.rb).
|
|
1186
|
+
|
|
1187
|
+
## 1541 disk mechanism
|
|
1188
|
+
|
|
1189
|
+
- A step out against the stop at track 1 slips the stepper's phases: the
|
|
1190
|
+
head stays on half track 2, and the phase that pulled it outwards
|
|
1191
|
+
becomes that half track's. The DOS's bump steps out 92 half tracks, a
|
|
1192
|
+
whole number of phase turns, and then takes the phase it ends on as
|
|
1193
|
+
track 1's: `N:` formats track 1 there and steps in two phases a track.
|
|
1194
|
+
With each half track holding its own phase from power-on instead, the
|
|
1195
|
+
bump ended two half tracks off track 1's phase, the first step in
|
|
1196
|
+
pulled the head against the stop, and the DOS wrote track 2 over track
|
|
1197
|
+
1 and every later track one track out, at half track 2n - 2.
|
|
1198
|
+
- The DOS reads its own format back either way, since each header
|
|
1199
|
+
carries the track the DOS thought it was on: `drive/format` passes
|
|
1200
|
+
without the slip, and so doesn't pin it. Reading the disk back into a
|
|
1201
|
+
D64 does, since each header has to name the track the image holds it
|
|
1202
|
+
on.
|
|
1203
|
+
- Spec guard: *steps in from the stop with the first phase after the
|
|
1204
|
+
bump* in
|
|
1205
|
+
[`mechanism_spec.rb`](../spec/badline/drive1541/mechanism_spec.rb),
|
|
1206
|
+
and *lists it through the traps* (`:slow`) in
|
|
1207
|
+
[`drive1541_spec.rb`](../spec/badline/drive1541_spec.rb).
|
|
1208
|
+
- The disk turns at 300 rpm whatever bit rate VIA 2's PB5-6 select: a
|
|
1209
|
+
turn is 200,000 drive cycles over every track and over a half track
|
|
1210
|
+
without data. Each track's bits pass under the head at the rate they
|
|
1211
|
+
were written at: spread evenly over the turn, so a track as long as a
|
|
1212
|
+
turn holds at its zone's rate passes at that rate and a `.g64` track
|
|
1213
|
+
longer or shorter than that passes faster or slower, and a `.g64`
|
|
1214
|
+
speed map widens each byte's cells by its zone. The read clock runs at
|
|
1215
|
+
the selected rate and each flux transition brings it back into step,
|
|
1216
|
+
so a track read in its own zone reads clean and one read far enough
|
|
1217
|
+
off garbles (zone 0 against zone 3). With the rate selected turning
|
|
1218
|
+
the disk instead, a zone 3 track read in zone 0 read clean and took
|
|
1219
|
+
246 ms a turn, and a `.g64` track turned in its length of bytes.
|
|
1220
|
+
- Pinned by `drive/rpm` (`rpm1`, `rpm2` and `rpm3`, on the `.d64` and
|
|
1221
|
+
the `.g64`): each times a turn and passes within 297 to 303 rpm.
|
|
1222
|
+
- Pinned by `drive/skew/skew2`, on a `.g64` whose tracks all start
|
|
1223
|
+
sector 0 at the same angle: the head keeps the disk's angle across
|
|
1224
|
+
the tracks and half tracks it steps over only while every track
|
|
1225
|
+
turns in the same time.
|
|
1226
|
+
- Pinned by `drive/scanner` (all six programs, on the `.d64` and the
|
|
1227
|
+
`.g64`), which reads every track and each track's error map.
|
|
1228
|
+
- Spec guard: the *at 300 rpm* examples and *turns once in 200 ms* in
|
|
1229
|
+
[`mechanism_spec.rb`](../spec/badline/drive1541/mechanism_spec.rb),
|
|
1230
|
+
and *#cell_at* in
|
|
1231
|
+
[`track_spec.rb`](../spec/badline/drive1541/track_spec.rb).
|
|
1232
|
+
- A write lays its bits one to a cell from the cell under the head. A
|
|
1233
|
+
half track without data gets a blank track first, as long as a turn at
|
|
1234
|
+
the rate the drive writes at, not at the rate of the track's own zone,
|
|
1235
|
+
and a track written in another zone is laid out again as a turn at the
|
|
1236
|
+
rate written, its flux kept at its angle to within a cell. A track
|
|
1237
|
+
written in the selected zone keeps its length, so a `.g64` track
|
|
1238
|
+
longer or shorter than a turn keeps the sectors around the one written.
|
|
1239
|
+
- Pinned by `drive/rpm/rpm3`, which writes track 36, past a 35-track
|
|
1240
|
+
D64, at the zone 2 rate the DOS leaves selected there: a turn and a
|
|
1241
|
+
half of SYNC and five bytes, and times a turn by reading it back.
|
|
1242
|
+
With a zone 0 track of 6250 bytes a turn took 175,001 cycles
|
|
1243
|
+
(342.86 rpm, `exit=$ff`). With the last byte's leftover part of a
|
|
1244
|
+
cell widening the track's last cell, instead of the cells spread
|
|
1245
|
+
evenly over the turn, the SYNC broke there and the turn read as
|
|
1246
|
+
194,728 cycles (`exit=$ff`).
|
|
1247
|
+
- Spec guard: *gives a half track a blank track 7142 bytes around,
|
|
1248
|
+
written in zone 2* in
|
|
1249
|
+
[`disk_spec.rb`](../spec/badline/drive1541/disk_spec.rb), and *lays
|
|
1250
|
+
a track written in another zone out again as a turn at the zone
|
|
1251
|
+
written* and *writes a turn of SYNC that reads back without a break*
|
|
1252
|
+
in [`mechanism_spec.rb`](../spec/badline/drive1541/mechanism_spec.rb).
|
|
1253
|
+
- A disk made from a `.d64` starts sector 0 of each track where the DOS's
|
|
1254
|
+
`N:` leaves it: track 1 at the index angle, and each track after it
|
|
1255
|
+
round from the last by the skew `N:` leaves in its zone, 0.6869 of a
|
|
1256
|
+
turn in zone 3, 0.8915 in zone 2, 0.0897 in zone 1 and 0.2916 in zone
|
|
1257
|
+
0. Those are measured off a disk formatted here with `N:`.
|
|
1258
|
+
- Pinned by `drive/skew/skew1`, which expects a `.d64` to read with
|
|
1259
|
+
the skew a DOS format leaves. With every track starting sector 0 at
|
|
1260
|
+
the same angle it read `kernal format, tracks are aligned`
|
|
1261
|
+
(`exit=$ff`).
|
|
1262
|
+
- The skew moves which block the DOS meets first on each track, and
|
|
1263
|
+
`drive/scanner`'s three error-map rows (`scanner35e`, `40e`, `42e`)
|
|
1264
|
+
read track 4's error 22 only when the header comes first. They pass
|
|
1265
|
+
with this skew at 300 rpm. With the rate selected turning the disk,
|
|
1266
|
+
the same skew made them read track 4 as error 27, which is why
|
|
1267
|
+
`skew1` failed before.
|
|
1268
|
+
- Spec guard: *starts sector 0 of track N where the DOS's N: leaves
|
|
1269
|
+
it* in [`disk_spec.rb`](../spec/badline/drive1541/disk_spec.rb).
|
|
1270
|
+
- The CPU sees BYTE READY on SO a cycle late: BYTE READY sets V for the
|
|
1271
|
+
next cycle's instruction step, not the one in the cycle it falls in.
|
|
1272
|
+
- Pinned by `drive/hls-protection`. Its drive code counts the bytes
|
|
1273
|
+
from one SYNC mark to the next, reading `$1C00` for SYNC once per
|
|
1274
|
+
byte, 20 to 22 cycles after each BYTE READY with the delay (19 to 21
|
|
1275
|
+
without). The SYNC marks it has to find follow an `$AF`, whose four
|
|
1276
|
+
trailing 1 bits make SYNC six bits, 19 cycles in zone 3, after its
|
|
1277
|
+
BYTE READY; the ones it has to miss follow a `$2B`, eight bits on.
|
|
1278
|
+
With V set in the same cycle, the read sometimes came a cycle
|
|
1279
|
+
before SYNC, depending on the bit phase: on track 1 two of the 16
|
|
1280
|
+
counts read `$02C8`, two sectors' worth, and the testbench row failed
|
|
1281
|
+
(`exit=$ff`) on track 17. With the delay, tracks 1, 5, 10, 12 and 17
|
|
1282
|
+
(the 4-cycle loop) and 18, 20 and 24 (the 6-cycle loop) all read as
|
|
1283
|
+
the test expects.
|
|
1284
|
+
- Spec guard: *sets V through SO while CA2 is high, a cycle after BYTE
|
|
1285
|
+
READY* in
|
|
1286
|
+
[`mechanism_spec.rb`](../spec/badline/drive1541/mechanism_spec.rb).
|
|
1287
|
+
|
|
1288
|
+
## REU DMA
|
|
1289
|
+
|
|
1290
|
+
The REC's timing against the VIC, each rule derived from the REU
|
|
1291
|
+
testprogs named.
|
|
1292
|
+
|
|
1293
|
+
- A requested transfer takes the bus on the CPU's next read cycle with BA
|
|
1294
|
+
high, and moves its first byte there. Starting a cycle later moves every
|
|
1295
|
+
`REU/xfertiming` row and `REU/reutiming/reutiming`.
|
|
1296
|
+
- After reading C64 memory the REC waits out every BA-low cycle. After
|
|
1297
|
+
writing it goes on through the first BA-low cycle and waits from the
|
|
1298
|
+
second, and a fetch or swap whose last write it waited out takes one
|
|
1299
|
+
more cycle before handing the bus back (`REU::DMA#note_ba`,
|
|
1300
|
+
`#wind_down`). Pinned by `REU/bonzai/spritetiming`, whose 45 bytes a
|
|
1301
|
+
line with eight sprites on is 63 less the 18 cycles this leaves.
|
|
1302
|
+
- On the line whose raster matches sprite 0's Y, the REU doesn't see BA
|
|
1303
|
+
fall on the first cycle of sprite 0's window (`VIC#reu_ba_low?`).
|
|
1304
|
+
Without it `REU/bonzai/spritetiming` reads `$5a,$87` where a real REU
|
|
1305
|
+
gives `$5b,$88`.
|
|
1306
|
+
- A swap's read that falls on the first BA-low cycle, after its write, is
|
|
1307
|
+
held open while AEC stays high and takes the byte on the bus two cycles
|
|
1308
|
+
later. If it is the transfer's last byte it is read there instead, its
|
|
1309
|
+
write follows on the next cycle, and the REC hands the bus back
|
|
1310
|
+
(`REU::DMA#swap_read_on_ba`). Pinned by `REU/reutiming2/e5-m2`, `f3-m2`
|
|
1311
|
+
and `f4-m2`, whose patterns were captured on a breadbin. The
|
|
1312
|
+
`reutiming2` references without `-m2` read `$42` or a timer value there
|
|
1313
|
+
instead, and still fail.
|
|
1314
|
+
|
|
1094
1315
|
## SID oscillator
|
|
1095
1316
|
|
|
1096
1317
|
- The phase accumulator powers on at `$555555` (all bits high, with the odd
|
data/exe/badline-ruby
CHANGED
|
@@ -6,6 +6,8 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
|
6
6
|
|
|
7
7
|
require "badline/options"
|
|
8
8
|
|
|
9
|
+
$stdout.sync = true
|
|
10
|
+
|
|
9
11
|
begin
|
|
10
12
|
options = Badline::Options.parse(ARGV)
|
|
11
13
|
rescue Badline::Options::Error => e
|
|
@@ -47,12 +49,13 @@ end
|
|
|
47
49
|
|
|
48
50
|
begin
|
|
49
51
|
application = Badline::GUI::Application.new(media_path: options.media_path, autostart: options.autostart?,
|
|
50
|
-
song: options.song,
|
|
51
|
-
|
|
52
|
+
song: options.song, sound: options.sound?, verbose: options.verbose?,
|
|
53
|
+
machine: { sid_model: options.sid_model, reu: options.reu,
|
|
54
|
+
true_drive: options.true_drive?, ntsc: options.ntsc? },
|
|
52
55
|
disk: { read_only: options.read_only? })
|
|
53
|
-
rescue Badline::Storage::SIDFile::FormatError, Badline::Storage::T64::FormatError,
|
|
56
|
+
rescue Badline::Media::TrueDrive::Error, Badline::Storage::SIDFile::FormatError, Badline::Storage::T64::FormatError,
|
|
54
57
|
Badline::Storage::TAP::FormatError, Badline::Storage::CRTFile::FormatError,
|
|
55
|
-
Badline::Cartridge::UnsupportedTypeError => e
|
|
58
|
+
Badline::Storage::G64Image::FormatError, Badline::Cartridge::UnsupportedTypeError => e
|
|
56
59
|
warn "badline-ruby: #{options.media_path}: #{e.message}"
|
|
57
60
|
exit 1
|
|
58
61
|
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
class AddressBus
|
|
5
|
+
# The page tables of an Ultimax cartridge, which AddressBus lays out
|
|
6
|
+
# in place of the $01 banking while the cartridge holds GAME low and
|
|
7
|
+
# EXROM high.
|
|
8
|
+
module UltimaxPages
|
|
9
|
+
private
|
|
10
|
+
|
|
11
|
+
# Ultimax cartridges ignore the $01 lines: 4K of RAM, ROML/ROMH windows,
|
|
12
|
+
# I/O always visible and open address space everywhere else. The ROML
|
|
13
|
+
# and ROMH selects fire on writes as well, so cartridge RAM or flash in
|
|
14
|
+
# either window takes the writes there.
|
|
15
|
+
def map_ultimax_pages
|
|
16
|
+
@read_pages.fill(@open_bus, 0x10, 0xf0)
|
|
17
|
+
@write_pages.fill(@open_bus, 0x10, 0xf0)
|
|
18
|
+
@read_pages.fill(@cartridge.roml, 0x80, 0x20) if @cartridge.roml
|
|
19
|
+
map_ultimax_writes(@cartridge.roml, 0x80)
|
|
20
|
+
@read_pages.fill(@cartridge.romh, 0xe0, 0x20) if @cartridge.romh
|
|
21
|
+
map_ultimax_writes(@cartridge.romh, 0xe0)
|
|
22
|
+
map_ultimax_a000
|
|
23
|
+
map_io_pages
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def map_ultimax_writes(bank, first_page)
|
|
27
|
+
@write_pages.fill(bank, first_page, 0x20) if bank.respond_to?(:poke)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def map_ultimax_a000
|
|
31
|
+
return unless (window = @cartridge.ultimax_a000)
|
|
32
|
+
|
|
33
|
+
@read_pages.fill(window, 0xa0, 0x20)
|
|
34
|
+
@write_pages.fill(window, 0xa0, 0x20)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
data/lib/badline/address_bus.rb
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "badline/address_bus/ultimax_pages"
|
|
4
|
+
|
|
3
5
|
module Badline
|
|
4
6
|
# Memory layout:
|
|
5
7
|
#
|
|
@@ -28,6 +30,7 @@ module Badline
|
|
|
28
30
|
# 0xE000-0xFFFF - KERNAL ROM / Cartridge ROM (high) - 8kb
|
|
29
31
|
class AddressBus
|
|
30
32
|
include Addressable
|
|
33
|
+
include UltimaxPages
|
|
31
34
|
|
|
32
35
|
# I/O 1 and 2, and the Ultimax holes, with nothing on the bus. A read
|
|
33
36
|
# picks up the byte the VIC fetched in the preceding phi1 half-cycle,
|
|
@@ -45,11 +48,15 @@ module Badline
|
|
|
45
48
|
PORT_FLOATING = 0b1100_1000
|
|
46
49
|
TAPE_SENSE = 0b0001_0000
|
|
47
50
|
|
|
48
|
-
|
|
51
|
+
# RAM powers on in runs of $00 $00 $FF $FF $FF $FF $00 $00, inverted in
|
|
52
|
+
# the second and fourth 16K: a C64C (ASSY 250469 R4) from
|
|
53
|
+
# C64/raminitpattern/readme.txt, without its occasional random bytes.
|
|
54
|
+
# See doc/pinned-behaviour.md.
|
|
55
|
+
RAM_POWER_ON = Array.new(2**16) { |addr| (((addr + 2) / 4) ^ (addr / 0x4000)).odd? ? 0xff : 0x00 }.freeze
|
|
49
56
|
|
|
50
57
|
attr_reader :io_port, :ram, :basic_rom, :character_rom, :kernal_rom,
|
|
51
58
|
:vic, :sid, :color_ram, :cia1, :cia2, :keyboard, :joystick1, :joystick2,
|
|
52
|
-
:control_ports, :cartridge, :ultimax, :phi1_ultimax, :datasette, :region, :video_ram
|
|
59
|
+
:control_ports, :cartridge, :ultimax, :phi1_ultimax, :datasette, :region, :video_ram, :reu
|
|
53
60
|
|
|
54
61
|
# ram_expansion fits a +60K (:plus60k) or +256K (:plus256k).
|
|
55
62
|
def initialize(sid_model: :mos6581, cia_model: :mos6526, vic_model: :mos6569, region: Region::PAL,
|
|
@@ -57,7 +64,7 @@ module Badline
|
|
|
57
64
|
@region = region
|
|
58
65
|
@ram = Memory.new(RAM_POWER_ON, length: 2**16, start: 0)
|
|
59
66
|
@ram_expansion = RAMExpansion.build(ram_expansion, @ram) { update_overlays! }
|
|
60
|
-
@cartridge = nil
|
|
67
|
+
@cartridge = @reu = nil
|
|
61
68
|
@debug_register = nil
|
|
62
69
|
|
|
63
70
|
@basic_rom = ROM.load("basic.rom", 0xa000)
|
|
@@ -99,6 +106,13 @@ module Badline
|
|
|
99
106
|
update_overlays!
|
|
100
107
|
end
|
|
101
108
|
|
|
109
|
+
# An REU takes I/O 2 unless a cartridge claims it, and watches writes
|
|
110
|
+
# to $FF00 for the one that starts an armed transfer.
|
|
111
|
+
def attach_reu(reu)
|
|
112
|
+
@reu = reu
|
|
113
|
+
update_overlays!
|
|
114
|
+
end
|
|
115
|
+
|
|
102
116
|
def power_on!
|
|
103
117
|
@ram.clear!(RAM_POWER_ON)
|
|
104
118
|
@ram_expansion.power_on!
|
|
@@ -177,6 +191,7 @@ module Badline
|
|
|
177
191
|
@video_ram = @ram_expansion.video_ram
|
|
178
192
|
|
|
179
193
|
@ultimax ? map_ultimax_pages : map_banked_pages
|
|
194
|
+
@write_pages[0xff] = @reu.trigger.wrap(@write_pages[0xff]) if @reu
|
|
180
195
|
end
|
|
181
196
|
|
|
182
197
|
def map_banked_pages
|
|
@@ -216,32 +231,6 @@ module Badline
|
|
|
216
231
|
@write_pages.fill(bank, first_page, 0x20) if bank.is_a?(Cartridge::RAMBank)
|
|
217
232
|
end
|
|
218
233
|
|
|
219
|
-
# Ultimax cartridges ignore the $01 lines: 4K of RAM, ROML/ROMH windows,
|
|
220
|
-
# I/O always visible and open address space everywhere else. The ROML
|
|
221
|
-
# and ROMH selects fire on writes as well, so cartridge RAM or flash in
|
|
222
|
-
# either window takes the writes there.
|
|
223
|
-
def map_ultimax_pages
|
|
224
|
-
@read_pages.fill(@open_bus, 0x10, 0xf0)
|
|
225
|
-
@write_pages.fill(@open_bus, 0x10, 0xf0)
|
|
226
|
-
@read_pages.fill(@cartridge.roml, 0x80, 0x20) if @cartridge.roml
|
|
227
|
-
map_ultimax_writes(@cartridge.roml, 0x80)
|
|
228
|
-
@read_pages.fill(@cartridge.romh, 0xe0, 0x20) if @cartridge.romh
|
|
229
|
-
map_ultimax_writes(@cartridge.romh, 0xe0)
|
|
230
|
-
map_ultimax_a000
|
|
231
|
-
map_io_pages
|
|
232
|
-
end
|
|
233
|
-
|
|
234
|
-
def map_ultimax_writes(bank, first_page)
|
|
235
|
-
@write_pages.fill(bank, first_page, 0x20) if bank.respond_to?(:poke)
|
|
236
|
-
end
|
|
237
|
-
|
|
238
|
-
def map_ultimax_a000
|
|
239
|
-
return unless (window = @cartridge.ultimax_a000)
|
|
240
|
-
|
|
241
|
-
@read_pages.fill(window, 0xa0, 0x20)
|
|
242
|
-
@write_pages.fill(window, 0xa0, 0x20)
|
|
243
|
-
end
|
|
244
|
-
|
|
245
234
|
def map_io_pages
|
|
246
235
|
{
|
|
247
236
|
vic => 0xd0..0xd3, sid => 0xd4..0xd7, color_ram => 0xd8..0xdb,
|
|
@@ -251,6 +240,7 @@ module Badline
|
|
|
251
240
|
end
|
|
252
241
|
@ram_expansion.map_io(@read_pages, @write_pages)
|
|
253
242
|
@read_pages[0xd7] = @write_pages[0xd7] = @debug_register if @debug_register
|
|
243
|
+
@read_pages[0xdf] = @write_pages[0xdf] = @reu if @reu
|
|
254
244
|
map_cartridge_io if @cartridge
|
|
255
245
|
end
|
|
256
246
|
|