badline 0.16.0 → 0.16.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +16 -0
- data/README.md +32 -9
- data/doc/media.md +9 -0
- data/doc/snapshot-format.md +4 -2
- data/doc/snapshots.md +51 -10
- data/lib/badline/drives/ieee_bus.rb +50 -4
- data/lib/badline/drives/model2031/ieee_port.rb +133 -0
- data/lib/badline/drives/model2031/keyed_state.rb +66 -0
- data/lib/badline/drives/model2031/parts.rb +25 -0
- data/lib/badline/drives/model2031.rb +92 -0
- data/lib/badline/frontend/menu/menu_pages.rb +11 -2
- data/lib/badline/kernal_trap/ieee.rb +3 -0
- data/lib/badline/machine.rb +3 -2
- data/lib/badline/media/pet_media.rb +5 -5
- data/lib/badline/media/true_drive.rb +19 -6
- data/lib/badline/media.rb +6 -2
- data/lib/badline/options/help.rb +8 -4
- data/lib/badline/options/table.rb +9 -3
- data/lib/badline/options/validation.rb +20 -4
- data/lib/badline/options.rb +27 -8
- data/lib/badline/pet/bus/expansion.rb +99 -5
- data/lib/badline/pet/bus.rb +30 -12
- data/lib/badline/pet/characters.rb +8 -2
- data/lib/badline/pet/crtc_video.rb +20 -7
- data/lib/badline/pet/ieee_drive.rb +76 -0
- data/lib/badline/pet/ieee_ports.rb +12 -1
- data/lib/badline/pet/ieee_traps.rb +10 -5
- data/lib/badline/pet/keyed_modules.rb +4 -1
- data/lib/badline/pet/keyed_parts.rb +10 -6
- data/lib/badline/pet/model.rb +56 -6
- data/lib/badline/pet/rom_sets.rb +30 -6
- data/lib/badline/pet.rb +33 -15
- data/lib/badline/roms/README +33 -1
- data/lib/badline/roms/dos2031.rom +0 -0
- data/lib/badline/roms/pet/characters-din.rom +0 -0
- data/lib/badline/roms/pet/editor4-80-b-50hz-8296.rom +0 -0
- data/lib/badline/snapshot/container.rb +4 -1
- data/lib/badline/snapshot/field_doc.rb +4 -3
- data/lib/badline/snapshot/image.rb +22 -11
- data/lib/badline/snapshot/pet_setup.rb +30 -13
- data/lib/badline/snapshot/vice/pet.rb +117 -0
- data/lib/badline/snapshot/vice/pet_cpu.rb +67 -0
- data/lib/badline/snapshot/vice/pet_crtc.rb +246 -0
- data/lib/badline/snapshot/vice/pet_mem.rb +93 -0
- data/lib/badline/snapshot/vice/pet_pias.rb +56 -0
- data/lib/badline/snapshot/vice/pet_restore.rb +150 -0
- data/lib/badline/snapshot/vice/pet_via.rb +164 -0
- data/lib/badline/snapshot/vice.rb +1 -0
- data/lib/badline/version.rb +1 -1
- metadata +16 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3e6b53df8a82bbf9f9524441b6d948fb245c0e81c589757dcd23b7e69fbd4c69
|
|
4
|
+
data.tar.gz: 83e5a043f2a09ed982663e1df4a0f4e2c5483791417fd1220785dab8d0e39d42
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f739ad0afd029d46648ceff8307e010ca6290e4c8951175e30c70e08ec11385dba966c79ebd2d3d5a93e52db922b0362b45239825e3f42e750ee4f0fe56589a5
|
|
7
|
+
data.tar.gz: d5e22aa87453b2446e2d5c2168f99076e4a950a753022fd5dc5946fb56ababc156bcda2b0d57ed1e90a923dee9d693c7040187f4475d8c1a6e50afa5346ca7e3
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.16.1](https://github.com/elektronaut/badline/compare/v0.16.0...v0.16.1) (2026-10-18)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* add the 2031 disk drive on the PET's IEEE-488 bus ([bcc76a5](https://github.com/elektronaut/badline/commit/bcc76a536a44bdb6173b26823c83a5026a788c4f))
|
|
9
|
+
* add the CBM 8296 ([48fb14d](https://github.com/elektronaut/badline/commit/48fb14d0d94b75cf003d7601c637158801acaef8))
|
|
10
|
+
* put a true 2031 on the PET's device 8 with --true-drive ([c64e1b4](https://github.com/elektronaut/badline/commit/c64e1b4245b1390ba6d57e5ffaad620c4055b5cd))
|
|
11
|
+
* read and write xpet's .vsf snapshots for the PET ([607050b](https://github.com/elektronaut/badline/commit/607050b9c72767e335d2b488afd0d3c4ce153c81))
|
|
12
|
+
* save a PET's 2031 in its snapshots ([a8d6880](https://github.com/elektronaut/badline/commit/a8d68806a34e34f45c02fb7a5a00dd2699ccb618))
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
### Bug Fixes
|
|
16
|
+
|
|
17
|
+
* blank the CRTC PETs' character lines past the eighth ([f02db02](https://github.com/elektronaut/badline/commit/f02db02a6255944b552b6a5f3e8108483ae78875))
|
|
18
|
+
|
|
3
19
|
## [0.16.0](https://github.com/elektronaut/badline/compare/v0.15.1...v0.16.0) (2026-10-17)
|
|
4
20
|
|
|
5
21
|
|
data/README.md
CHANGED
|
@@ -82,7 +82,7 @@ differences noted in the table. `--help` lists the options for either.
|
|
|
82
82
|
| --- | --- |
|
|
83
83
|
| `--no-autostart` | Attach the media and stop at `READY.`, so you can type the `LOAD` yourself |
|
|
84
84
|
| `--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 |
|
|
85
|
-
| `--true-drive` | Put an emulated 1541 on device 8 instead of the KERNAL traps,
|
|
85
|
+
| `--true-drive` | Put an emulated 1541 on device 8 instead of the KERNAL traps, a 1551 on the Plus/4, or a 2031 on the PET. See [Media](#media) |
|
|
86
86
|
| `--drive-type 1541` | On the Plus/4, make the true drive on device 8 an emulated 1541 on the serial bus in place of the 1551. It implies `--true-drive`. See [The Plus/4](#the-plus4) |
|
|
87
87
|
| `-s`, `--subtune N` | Pick a subtune of a `.sid` file, counting from 1 |
|
|
88
88
|
| `--sid 6581`, `--sid 8580` | Fit the older or newer SID. `--sid auto`, the default, takes a `.sid` tune's own |
|
|
@@ -115,7 +115,7 @@ differences noted in the table. `--help` lists the options for either.
|
|
|
115
115
|
| `--script FILE` | Run the events in `FILE`, one `FRAME:EVENT` a line |
|
|
116
116
|
| `--screenshot FILE` | Save the last frame as a `.bmp` |
|
|
117
117
|
| `--save-snapshot FILE` | Save the machine as a `.vsf` snapshot after the last frame |
|
|
118
|
-
| `--vice` | Add VICE's modules to `--save-snapshot`'s C64 snapshot, so x64sc opens it too |
|
|
118
|
+
| `--vice` | Add VICE's modules to `--save-snapshot`'s C64 or PET snapshot, so x64sc or xpet opens it too |
|
|
119
119
|
|
|
120
120
|
```sh
|
|
121
121
|
badline --unpaced --frames 12000 --true-drive disk1.d64 \
|
|
@@ -302,7 +302,8 @@ badline pet --model 2001 # The first PET, with BASIC 1
|
|
|
302
302
|
|
|
303
303
|
`--model` picks the machine: `8032` (the default, 32K, 80 columns and
|
|
304
304
|
the business keyboard), `8096` (an 8032 with the 64K board at `$FFF0`),
|
|
305
|
-
`4032` (40 columns on a CRTC and the
|
|
305
|
+
`8296` (128K on one board, below), `4032` (40 columns on a CRTC and the
|
|
306
|
+
graphics keyboard), `3032` (the
|
|
306
307
|
discrete 40-column board with BASIC 2), `3032b` (the same with the
|
|
307
308
|
business keyboard) and `2001` (8K, BASIC 1 and the chiclet keyboard).
|
|
308
309
|
`--ram 4k`, `8k` or `16k` fits less RAM in place of the model's own, as
|
|
@@ -324,7 +325,22 @@ cycles, 400 to a frame. `4-80-b-50hz-0283`, the chip dated 0283, runs
|
|
|
324
325
|
lines of 64 cycles, 313 to a frame, with the screen's rows a scanline
|
|
325
326
|
closer together and lower down. Both boot to the same READY.
|
|
326
327
|
|
|
328
|
+
The 8296 is an 8032 on a board of its own, with 128K: 32K below
|
|
329
|
+
`$8000`, 32K above it, whose first 4K hold the screen and the rest lies
|
|
330
|
+
under the ROMs, and a 64K board banked through `$FFF0` as the 8096's is.
|
|
331
|
+
Writes from `$8000` up go to the RAM under the ROMs, and `--jumpers`
|
|
332
|
+
fits the jumpers that let reads find it there: `ram9` and `rama` for RAM
|
|
333
|
+
at `$9000` and `$A000`, as xpet's `-petram9` and `-petramA`, and
|
|
334
|
+
`userport` for the user port's PA0 to PA2 to switch it in over BASIC,
|
|
335
|
+
the editor and the KERNAL. Its CRTC's RA4 is an address line, so the
|
|
336
|
+
lines past the 16th of a tall character row show RAM from `$C000`. It
|
|
337
|
+
runs its own 50 Hz editor, `4-80-b-50hz-8296`, with lines of 59 cycles,
|
|
338
|
+
339 to a frame, and takes the 8032's editors too. `--characters din`
|
|
339
|
+
fits the German boards' 4K character ROM, whose two halves R12's bit 5
|
|
340
|
+
picks between.
|
|
341
|
+
|
|
327
342
|
```sh
|
|
343
|
+
badline pet --model 8296 --jumpers ram9,rama # An 8296 with RAM at $9000 and $A000
|
|
328
344
|
badline pet --model 2001 --basic 2 # A 2001 with BASIC 2
|
|
329
345
|
badline pet --model 3032 --basic 4 # A 3032 with BASIC 4, without a CRTC
|
|
330
346
|
badline pet --editor 4-80-b-60hz # An 8032 at 60 Hz, as --60hz runs it
|
|
@@ -341,11 +357,13 @@ A program loads at the start of BASIC, `$0401`, and runs. A `.d80`, a
|
|
|
341
357
|
`.d82` or a tape made for the PET starts a PET without `pet`, but a
|
|
342
358
|
program that loads at `$0401` starts a C64, since a VIC-20 with 3K loads
|
|
343
359
|
there too. Disks go through the KERNAL traps on the IEEE-488 bus as
|
|
344
|
-
device 8,
|
|
360
|
+
device 8, or with `--true-drive` an emulated 2031 on the bus, which
|
|
361
|
+
reads `.d64` and `.g64` images, and tapes play on cassette port 1. The keys type what the host's
|
|
345
362
|
keys type, on either keyboard, and `Alt` adds the PET's `SHIFT`:
|
|
346
363
|
[Input](#input) lists the rest. The pause
|
|
347
364
|
menu's Power page names the model, its RAM and its frame rate, and its
|
|
348
|
-
Expansion port page its RAM and the 8096's 64K board
|
|
365
|
+
Expansion port page its RAM and the 8096's 64K board, or the 8296's
|
|
366
|
+
jumpers. The PET has no
|
|
349
367
|
joysticks. `F11` and the Snapshots page save it as they do the others,
|
|
350
368
|
but not for VICE.
|
|
351
369
|
|
|
@@ -357,7 +375,8 @@ newest, even after badline has quit and started again. badline also
|
|
|
357
375
|
autosaves every two minutes of play, and the pause menu's Snapshots page
|
|
358
376
|
saves under a name and loads any of them. Snapshots from badline 0.14.0
|
|
359
377
|
on load in every later version. They use VICE's `.vsf` format: badline
|
|
360
|
-
opens those x64sc
|
|
378
|
+
opens those x64sc and xpet save, and x64sc and xpet open badline's saved
|
|
379
|
+
with `--vice`.
|
|
361
380
|
See [doc/snapshots.md](doc/snapshots.md) for where they're kept, what
|
|
362
381
|
they hold and how far the two agree.
|
|
363
382
|
|
|
@@ -534,12 +553,16 @@ in cartridge 1 as it does from the command line.
|
|
|
534
553
|
receives at its baud rates, with nothing on the user port; Ruby code
|
|
535
554
|
driving the machine can plug in a terminal, whose bytes it feeds and
|
|
536
555
|
drains, or a loopback plug (`Plus4::ACIA#plug`).
|
|
537
|
-
- **PET and CBM**: the 2001, 3032, 3032b, 4032, 8032 and
|
|
538
|
-
BASIC 1, 2 or 4, the discrete video and the 6545 CRTC on 40 and 80
|
|
556
|
+
- **PET and CBM**: the 2001, 3032, 3032b, 4032, 8032, 8096 and 8296,
|
|
557
|
+
with BASIC 1, 2 or 4, the discrete video and the 6545 CRTC on 40 and 80
|
|
539
558
|
columns, both 6520 PIAs and the 6522 VIA, the graphics and business
|
|
540
|
-
keyboards, the sound on the VIA's CB2, the 8096's 64K board,
|
|
559
|
+
keyboards, the sound on the VIA's CB2, the 8096's 64K board, the
|
|
560
|
+
8296's 128K with its jumpers, RA4 and 4K character ROM, both
|
|
541
561
|
cassette ports and device 8 on the IEEE-488 bus through the KERNAL
|
|
542
562
|
traps, `.d80` and `.d82` among its disks (`badline pet`).
|
|
563
|
+
- **2031**: the PET's single drive, the 1541's board and DOS 2.6 on the
|
|
564
|
+
IEEE-488 bus, with the bus's handshake on every line
|
|
565
|
+
(`badline pet --true-drive`).
|
|
543
566
|
- **1541**: an emulated drive running its own DOS (`--true-drive`).
|
|
544
567
|
- **1571**: the C128D's drive running DOS 3.0, in 1541 mode and in 1571
|
|
545
568
|
mode at 2 MHz with both sides of a `.d71` or `.g71`, and fast serial
|
data/doc/media.md
CHANGED
|
@@ -38,6 +38,15 @@ On the Plus/4 and the C16, `--true-drive` puts an emulated 1551 there
|
|
|
38
38
|
instead, on TCBM, the parallel cable to its interface in the expansion
|
|
39
39
|
port, and `--drive-type 1541` a 1541 on the serial bus.
|
|
40
40
|
|
|
41
|
+
On the PET, `--true-drive` puts an emulated 2031 there, the 1541's board
|
|
42
|
+
with an IEEE-488 interface, running DOS 2.6 and answering the bus's
|
|
43
|
+
handshake line by line. It reads `.d64` and `.g64` images, which BASIC 2
|
|
44
|
+
and 4 autostart with `LOAD"*",8` and `RUN`; a `.d80` or `.d82` needs the
|
|
45
|
+
traps. BASIC 1's LOAD never finishes on the drive, since its UNLISTEN
|
|
46
|
+
leaves its command in the device number the LOAD then talks to, but its
|
|
47
|
+
OPEN, INPUT# and PRINT# work, and the error channel with them. `--basic
|
|
48
|
+
2`, the upgrade Commodore sold, loads.
|
|
49
|
+
|
|
41
50
|
A `.g64` holds the disk's raw GCR, half tracks and all, so copy
|
|
42
51
|
protection and fast loaders that read it work.
|
|
43
52
|
|
data/doc/snapshot-format.md
CHANGED
|
@@ -68,9 +68,10 @@ Written by the C64, VIC-20, C128, Plus/4, PET.
|
|
|
68
68
|
| `host.true_drive_model` | text: 1541 1551 | Plus/4: "1551" |
|
|
69
69
|
| `refresh_hz` | int | PET: 50 or 60 |
|
|
70
70
|
| `rom.basic` | text | PET: "pet/basic4.rom" or "pet/basic1.rom" |
|
|
71
|
-
| `rom.editor` | text | PET: "pet/editor4-80-b-50hz.rom" or "pet/editor1.rom" |
|
|
71
|
+
| `rom.editor` | text | PET: "pet/editor4-80-b-50hz.rom" or "pet/editor1.rom" or "pet/editor4-80-b-50hz-8296.rom" |
|
|
72
72
|
| `rom.kernal` | text | PET: "pet/kernal4.rom" or "pet/kernal1.rom" |
|
|
73
73
|
| `rom.characters` | text | PET: "pet/characters2.rom" or "pet/characters1.rom" |
|
|
74
|
+
| `jumpers` | text | PET: "" |
|
|
74
75
|
| `vertical_drive` | bool | PET: true |
|
|
75
76
|
|
|
76
77
|
## BL-RAM
|
|
@@ -84,9 +85,10 @@ Written by the C64, VIC-20, C128, Plus/4, PET.
|
|
|
84
85
|
| `bus.high_bank` | int & 0x3 | Plus/4: 0 |
|
|
85
86
|
| `bus.rom_selected` | bool | Plus/4: true |
|
|
86
87
|
| `bus.data` | byte | Plus/4: 255; PET: 253 |
|
|
87
|
-
| `screen` | bytes | PET: 2048 × 0 or 1024 × 0 |
|
|
88
|
+
| `screen` | bytes | PET: 2048 × 0 or 1024 × 0 or 32768 × 0 |
|
|
88
89
|
| `expansion.control` | byte | PET: 0 |
|
|
89
90
|
| `expansion.ram` | bytes | PET: 65536 × 0 |
|
|
91
|
+
| `expansion.user_port` | int | PET: 7 |
|
|
90
92
|
|
|
91
93
|
## BL-COLORRAM
|
|
92
94
|
|
data/doc/snapshots.md
CHANGED
|
@@ -143,16 +143,57 @@ doesn't open it, and badline doesn't open xplus4's. `--vice` and
|
|
|
143
143
|
## The PET
|
|
144
144
|
|
|
145
145
|
Every PET and CBM model saves and restores the same way. A snapshot of
|
|
146
|
-
one holds its RAM, the screen RAM, an
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
146
|
+
one holds its RAM, the screen RAM, or on an 8296 all 32K from `$8000`,
|
|
147
|
+
an 8096's or 8296's 64K board with its control register and the user
|
|
148
|
+
port lines the 8296's RAM follows, both PIAs, the VIA, the CPU, the
|
|
149
|
+
CRTC on the boards that have one, the picture with where the video is
|
|
150
|
+
in its frame, the tapes in both datasettes with their places on them,
|
|
151
|
+
and the disk or directory device 8 serves through the IEEE-488 traps.
|
|
152
|
+
It restores into a machine of the same model with the same RAM, the
|
|
153
|
+
same editor frame rate, the same ROMs, which come with the model, and
|
|
154
|
+
an 8296's same jumpers. Its file names its machine `PET`, as xpet's do,
|
|
155
|
+
and holds only badline's modules, so xpet doesn't open it unless it is
|
|
156
|
+
saved with `--vice` (see [xpet](#xpet)).
|
|
157
|
+
|
|
158
|
+
### xpet
|
|
159
|
+
|
|
160
|
+
A PET snapshot saved with `--vice`, or `computer.export_snapshot(path)`,
|
|
161
|
+
also holds xpet's modules for the CPU, the RAM with the screen RAM, or
|
|
162
|
+
an 8296's 32K from `$8000`, an 8096's or 8296's board and its control
|
|
163
|
+
register, the CRTC, both PIAs and the VIA, and the ones xpet 3.10 needs
|
|
164
|
+
to open the file, with nothing open on the file system drive and
|
|
165
|
+
nothing on the user port. The CRTC module carries the discrete boards'
|
|
166
|
+
video too, in xpet's terms. The file says it is in VICE's snapshot
|
|
167
|
+
format 1.0, the only one xpet reads.
|
|
168
|
+
|
|
169
|
+
xpet 3.10 opens those snapshots as long as its model and editor match:
|
|
170
|
+
`-model 2001`, `3032`, `3032B`, `4032`, `8032`, `8096` or `8296`, and
|
|
171
|
+
for a badline editor other than xpet's own, `-editor` naming badline's
|
|
172
|
+
ROM. xpet's 8296 runs the 8032's editor, 901474-04 dated 0283, so an
|
|
173
|
+
8296 saved for it has to run that editor (`--editor 4-80-b-50hz-0283`),
|
|
174
|
+
and xpet takes the 8296's jumpers from its own settings. It keeps its
|
|
175
|
+
own drives and datasettes and leaves out badline's tapes and disk. Its
|
|
176
|
+
CRTC loses a few lines on opening any snapshot, its own too, so a
|
|
177
|
+
program it runs on from one finds the vertical sync a few hundred
|
|
178
|
+
cycles later than one it ran without stopping.
|
|
179
|
+
|
|
180
|
+
Snapshots xpet saves open in badline through the same modules: the CPU
|
|
181
|
+
at its instruction boundary, the RAM, the screen RAM, an 8096's or
|
|
182
|
+
8296's board, the CRTC's registers and beam, both PIAs' registers and
|
|
183
|
+
flags, and the VIA's registers, timers and shift register. A program
|
|
184
|
+
badline runs on from xpet's snapshot finds the vertical sync on the
|
|
185
|
+
cycle xpet does, so the two run on alike, cycle for cycle. The picture
|
|
186
|
+
starts again with the next frame. xpet doesn't save its ROMs, which
|
|
187
|
+
keyboard it has or an 8296's jumpers, so the board, the RAM and the
|
|
188
|
+
CRTC's frame build the machine: a 2001, a 3032 for any other discrete
|
|
189
|
+
board, a 4032, an 8032, an 8096 or an 8296 with none of its jumpers
|
|
190
|
+
fitted, at 50 or 60 Hz, and an 8032, 8096 or 8296 whose CRTC lines are
|
|
191
|
+
64 cycles long runs xpet's own 50 Hz editor, 901474-04 dated 0283. A
|
|
192
|
+
SuperPET's snapshot fails. It reports the modules it leaves out, such
|
|
193
|
+
as the drives, the datasettes and the keyboard, and carries on without
|
|
194
|
+
them. Restored into a running PET, an xpet snapshot fails for a PET
|
|
195
|
+
built another way, such as a 3032B, and otherwise switches the machine
|
|
196
|
+
off and on, keeping its tapes and the disk device 8 serves.
|
|
156
197
|
|
|
157
198
|
## VICE
|
|
158
199
|
|
|
@@ -8,9 +8,13 @@ module Badline
|
|
|
8
8
|
# pulls it low. The PET is the controller and always on the bus, and
|
|
9
9
|
# reads back every line it drives.
|
|
10
10
|
#
|
|
11
|
-
#
|
|
12
|
-
#
|
|
13
|
-
#
|
|
11
|
+
# The controller pushes the lines it pulls whenever they may have moved
|
|
12
|
+
# (host_lines=), and the bus holds them for the drives, which take them
|
|
13
|
+
# a host cycle late. Each drive on the bus answers the lines it pulls
|
|
14
|
+
# (ieee_low_lines) while ATN is or isn't low, from its own outputs, which
|
|
15
|
+
# hold still while it sleeps. With no drive on the bus nobody answers a
|
|
16
|
+
# handshake, and the KERNAL reports DEVICE NOT PRESENT for every device
|
|
17
|
+
# the traps don't serve.
|
|
14
18
|
class IEEEBus
|
|
15
19
|
# The lines as bits, the data lines in the low byte, DIO1 in bit 0.
|
|
16
20
|
DIO = 0x00ff
|
|
@@ -20,9 +24,51 @@ module Badline
|
|
|
20
24
|
NDAC = 0x0800
|
|
21
25
|
ATN = 0x1000
|
|
22
26
|
|
|
27
|
+
# The lines the controller last pushed.
|
|
28
|
+
attr_reader :host_lines
|
|
29
|
+
|
|
30
|
+
attr_reader :drives
|
|
31
|
+
|
|
32
|
+
def initialize
|
|
33
|
+
@drives = []
|
|
34
|
+
@host_lines = 0
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# The controller pushes the lines it pulls, and the drives look at
|
|
38
|
+
# them again.
|
|
39
|
+
def host_lines=(lines)
|
|
40
|
+
@host_lines = lines
|
|
41
|
+
drives = @drives
|
|
42
|
+
i = 0
|
|
43
|
+
while i < drives.length
|
|
44
|
+
drives[i].host_written!
|
|
45
|
+
i += 1
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def attach(drive)
|
|
50
|
+
@drives << drive unless @drives.include?(drive)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def detach(drive)
|
|
54
|
+
@drives.delete(drive)
|
|
55
|
+
end
|
|
56
|
+
|
|
23
57
|
# The lines low on the bus while the controller pulls the lines in
|
|
24
58
|
# +controller+ low.
|
|
25
|
-
def low_lines(controller)
|
|
59
|
+
def low_lines(controller)
|
|
60
|
+
low = controller
|
|
61
|
+
drives = @drives
|
|
62
|
+
return low if drives.empty?
|
|
63
|
+
|
|
64
|
+
atn = controller.anybits?(ATN)
|
|
65
|
+
i = 0
|
|
66
|
+
while i < drives.length
|
|
67
|
+
low |= drives[i].ieee_low_lines(atn)
|
|
68
|
+
i += 1
|
|
69
|
+
end
|
|
70
|
+
low
|
|
71
|
+
end
|
|
26
72
|
end
|
|
27
73
|
end
|
|
28
74
|
end
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
module Drives
|
|
5
|
+
class Model2031
|
|
6
|
+
# VIA 1's side of the IEEE-488 bus (Drives::IEEEBus), through a 75160
|
|
7
|
+
# for the data lines and a 75161 for the control lines (schematic
|
|
8
|
+
# 2031038, sheet 2):
|
|
9
|
+
#
|
|
10
|
+
# PA0-7 DIO1-8 PB4 T/R, high to talk
|
|
11
|
+
# PB0 ATN acknowledge (ATNA) PB5 HD SEL, unused
|
|
12
|
+
# PB1 NRFD PB6 DAV
|
|
13
|
+
# PB2 NDAC PB7 ATN in, high while ATN is low
|
|
14
|
+
# PB3 EOI CA1 ATN in, as PB7
|
|
15
|
+
#
|
|
16
|
+
# The ports read and drive the lines' levels, 0 for a low line. T/R
|
|
17
|
+
# turns the transceivers: talking, the drive drives the data lines,
|
|
18
|
+
# DAV and EOI, and reads NRFD and NDAC; listening, the other way round.
|
|
19
|
+
# EOI turns back to the bus while ATN is low. A pin the transceiver
|
|
20
|
+
# drives the bus from reads the drive's own side, high but for what
|
|
21
|
+
# the VIA or a jumper pulls low.
|
|
22
|
+
#
|
|
23
|
+
# An XOR of ATN in and ATNA drives two 7406s that pull NRFD and NDAC
|
|
24
|
+
# low on the bus, so the drive answers ATN before the DOS gets to it,
|
|
25
|
+
# and holds the bus until the DOS acknowledges. ATNA reaches the XOR
|
|
26
|
+
# only while PB0 is an output, and is low there otherwise, which
|
|
27
|
+
# leaves the bus alone while the DOS reads the jumpers.
|
|
28
|
+
#
|
|
29
|
+
# The device number jumpers are diodes from PB0 and PB1 to CA2: with
|
|
30
|
+
# CA2 low, a fitted diode pulls its line low, and the DOS reads the
|
|
31
|
+
# device number off the two at reset, 8 with both fitted. Lifting the
|
|
32
|
+
# one on PB0 adds 1, and the one on PB1 2.
|
|
33
|
+
#
|
|
34
|
+
# The drive takes the controller's lines a host cycle late, as the
|
|
35
|
+
# serial drives do (Engine::SerialLines): latch_host takes them at the
|
|
36
|
+
# end of each host cycle.
|
|
37
|
+
class IEEEPort
|
|
38
|
+
ATNA = 0x01
|
|
39
|
+
NRFD = 0x02
|
|
40
|
+
NDAC = 0x04
|
|
41
|
+
EOI = 0x08
|
|
42
|
+
TALK = 0x10
|
|
43
|
+
DAV = 0x40
|
|
44
|
+
ATN_IN = 0x80
|
|
45
|
+
|
|
46
|
+
attr_reader :bus
|
|
47
|
+
|
|
48
|
+
# The lines the controller pulled, as latch_host took them.
|
|
49
|
+
attr_reader :host
|
|
50
|
+
|
|
51
|
+
def initialize(device:, bus:)
|
|
52
|
+
raise ArgumentError, "a 2031 is device 8 to 11, not #{device}" unless device.between?(8, 11)
|
|
53
|
+
|
|
54
|
+
@diodes = ~(device - 8) & 0x03
|
|
55
|
+
@via = nil
|
|
56
|
+
@host = 0
|
|
57
|
+
self.bus = bus
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# The drive's VIA 1, whose outputs it drives the bus with.
|
|
61
|
+
attr_writer :via
|
|
62
|
+
|
|
63
|
+
def bus=(bus)
|
|
64
|
+
@bus = bus
|
|
65
|
+
latch_host
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Takes the controller's lines for the drive cycles of the next host
|
|
69
|
+
# cycle.
|
|
70
|
+
def latch_host
|
|
71
|
+
@host = @bus.host_lines
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Whether the controller has moved ATN since the last latch_host.
|
|
75
|
+
def atn_moved? = (@bus.host_lines ^ @host).anybits?(IEEEBus::ATN)
|
|
76
|
+
|
|
77
|
+
def atn_low? = @host.anybits?(IEEEBus::ATN)
|
|
78
|
+
|
|
79
|
+
# The data lines, while the transceiver listens.
|
|
80
|
+
def read_a(_lines) = talking? ? 0xff : ~@bus.low_lines(@host) & 0xff
|
|
81
|
+
|
|
82
|
+
# The control lines the transceiver turns towards the drive, and
|
|
83
|
+
# ATN.
|
|
84
|
+
def read_b(_lines)
|
|
85
|
+
low = @bus.low_lines(@host)
|
|
86
|
+
value = 0x7f
|
|
87
|
+
if talking?
|
|
88
|
+
value &= ~NRFD if low.anybits?(IEEEBus::NRFD)
|
|
89
|
+
value &= ~NDAC if low.anybits?(IEEEBus::NDAC)
|
|
90
|
+
value &= ~EOI if low.anybits?(IEEEBus::EOI) && low.anybits?(IEEEBus::ATN)
|
|
91
|
+
else
|
|
92
|
+
value &= ~EOI if low.anybits?(IEEEBus::EOI)
|
|
93
|
+
value &= ~DAV if low.anybits?(IEEEBus::DAV)
|
|
94
|
+
end
|
|
95
|
+
value |= ATN_IN if low.anybits?(IEEEBus::ATN)
|
|
96
|
+
value & jumpers
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# The lines the drive pulls low while ATN is low or not, as bus bits.
|
|
100
|
+
def ieee_low_lines(atn)
|
|
101
|
+
via = @via
|
|
102
|
+
lines = via.port_b_output & jumpers
|
|
103
|
+
low = 0
|
|
104
|
+
if lines.anybits?(TALK)
|
|
105
|
+
low |= ~via.port_a_output & IEEEBus::DIO
|
|
106
|
+
low |= IEEEBus::DAV if lines.nobits?(DAV)
|
|
107
|
+
low |= IEEEBus::EOI if lines.nobits?(EOI) && !atn
|
|
108
|
+
else
|
|
109
|
+
low |= IEEEBus::NRFD if lines.nobits?(NRFD)
|
|
110
|
+
low |= IEEEBus::NDAC if lines.nobits?(NDAC)
|
|
111
|
+
end
|
|
112
|
+
low |= IEEEBus::NRFD | IEEEBus::NDAC if atn != (lines & via.port_b_directions).anybits?(ATNA)
|
|
113
|
+
low
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# The controller's lines as the port latched them, under `prefix`.
|
|
117
|
+
def save_fields(out, prefix) = out.int("#{prefix}host", @host)
|
|
118
|
+
|
|
119
|
+
def load_fields(input, prefix)
|
|
120
|
+
@host = input.int("#{prefix}host", @host)
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
private
|
|
124
|
+
|
|
125
|
+
def talking? = @via.port_b_output.anybits?(TALK)
|
|
126
|
+
|
|
127
|
+
# PB0 and PB1 as the jumper diodes leave them: pulled low through
|
|
128
|
+
# each fitted one while CA2 is low.
|
|
129
|
+
def jumpers = @via.ca2_output ? 0xff : ~@diodes & 0xff
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
end
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
module Drives
|
|
5
|
+
class Model2031
|
|
6
|
+
# The drive's own fields in a keyed snapshot module (BL-D8): its
|
|
7
|
+
# model, its clock phase and cycle count, the SO pulse due, and the
|
|
8
|
+
# controller's lines as the IEEE-488 port latched them under ieee.*. A
|
|
9
|
+
# save settles the drive first, so it is awake; a load forgets its
|
|
10
|
+
# orbits.
|
|
11
|
+
module KeyedState
|
|
12
|
+
def save_fields(out)
|
|
13
|
+
settle!
|
|
14
|
+
out.text("model", model_name).int("phase", @phase).int("cycles", @cycles)
|
|
15
|
+
out.bool("so_pending", @mechanism.so_pending)
|
|
16
|
+
@serial_port.save_fields(out, "ieee.")
|
|
17
|
+
out
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def load_fields(input)
|
|
21
|
+
settle!
|
|
22
|
+
input.choice("model", [model_name], model_name)
|
|
23
|
+
@phase = input.int("phase", @phase)
|
|
24
|
+
@cycles = input.int("cycles", @cycles)
|
|
25
|
+
@mechanism.so_pending = input.bool("so_pending", @mechanism.so_pending)
|
|
26
|
+
@serial_port.load_fields(input, "ieee.")
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# The drive as modules of its own, named from `name` (BL-D8 for
|
|
30
|
+
# device 8): its own fields, its RAM, each VIA, the CPU, the
|
|
31
|
+
# mechanism and the disk, when one is in, as the 1541's.
|
|
32
|
+
def save_modules(sections, name)
|
|
33
|
+
sections << Snapshot::Keyed.section(name) { |out| save_fields(out) }
|
|
34
|
+
sections << Snapshot::Keyed.section("#{name}-RAM") { |out| @bus.save_fields(out) }
|
|
35
|
+
sections << Snapshot::Keyed.section("#{name}-VIA1") { |out| @via1.save_fields(out) }
|
|
36
|
+
sections << Snapshot::Keyed.section("#{name}-VIA2") { |out| @via2.save_fields(out) }
|
|
37
|
+
sections << Snapshot::Keyed.section("#{name}-CPU") { |out| @cpu.save_fields(out) }
|
|
38
|
+
sections << Snapshot::Keyed.section("#{name}-MECH") { |out| @mechanism.save_fields(out) }
|
|
39
|
+
disk = @mechanism.disk
|
|
40
|
+
sections << Snapshot::Keyed.section("#{name}-DISK") { |out| disk.save_fields(out) } if disk
|
|
41
|
+
sections
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# The drive from its modules, `input` being its own. No disk module
|
|
45
|
+
# means no disk in the drive.
|
|
46
|
+
def load_modules(modules, input, name)
|
|
47
|
+
load_fields(input)
|
|
48
|
+
@bus.load_fields(modules.required("#{name}-RAM"))
|
|
49
|
+
@via1.load_fields(modules.required("#{name}-VIA1"))
|
|
50
|
+
@via2.load_fields(modules.required("#{name}-VIA2"))
|
|
51
|
+
@cpu.load_fields(modules.required("#{name}-CPU"))
|
|
52
|
+
disk = modules.optional("#{name}-DISK")
|
|
53
|
+
mechanism = modules.required("#{name}-MECH")
|
|
54
|
+
@mechanism.load_fields(mechanism,
|
|
55
|
+
disk.nil? ? nil : Model1541::Disk.from_fields(disk, detached: modules.detached?))
|
|
56
|
+
@via1.restored!
|
|
57
|
+
@via2.restored!
|
|
58
|
+
@cpu.restored!
|
|
59
|
+
forget_orbits
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
include KeyedState
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
module Drives
|
|
5
|
+
class Model2031
|
|
6
|
+
# VIA 1, facing the IEEE-488 bus (IEEEPort), a class of its own so
|
|
7
|
+
# its peripheral keeps one type, apart from the serial drives'.
|
|
8
|
+
class IEEEVIA < Chips::VIA
|
|
9
|
+
# DDRB, whose PB0 says whether the VIA drives ATNA (IEEEPort).
|
|
10
|
+
def port_b_directions = @ddrb
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# The drive CPU's bus, decoded as the 1541's (Model1541::Bus), with
|
|
14
|
+
# VIA 1 an IEEEVIA.
|
|
15
|
+
class Bus < Model1541::Bus
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# The drive's 6502, on the drive's Bus.
|
|
19
|
+
class CPU
|
|
20
|
+
include Badline::Chips::CPU::Core
|
|
21
|
+
include Engine::IdleCPU
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "badline/drives/model1541"
|
|
4
|
+
require "badline/drives/ieee_bus"
|
|
5
|
+
require "badline/drives/model2031/parts"
|
|
6
|
+
require "badline/drives/model2031/ieee_port"
|
|
7
|
+
require "badline/drives/model2031/keyed_state"
|
|
8
|
+
|
|
9
|
+
module Badline
|
|
10
|
+
module Drives
|
|
11
|
+
# The 2031 disk drive of the PET and CBM: the 1541's board with an
|
|
12
|
+
# IEEE-488 interface in place of the serial one (Commodore schematic
|
|
13
|
+
# 2031038). A 6502 at 1 MHz runs DOS 2.6 from 16 KB of ROM, with 2 KB
|
|
14
|
+
# of RAM, VIA 1 at $1800 on the IEEE-488 bus (IEEEPort) and VIA 2 at
|
|
15
|
+
# $1C00 running the 1541's GCR Mechanism, decoded as the 1541's.
|
|
16
|
+
#
|
|
17
|
+
# Drives::Engine::Core clocks it against the host and sleeps through
|
|
18
|
+
# the DOS's idle loop, as the 1541's, until ATN moves or a timer is due
|
|
19
|
+
# (Engine::VIAs). The bus reads the lines it pulls without waking it.
|
|
20
|
+
class Model2031
|
|
21
|
+
include Engine::Core
|
|
22
|
+
include Engine::VIAs
|
|
23
|
+
|
|
24
|
+
CLOCK_HZ = 1_000_000
|
|
25
|
+
|
|
26
|
+
# Where the DOS's idle loop starts over (see Drives::Engine::Idle).
|
|
27
|
+
IDLE_LOOP = 0xec4d
|
|
28
|
+
|
|
29
|
+
# The host cycles by which a 2031 switched on with the machine has run
|
|
30
|
+
# its DOS's RAM test and ROM checksums and reads its jumpers.
|
|
31
|
+
BOOT_CYCLES = 1_300_000
|
|
32
|
+
|
|
33
|
+
attr_reader :ieee_bus
|
|
34
|
+
|
|
35
|
+
def model_name = "2031"
|
|
36
|
+
|
|
37
|
+
# +rom+ covers $C000-$FFFF, and defaults to the DOS image in the ROM
|
|
38
|
+
# path. +device+ is the number the jumpers set, 8 to 11.
|
|
39
|
+
def initialize(rom: nil, host_clock_hz: CLOCK_HZ, device: 8, debug: false)
|
|
40
|
+
@device = device
|
|
41
|
+
@ieee_bus = IEEEBus.new
|
|
42
|
+
@serial_port = IEEEPort.new(device:, bus: @ieee_bus)
|
|
43
|
+
@via1 = IEEEVIA.new(start: 0x1800, peripheral: @serial_port)
|
|
44
|
+
@serial_port.via = @via1
|
|
45
|
+
@mechanism = Model1541::Mechanism.new
|
|
46
|
+
@via2 = Model1541::DiskVIA.new(start: 0x1c00, mechanism: @mechanism)
|
|
47
|
+
@bus = Bus.new(rom: rom || ROM.load("dos2031.rom", 0xc000), via1: @via1, via2: @via2)
|
|
48
|
+
@cpu = CPU.new(@bus, debug:)
|
|
49
|
+
@clock_hz = CLOCK_HZ
|
|
50
|
+
@idle_loop = IDLE_LOOP
|
|
51
|
+
@host_clock_hz = host_clock_hz
|
|
52
|
+
@phase = 0
|
|
53
|
+
@cycles = 0
|
|
54
|
+
init_idle(debug)
|
|
55
|
+
@via1.ca1 = false
|
|
56
|
+
@via1.reset!
|
|
57
|
+
connect(@ieee_bus)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Plugs the drive into an IEEE-488 bus, leaving the one it was on.
|
|
61
|
+
def connect(ieee_bus)
|
|
62
|
+
settle!
|
|
63
|
+
@ieee_bus.detach(self)
|
|
64
|
+
@ieee_bus = ieee_bus
|
|
65
|
+
@serial_port.bus = ieee_bus
|
|
66
|
+
ieee_bus.attach(self)
|
|
67
|
+
hear_atn
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# The lines the drive pulls on the bus while ATN is low or not
|
|
71
|
+
# (IEEEPort). They hold still while it sleeps, so reading them leaves
|
|
72
|
+
# it asleep.
|
|
73
|
+
def ieee_low_lines(atn) = @serial_port.ieee_low_lines(atn)
|
|
74
|
+
|
|
75
|
+
private
|
|
76
|
+
|
|
77
|
+
# One drive cycle, as the 1541's (Model1541#step): the mechanism, then
|
|
78
|
+
# ATN on VIA 1's CA1, the VIAs and the CPU.
|
|
79
|
+
def step
|
|
80
|
+
so = @mechanism.take_so
|
|
81
|
+
@mechanism.cycle!
|
|
82
|
+
@via1.ca1 = @serial_port.atn_low?
|
|
83
|
+
@via1.cycle!
|
|
84
|
+
@via2.cycle!
|
|
85
|
+
@cpu.irq = @via1.irq? || @via2.irq?
|
|
86
|
+
@cpu.so! if so
|
|
87
|
+
@cpu.cycle!
|
|
88
|
+
@cycles += 1
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
@@ -156,10 +156,19 @@ module Badline
|
|
|
156
156
|
toggle(plus4? ? "SID CARD" : "SID", :sid, [%w[6581 8580], %i[sid6581 sid8580], model == :mos8580 ? 1 : 0])
|
|
157
157
|
end
|
|
158
158
|
|
|
159
|
-
# The PET's memory: its RAM,
|
|
159
|
+
# The PET's memory: its RAM, the 8096's 64K board, and the 8296's
|
|
160
|
+
# 128K with the jumpers that put RAM under its ROMs.
|
|
160
161
|
def draw_pet_memory
|
|
162
|
+
model = @computer.model
|
|
163
|
+
return draw_8296_memory if model.cbm8296?
|
|
164
|
+
|
|
161
165
|
info("RAM", "#{@computer.ram_kb}K")
|
|
162
|
-
info("EXPANSION RAM",
|
|
166
|
+
info("EXPANSION RAM", model.expansion ? "64K BOARD" : "")
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
def draw_8296_memory
|
|
170
|
+
info("RAM", "128K, 64K OF IT BANKED")
|
|
171
|
+
info("JUMPERS", @computer.jumpers.join(", ").upcase)
|
|
163
172
|
end
|
|
164
173
|
|
|
165
174
|
# Saving now and by name, loading a named save, and the quicksaves
|