badline 0.10.0 → 0.10.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 +19 -0
- data/README.md +1 -1
- data/doc/library.md +15 -1
- data/doc/snapshots.md +4 -3
- data/lib/badline/address_bus/fittings.rb +38 -0
- data/lib/badline/address_bus/pla.rb +35 -5
- data/lib/badline/address_bus/roms.rb +12 -5
- data/lib/badline/address_bus.rb +6 -2
- data/lib/badline/computer.rb +8 -8
- data/lib/badline/keyboard.rb +12 -1
- data/lib/badline/machine.rb +1 -1
- data/lib/badline/model.rb +29 -12
- data/lib/badline/options/table.rb +1 -1
- data/lib/badline/roms/README +24 -0
- data/lib/badline/roms/c64gs.rom +0 -0
- data/lib/badline/roms/kernal-pet64.rom +0 -0
- data/lib/badline/snapshot/setup.rb +15 -10
- data/lib/badline/snapshot/state.rb +5 -4
- data/lib/badline/snapshot/vice.rb +2 -1
- data/lib/badline/version.rb +1 -1
- data/lib/badline/vic/bank.rb +2 -1
- data/lib/badline/vic.rb +16 -2
- metadata +4 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e82b9f5bb012d15fd7744f164b82420c3b71c00b14b43843093e1a2b3722beeb
|
|
4
|
+
data.tar.gz: cf94f9a74e8c9d5d6b9d3a94a5404d29376a4c57da7f18b78c9dd87da4a7c880
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 14986d25fea5add5cecf4c10001b4f9cf9e087023f07bf8a8fc27d950f799ff41ae086518165ca67b840c440e5f56cf661d335ac5d4f5de347260c0bd44864bb
|
|
7
|
+
data.tar.gz: 12ba3fa5d2276efb1003edaa884e8f1c58ee7d2e2ac5e05b0f7d927e6d8b5a37869893f012d92560772b3adf18ea2d886c058737bf9e10aac25aec54e36afe6b
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.10.1](https://github.com/elektronaut/badline/compare/v0.10.0...v0.10.1) (2026-10-08)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* add the C64GS as --model c64gs ([ea04c0d](https://github.com/elektronaut/badline/commit/ea04c0d03b3a8c34426ff68a80ae0fc36db8123b))
|
|
9
|
+
* add the C64GS as --model c64gs ([e533932](https://github.com/elektronaut/badline/commit/e533932360befa88d05cf1369cc417f40cababa7))
|
|
10
|
+
* add the MAX Machine as --model ultimax ([fc48c40](https://github.com/elektronaut/badline/commit/fc48c40035892409175bf3d8fd08add6af0bbac9))
|
|
11
|
+
* add the MAX Machine as --model ultimax ([22aca64](https://github.com/elektronaut/badline/commit/22aca643abc0746e362209fb48977cee94c2b985))
|
|
12
|
+
* add the PET 64 as --model pet64 ([e17c0bd](https://github.com/elektronaut/badline/commit/e17c0bd0d13d15caf6d0b5377a5ac0a5756847cf))
|
|
13
|
+
* add the PET 64 as --model pet64 ([2320ac7](https://github.com/elektronaut/badline/commit/2320ac7cd41ef6a4865e56d433585ddebb7ef01f))
|
|
14
|
+
* bundle the C64GS's ROM, 390852-01 ([e31b280](https://github.com/elektronaut/badline/commit/e31b280044c18e639cc65df62a8b39d97dd874be))
|
|
15
|
+
* bundle the PET 64's KERNAL, 901246-01 ([9a029ec](https://github.com/elektronaut/badline/commit/9a029ecfe25c0d9a806d8858253c4c172e175b7f))
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
### Miscellaneous Chores
|
|
19
|
+
|
|
20
|
+
* release 0.10.1 ([9d6f6ba](https://github.com/elektronaut/badline/commit/9d6f6ba71a647f24f00a830fbc772531fbcb4d92))
|
|
21
|
+
|
|
3
22
|
## [0.10.0](https://github.com/elektronaut/badline/compare/v0.9.0...v0.10.0) (2026-10-08)
|
|
4
23
|
|
|
5
24
|
|
data/README.md
CHANGED
|
@@ -85,7 +85,7 @@ differences noted in the table. `--help` lists the options for either.
|
|
|
85
85
|
| `--sid 6581`, `--sid 8580` | Fit the older or newer SID. `--sid auto`, the default, takes a `.sid` tune's own |
|
|
86
86
|
| `--ram SIZE` | Give a VIC-20 the RAM expansion `SIZE`: `unexpanded`, `3k`, `8k`, `16k`, `24k`, `32k` or `all`. Without it, the VIC-20 gets the expansion its media needs. See [The VIC-20](#the-vic-20) |
|
|
87
87
|
| `--reu SIZE` | Plug in a RAM Expansion Unit of `SIZE` K: 128, 256, 512 (a 1750) or up to 16384 |
|
|
88
|
-
| `--model NAME` | Run another model of the machine, named as in VICE. For the C64: `c64` (the default, a PAL C64 with the 6569 VIC-II, 6526 CIAs and the 6581 SID), `c64c` (the PAL C64C, with the 8565, 6526As and the 8580), `ntsc` (the 6567R8), `newntsc` (the NTSC C64C, with the 8562, 6526As and the 8580), `oldntsc` (the first NTSC C64s' 6567R56A), `drean` (the Drean C64 of Argentina, PAL-N with the 6572)
|
|
88
|
+
| `--model NAME` | Run another model of the machine, named as in VICE. For the C64: `c64` (the default, a PAL C64 with the 6569 VIC-II, 6526 CIAs and the 6581 SID), `c64c` (the PAL C64C, with the 8565, 6526As and the 8580), `ntsc` (the 6567R8), `newntsc` (the NTSC C64C, with the 8562, 6526As and the 8580), `oldntsc` (the first NTSC C64s' 6567R56A), `drean` (the Drean C64 of Argentina, PAL-N with the 6572), `sx64` (the portable SX-64, with its own KERNAL and no datasette), `pet64` (the PET 64 or Educator 64, a PAL C64 in a PET's case with its own KERNAL and a green monochrome monitor) `c64gs` (the C64GS games console, a PAL C64C with its own BASIC and KERNAL, and no keyboard or datasette) or `ultimax` (the MAX Machine of Japan, NTSC with 2K of RAM, one CIA and no ROMs, which runs only a cartridge). `--sid` or a `.sid` tune's own SID takes the model's place |
|
|
89
89
|
| `--ntsc` | Run an NTSC C64, as `--model ntsc` does |
|
|
90
90
|
| `--no-sound` | Don't play the SID (`badline` only, where sound is on by default) |
|
|
91
91
|
| `--sound` | Play the SID (`badline-ruby`, where sound is off by default) |
|
data/doc/library.md
CHANGED
|
@@ -42,6 +42,19 @@ model (`Badline::Model::ALL`):
|
|
|
42
42
|
- `kernal: :sx64` fits the SX-64's KERNAL, and `datasette: false`
|
|
43
43
|
leaves the cassette port empty, so inserting a tape raises
|
|
44
44
|
`Badline::Datasette::Missing`. The SX-64 has both.
|
|
45
|
+
- `kernal: :pet64` fits the PET 64's KERNAL, and `board: :pet64` its
|
|
46
|
+
green monochrome monitor: the VIC's `palette` is
|
|
47
|
+
`Badline::VIC::GREEN_PALETTE`, each colour's luminance in green. The
|
|
48
|
+
PET 64 has both.
|
|
49
|
+
- `kernal: :gs` fits the C64GS's BASIC and KERNAL, and `board: :gs` its
|
|
50
|
+
case, which has no keyboard: `computer.keyboard` takes no keys. The
|
|
51
|
+
C64GS has both, and no datasette.
|
|
52
|
+
- `board: :max` builds the MAX Machine's board: 2K of RAM at
|
|
53
|
+
`$0000-$07FF`, no ROMs mapped, CIA 1 alone, answering all through
|
|
54
|
+
`$DC00-$DFFF` but for the I/O 1 page a cartridge takes, and Ultimax
|
|
55
|
+
mode with or without a cartridge. CIA 2 is never on the bus, so the
|
|
56
|
+
VIC stays in bank 0. `Badline::Model::ULTIMAX` runs it on
|
|
57
|
+
`Badline::Region::NTSC_OLD`.
|
|
45
58
|
|
|
46
59
|
## Cartridges
|
|
47
60
|
|
|
@@ -63,7 +76,8 @@ Its contents are lost when the emulator quits.
|
|
|
63
76
|
The KERNAL, BASIC and character ROMs come with the gem. To run other
|
|
64
77
|
images, such as a patched KERNAL, point `BADLINE_ROM_PATH` at a
|
|
65
78
|
directory that holds `kernal.rom`, `basic.rom` and `character.rom`, plus
|
|
66
|
-
`kernal-sx64.rom` for the SX-64
|
|
79
|
+
`kernal-sx64.rom` for the SX-64, `kernal-pet64.rom` for the PET 64,
|
|
80
|
+
`c64gs.rom` for the C64GS and `eapi/eapi-am29f040-14` if you
|
|
67
81
|
attach EasyFlash cartridges.
|
|
68
82
|
`Badline.rom_path = dir` does the same before a `Badline::Computer` is
|
|
69
83
|
built, and `nil` restores the bundled set.
|
data/doc/snapshots.md
CHANGED
|
@@ -62,9 +62,10 @@ Every model saves this way, PAL, NTSC, old NTSC and Drean alike, and so
|
|
|
62
62
|
does a machine with an REU, with its RAM, its registers and a transfer
|
|
63
63
|
part way through. A snapshot only restores in the badline version that
|
|
64
64
|
wrote it, into a machine with the same chip models, region, RAM
|
|
65
|
-
expansion, REU, KERNAL and
|
|
66
|
-
the KERNAL and the datasette restores as a machine with the
|
|
67
|
-
and a datasette
|
|
65
|
+
expansion, REU, KERNAL, datasette and board. One saved before badline
|
|
66
|
+
recorded the KERNAL and the datasette restores as a machine with the
|
|
67
|
+
C64's KERNAL and a datasette, and one saved before it recorded the board
|
|
68
|
+
restores on a C64's.
|
|
68
69
|
|
|
69
70
|
## The VIC-20
|
|
70
71
|
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Badline
|
|
4
|
+
class AddressBus
|
|
5
|
+
# What a model fits around the chips: its KERNAL, a datasette or none,
|
|
6
|
+
# and the board and case they sit in.
|
|
7
|
+
module Fittings
|
|
8
|
+
# The keywords #fit takes.
|
|
9
|
+
NAMES = %i[kernal datasette board].freeze
|
|
10
|
+
|
|
11
|
+
# The C64's board and case, the PET 64's, whose built-in monitor
|
|
12
|
+
# shows the VIC's output in shades of green, the C64GS's, which has
|
|
13
|
+
# no keyboard, or the MAX Machine's (:max): 2K of RAM, no ROMs, CIA 1
|
|
14
|
+
# alone at $DC00-$DFFF and the cartridge port in Ultimax mode.
|
|
15
|
+
BOARDS = %i[c64 pet64 gs max].freeze
|
|
16
|
+
|
|
17
|
+
attr_reader :board
|
|
18
|
+
|
|
19
|
+
# Fits the KERNAL ROM ROMs::KERNALS names, leaves the cassette port
|
|
20
|
+
# empty unless `datasette`, and puts the chips in the board BOARDS
|
|
21
|
+
# names.
|
|
22
|
+
def fit(kernal: :c64, datasette: true, board: :c64)
|
|
23
|
+
self.kernal = kernal unless kernal == :c64
|
|
24
|
+
@datasette.disconnect! unless datasette
|
|
25
|
+
self.board = board unless board == :c64
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def board=(name)
|
|
29
|
+
raise ArgumentError, "no board named #{name}" unless BOARDS.include?(name)
|
|
30
|
+
|
|
31
|
+
@board = name
|
|
32
|
+
vic.palette = name == :pet64 ? VIC::GREEN_PALETTE : VIC::PALETTE
|
|
33
|
+
keyboard.connected = name != :gs
|
|
34
|
+
update_overlays!
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -8,6 +8,18 @@ module Badline
|
|
|
8
8
|
# ROMs, the chips, and #map_ram_pages, which lays out the RAM that the
|
|
9
9
|
# ROMs, the cartridge and I/O map over.
|
|
10
10
|
module PLA
|
|
11
|
+
# A chip's registers seen at another page, which reads and writes
|
|
12
|
+
# them as at the page the chip starts at.
|
|
13
|
+
class Mirror
|
|
14
|
+
def initialize(chip, start)
|
|
15
|
+
@chip = chip
|
|
16
|
+
@start = start
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def peek(addr) = @chip.peek(@start | (addr & 0xff))
|
|
20
|
+
def poke(addr, value) = @chip.poke(@start | (addr & 0xff), value)
|
|
21
|
+
end
|
|
22
|
+
|
|
11
23
|
private
|
|
12
24
|
|
|
13
25
|
def map_pla_pages
|
|
@@ -27,19 +39,26 @@ module Badline
|
|
|
27
39
|
end
|
|
28
40
|
end
|
|
29
41
|
|
|
30
|
-
# Ultimax
|
|
42
|
+
# Ultimax mode ignores the $01 lines: 4K of RAM, ROML/ROMH windows,
|
|
31
43
|
# I/O always visible and open address space everywhere else. The ROML
|
|
32
44
|
# and ROMH selects fire on writes as well, so cartridge RAM or flash in
|
|
33
|
-
# either window takes the writes there.
|
|
45
|
+
# either window takes the writes there. The MAX has 2K of RAM, and is
|
|
46
|
+
# in Ultimax mode with no cartridge too.
|
|
34
47
|
def map_ultimax_pages
|
|
35
|
-
@
|
|
36
|
-
@
|
|
48
|
+
first = @board == :max ? 0x08 : 0x10
|
|
49
|
+
@read_pages.fill(@open_bus, first, 0x100 - first)
|
|
50
|
+
@write_pages.fill(@open_bus, first, 0x100 - first)
|
|
51
|
+
map_ultimax_cartridge if @cartridge
|
|
52
|
+
map_io_pages
|
|
53
|
+
map_max_io if @board == :max
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def map_ultimax_cartridge
|
|
37
57
|
@read_pages.fill(@cartridge.roml, 0x80, 0x20) if @cartridge.roml
|
|
38
58
|
map_ultimax_writes(@cartridge.roml, 0x80)
|
|
39
59
|
@read_pages.fill(@cartridge.romh, 0xe0, 0x20) if @cartridge.romh
|
|
40
60
|
map_ultimax_writes(@cartridge.romh, 0xe0)
|
|
41
61
|
map_ultimax_a000
|
|
42
|
-
map_io_pages
|
|
43
62
|
end
|
|
44
63
|
|
|
45
64
|
def map_ultimax_writes(bank, first_page)
|
|
@@ -53,6 +72,17 @@ module Badline
|
|
|
53
72
|
@write_pages.fill(window, 0xa0, 0x20)
|
|
54
73
|
end
|
|
55
74
|
|
|
75
|
+
# The MAX has no CIA 2 and no I/O 2: CIA 1 answers all through
|
|
76
|
+
# $DC00-$DFFF, but for the I/O 1 page a cartridge takes.
|
|
77
|
+
def map_max_io
|
|
78
|
+
@cia1_mirror ||= Mirror.new(cia1, 0xdc00)
|
|
79
|
+
(0xdd..0xdf).each { |page| @read_pages[page] = @write_pages[page] = @cia1_mirror }
|
|
80
|
+
return unless @cartridge
|
|
81
|
+
|
|
82
|
+
@write_pages[0xde] = @cartridge
|
|
83
|
+
@read_pages[0xde] = @cartridge if @cartridge.readable_io_pages.include?(0xde)
|
|
84
|
+
end
|
|
85
|
+
|
|
56
86
|
# EXROM and GAME select the cartridge whether or not it has a chip
|
|
57
87
|
# there, and an empty socket leaves the bus floating.
|
|
58
88
|
def map_rom_overlays
|
|
@@ -4,18 +4,25 @@ module Badline
|
|
|
4
4
|
class AddressBus
|
|
5
5
|
# The BASIC, character and KERNAL ROMs, and which KERNAL is fitted.
|
|
6
6
|
module ROMs
|
|
7
|
-
# The KERNAL ROMs a machine can be fitted with, by name: the C64's
|
|
8
|
-
#
|
|
9
|
-
|
|
7
|
+
# The KERNAL ROMs a machine can be fitted with, by name: the C64's,
|
|
8
|
+
# the SX-64's, the PET 64's and the C64GS's. The C64GS's is a 16K
|
|
9
|
+
# image of its BASIC and its KERNAL, and fits both.
|
|
10
|
+
KERNALS = {
|
|
11
|
+
c64: "kernal.rom", sx64: "kernal-sx64.rom", pet64: "kernal-pet64.rom", gs: "c64gs.rom"
|
|
12
|
+
}.freeze
|
|
10
13
|
|
|
11
14
|
attr_reader :basic_rom, :character_rom, :kernal_rom, :kernal
|
|
12
15
|
|
|
13
|
-
# Fits the KERNAL ROM KERNALS names `name`, in place of the one before
|
|
16
|
+
# Fits the KERNAL ROM KERNALS names `name`, in place of the one before,
|
|
17
|
+
# with the BASIC of a 16K image or else the C64's.
|
|
14
18
|
def kernal=(name)
|
|
15
19
|
raise ArgumentError, "no KERNAL named #{name}" unless KERNALS.key?(name)
|
|
16
20
|
|
|
17
21
|
@kernal = name
|
|
18
|
-
|
|
22
|
+
data = ROM.read(KERNALS[name])
|
|
23
|
+
basic = data.length == 0x4000 ? data[0, 0x2000] : ROM.read("basic.rom")
|
|
24
|
+
@basic_rom = ROM.new(basic, length: 0x2000, start: 0xa000)
|
|
25
|
+
@kernal_rom = ROM.new(data[-0x2000, 0x2000], length: 0x2000, start: 0xe000)
|
|
19
26
|
update_overlays!
|
|
20
27
|
end
|
|
21
28
|
|
data/lib/badline/address_bus.rb
CHANGED
|
@@ -5,6 +5,7 @@ require "badline/address_bus/sid_slots"
|
|
|
5
5
|
require "badline/address_bus/extra_sids"
|
|
6
6
|
require "badline/address_bus/pla"
|
|
7
7
|
require "badline/address_bus/roms"
|
|
8
|
+
require "badline/address_bus/fittings"
|
|
8
9
|
|
|
9
10
|
module Badline
|
|
10
11
|
# Memory layout:
|
|
@@ -37,6 +38,7 @@ module Badline
|
|
|
37
38
|
include PLA
|
|
38
39
|
include ExtraSIDs
|
|
39
40
|
include ROMs
|
|
41
|
+
include Fittings
|
|
40
42
|
|
|
41
43
|
# I/O 1 and 2, and the Ultimax holes, with nothing on the bus. A read
|
|
42
44
|
# picks up the byte the VIC fetched in the preceding phi1 half-cycle,
|
|
@@ -68,6 +70,7 @@ module Badline
|
|
|
68
70
|
def initialize(sid_model: :mos6581, cia_model: :mos6526, vic_model: :mos6569, region: Region::PAL,
|
|
69
71
|
ram_expansion: nil)
|
|
70
72
|
@region = region
|
|
73
|
+
@board = :c64
|
|
71
74
|
@ram = Memory.new(RAM_POWER_ON, length: 2**16, start: 0)
|
|
72
75
|
@ram_expansion = RAMExpansion.build(ram_expansion, @ram) { update_overlays! }
|
|
73
76
|
@cartridge = @reu = nil
|
|
@@ -201,8 +204,9 @@ module Badline
|
|
|
201
204
|
# so reads and writes dispatch through per-page handler tables instead
|
|
202
205
|
# of range checks.
|
|
203
206
|
def update_overlays!
|
|
204
|
-
|
|
205
|
-
@
|
|
207
|
+
max = @board == :max
|
|
208
|
+
@ultimax = max || (@cartridge ? @cartridge.ultimax? : false)
|
|
209
|
+
@phi1_ultimax = max || (@cartridge ? @cartridge.phi1_ultimax? : false)
|
|
206
210
|
@video_ram = @ram_expansion.video_ram
|
|
207
211
|
@vic.vic_bank.map(@video_ram, phi1_ultimax: @phi1_ultimax, ultimax: @ultimax, romh: @cartridge&.romh)
|
|
208
212
|
|
data/lib/badline/computer.rb
CHANGED
|
@@ -48,13 +48,12 @@ module Badline
|
|
|
48
48
|
# TOD clocks count. The KERNAL tells PAL from NTSC by the raster, so
|
|
49
49
|
# every region boots the same ROMs. reu plugs an REU of that many K
|
|
50
50
|
# into the expansion port, where it drives the IRQ line and takes the
|
|
51
|
-
# bus for its transfers. kernal:
|
|
52
|
-
# (AddressBus::
|
|
53
|
-
# empty
|
|
54
|
-
def initialize(debug: false, reu: nil,
|
|
55
|
-
@address_bus = AddressBus.new(**machine)
|
|
56
|
-
@address_bus.
|
|
57
|
-
@address_bus.datasette.disconnect! unless datasette
|
|
51
|
+
# bus for its transfers. kernal:, datasette: and board: fit the bus
|
|
52
|
+
# afterwards (AddressBus::Fittings): the KERNAL ROM, the cassette port,
|
|
53
|
+
# which is empty on the SX-64, and the board and case.
|
|
54
|
+
def initialize(debug: false, reu: nil, **machine)
|
|
55
|
+
@address_bus = AddressBus.new(**machine.except(*AddressBus::Fittings::NAMES))
|
|
56
|
+
@address_bus.fit(**machine.slice(*AddressBus::Fittings::NAMES))
|
|
58
57
|
@cpu = CPU.new(@address_bus, debug:)
|
|
59
58
|
@vic = @address_bus.vic
|
|
60
59
|
@vic.open_bus = -> { @address_bus.ram.peek(@cpu.program_counter) }
|
|
@@ -195,8 +194,9 @@ module Badline
|
|
|
195
194
|
|
|
196
195
|
# RESTORE isn't in the key matrix. It fires a one-shot that pulses the
|
|
197
196
|
# NMI line however long the key is held, modelled here as one cycle.
|
|
197
|
+
# A machine without a keyboard has no RESTORE key either.
|
|
198
198
|
def press_restore
|
|
199
|
-
@restore_pulse = true
|
|
199
|
+
@restore_pulse = true if keyboard.connected?
|
|
200
200
|
end
|
|
201
201
|
|
|
202
202
|
# The one-shot has pulsed already, so letting go does nothing.
|
data/lib/badline/keyboard.rb
CHANGED
|
@@ -38,10 +38,21 @@ module Badline
|
|
|
38
38
|
@keys = []
|
|
39
39
|
@row_masks = nil
|
|
40
40
|
@matrix = matrix
|
|
41
|
+
@connected = true
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Whether the machine has a keyboard. The C64GS has none: no key goes
|
|
45
|
+
# down on it.
|
|
46
|
+
def connected? = @connected
|
|
47
|
+
|
|
48
|
+
def connected=(connected)
|
|
49
|
+
@connected = connected
|
|
50
|
+
@keys.clear unless connected
|
|
51
|
+
@row_masks = nil
|
|
41
52
|
end
|
|
42
53
|
|
|
43
54
|
def press(key)
|
|
44
|
-
return unless valid_key?(key)
|
|
55
|
+
return unless @connected && valid_key?(key)
|
|
45
56
|
|
|
46
57
|
@keys << key
|
|
47
58
|
@row_masks = nil
|
data/lib/badline/machine.rb
CHANGED
|
@@ -47,7 +47,7 @@ module Badline
|
|
|
47
47
|
profile = Model.named(model)
|
|
48
48
|
Computer.new(vic_model: profile.vic_model, cia_model: profile.cia_model,
|
|
49
49
|
sid_model: sid_model || profile.sid_model, region: profile.region, reu:,
|
|
50
|
-
kernal: profile.kernal, datasette: profile.datasette)
|
|
50
|
+
kernal: profile.kernal, datasette: profile.datasette, board: profile.board)
|
|
51
51
|
end
|
|
52
52
|
|
|
53
53
|
def self.vic20(model, ram)
|
data/lib/badline/model.rb
CHANGED
|
@@ -8,39 +8,56 @@ module Badline
|
|
|
8
8
|
# a datasette. The names are x64sc's.
|
|
9
9
|
module Model
|
|
10
10
|
# Each field but the name is a keyword Computer.new takes.
|
|
11
|
-
Profile = Data.define(:name, :vic_model, :cia_model, :sid_model, :region, :kernal, :datasette)
|
|
11
|
+
Profile = Data.define(:name, :vic_model, :cia_model, :sid_model, :region, :kernal, :datasette, :board)
|
|
12
12
|
|
|
13
13
|
# The breadbin PAL C64: the 6569 VIC-II, 6526 CIAs and the 6581 SID.
|
|
14
14
|
C64 = Profile.new(name: "c64", vic_model: :mos6569, cia_model: :mos6526, sid_model: :mos6581,
|
|
15
|
-
region: Region::PAL, kernal: :c64, datasette: true)
|
|
15
|
+
region: Region::PAL, kernal: :c64, datasette: true, board: :c64)
|
|
16
16
|
|
|
17
17
|
# The PAL C64C: the HMOS 8565 VIC-II, 6526A CIAs and the 8580 SID.
|
|
18
18
|
C64C = Profile.new(name: "c64c", vic_model: :mos8565, cia_model: :mos6526a, sid_model: :mos8580,
|
|
19
|
-
region: Region::PAL, kernal: :c64, datasette: true)
|
|
19
|
+
region: Region::PAL, kernal: :c64, datasette: true, board: :c64)
|
|
20
20
|
|
|
21
21
|
# The NTSC C64 with the 6567R8 VIC-II.
|
|
22
22
|
NTSC = Profile.new(name: "ntsc", vic_model: :mos6569, cia_model: :mos6526, sid_model: :mos6581,
|
|
23
|
-
region: Region::NTSC, kernal: :c64, datasette: true)
|
|
23
|
+
region: Region::NTSC, kernal: :c64, datasette: true, board: :c64)
|
|
24
24
|
|
|
25
25
|
# The NTSC C64C: the 8562, the 6567R8's HMOS successor, with the
|
|
26
26
|
# C64C's CIAs and SID.
|
|
27
27
|
NEW_NTSC = Profile.new(name: "newntsc", vic_model: :mos8565, cia_model: :mos6526a, sid_model: :mos8580,
|
|
28
|
-
region: Region::NTSC, kernal: :c64, datasette: true)
|
|
28
|
+
region: Region::NTSC, kernal: :c64, datasette: true, board: :c64)
|
|
29
29
|
|
|
30
30
|
# The first NTSC C64s, with the 6567R56A VIC-II.
|
|
31
31
|
OLD_NTSC = Profile.new(name: "oldntsc", vic_model: :mos6569, cia_model: :mos6526, sid_model: :mos6581,
|
|
32
|
-
region: Region::NTSC_OLD, kernal: :c64, datasette: true)
|
|
32
|
+
region: Region::NTSC_OLD, kernal: :c64, datasette: true, board: :c64)
|
|
33
33
|
|
|
34
34
|
# The Drean C64 of Argentina, on PAL-N with the 6572 VIC-II.
|
|
35
35
|
DREAN = Profile.new(name: "drean", vic_model: :mos6569, cia_model: :mos6526, sid_model: :mos6581,
|
|
36
|
-
region: Region::DREAN, kernal: :c64, datasette: true)
|
|
36
|
+
region: Region::DREAN, kernal: :c64, datasette: true, board: :c64)
|
|
37
37
|
|
|
38
38
|
# The portable SX-64: a PAL C64's chips with its own KERNAL, a 1541
|
|
39
39
|
# built in as device 8 and no cassette port.
|
|
40
40
|
SX64 = Profile.new(name: "sx64", vic_model: :mos6569, cia_model: :mos6526, sid_model: :mos6581,
|
|
41
|
-
region: Region::PAL, kernal: :sx64, datasette: false)
|
|
41
|
+
region: Region::PAL, kernal: :sx64, datasette: false, board: :c64)
|
|
42
42
|
|
|
43
|
-
|
|
43
|
+
# The PET 64, or Educator 64: a PAL C64's board in a PET's case, with
|
|
44
|
+
# its own KERNAL and a green monochrome monitor built in.
|
|
45
|
+
PET64 = Profile.new(name: "pet64", vic_model: :mos6569, cia_model: :mos6526, sid_model: :mos6581,
|
|
46
|
+
region: Region::PAL, kernal: :pet64, datasette: true, board: :pet64)
|
|
47
|
+
|
|
48
|
+
# The C64GS games console: a PAL C64C's board with its own BASIC and
|
|
49
|
+
# KERNAL, which show an animation asking for a cartridge when it has
|
|
50
|
+
# none, in a case with no keyboard and no cassette port.
|
|
51
|
+
C64GS = Profile.new(name: "c64gs", vic_model: :mos8565, cia_model: :mos6526a, sid_model: :mos8580,
|
|
52
|
+
region: Region::PAL, kernal: :gs, datasette: false, board: :gs)
|
|
53
|
+
|
|
54
|
+
# The MAX Machine, or Ultimax, of Japan: the 6566, an NTSC VIC-II
|
|
55
|
+
# for static RAM, which runs here as the first NTSC C64s' 6567R56A,
|
|
56
|
+
# one CIA, 2K of RAM and no ROMs, so it runs only a cartridge's.
|
|
57
|
+
ULTIMAX = Profile.new(name: "ultimax", vic_model: :mos6569, cia_model: :mos6526, sid_model: :mos6581,
|
|
58
|
+
region: Region::NTSC_OLD, kernal: :c64, datasette: true, board: :max)
|
|
59
|
+
|
|
60
|
+
ALL = [C64, C64C, NTSC, NEW_NTSC, OLD_NTSC, DREAN, SX64, PET64, C64GS, ULTIMAX].freeze
|
|
44
61
|
|
|
45
62
|
def self.named(name)
|
|
46
63
|
model = ALL.find { |candidate| candidate.name == name }
|
|
@@ -49,13 +66,13 @@ module Badline
|
|
|
49
66
|
model
|
|
50
67
|
end
|
|
51
68
|
|
|
52
|
-
# The model whose chips, region, KERNAL and
|
|
53
|
-
# nil for none.
|
|
69
|
+
# The model whose chips, region, KERNAL, datasette and board `machine`
|
|
70
|
+
# has, or nil for none.
|
|
54
71
|
def self.of(machine)
|
|
55
72
|
ALL.find do |model|
|
|
56
73
|
model.vic_model == machine.vic_model && model.cia_model == machine.cia_model &&
|
|
57
74
|
model.sid_model == machine.sid_model && model.region == machine.region &&
|
|
58
|
-
model.kernal == machine.kernal && model.datasette == machine.datasette
|
|
75
|
+
model.kernal == machine.kernal && model.datasette == machine.datasette && model.board == machine.board
|
|
59
76
|
end
|
|
60
77
|
end
|
|
61
78
|
end
|
|
@@ -5,7 +5,7 @@ module Badline
|
|
|
5
5
|
DEFAULT_RATE = 44_100
|
|
6
6
|
|
|
7
7
|
# The names Badline::Model builds, in the order the help lists them.
|
|
8
|
-
MODELS = %w[c64 c64c ntsc newntsc oldntsc drean sx64].freeze
|
|
8
|
+
MODELS = %w[c64 c64c ntsc newntsc oldntsc drean sx64 pet64 c64gs ultimax].freeze
|
|
9
9
|
|
|
10
10
|
# The VIC-20's models, as xvic names its video standards. Only PAL is
|
|
11
11
|
# emulated.
|
data/lib/badline/roms/README
CHANGED
|
@@ -27,6 +27,30 @@ Downloaded from
|
|
|
27
27
|
https://www.zimmers.net/anonftp/pub/cbm/firmware/computers/c64/kernal.sx.251104-04.bin
|
|
28
28
|
SHA-1 aa136e91ecf3c5ac64f696b3dbcbfc5ba0871c98.
|
|
29
29
|
|
|
30
|
+
kernal-pet64.rom
|
|
31
|
+
----------------
|
|
32
|
+
The KERNAL of the PET 64, 4064 and Educator 64, 901246-01. It boots
|
|
33
|
+
with the "**** COMMODORE 4064 BASIC V2.0 ****" banner and a white
|
|
34
|
+
cursor, and its interrupt routine keeps the border, background and
|
|
35
|
+
sprite colours black unless bit 7 of $0286 is set. Computer.new(kernal:
|
|
36
|
+
:pet64) fits it, as --model pet64 does.
|
|
37
|
+
|
|
38
|
+
Downloaded from
|
|
39
|
+
https://www.zimmers.net/anonftp/pub/cbm/firmware/computers/c64/kernal.4064.901246-01.bin
|
|
40
|
+
MD5 da92801e3a03b005b746a4dd0b639c7c.
|
|
41
|
+
|
|
42
|
+
c64gs.rom
|
|
43
|
+
---------
|
|
44
|
+
The C64GS's BASIC and KERNAL, 390852-01, 16 KB: BASIC for $A000-$BFFF
|
|
45
|
+
followed by the KERNAL for $E000-$FFFF. Its BASIC is 901226-01's but
|
|
46
|
+
for 30 unused bytes. Its KERNAL is 901227-03's with most changes in
|
|
47
|
+
$F72C-$FC92, the cassette routines' place, and many jumps redirected to
|
|
48
|
+
I/O error 5. Computer.new(kernal: :gs) fits both, as --model c64gs does.
|
|
49
|
+
|
|
50
|
+
Downloaded from
|
|
51
|
+
https://www.zimmers.net/anonftp/pub/cbm/firmware/computers/c64/64gs.390852-01.bin
|
|
52
|
+
MD5 049e657afafbdd802e8c6a4f88f3d730.
|
|
53
|
+
|
|
30
54
|
dos1541.rom
|
|
31
55
|
-----------
|
|
32
56
|
The 1541 disk drive's DOS 2.6, 16 KB for $C000-$FFFF: 325302-01
|
|
Binary file
|
|
Binary file
|
|
@@ -3,32 +3,36 @@
|
|
|
3
3
|
module Badline
|
|
4
4
|
module Snapshot
|
|
5
5
|
# How a machine was built: its VIC, CIA and SID models, its region, its
|
|
6
|
-
# RAM expansion, the size of its REU, its KERNAL
|
|
7
|
-
# datasette, the arguments Computer.new takes. A State
|
|
8
|
-
# be built first.
|
|
9
|
-
Setup = Data.define(:vic_model, :cia_model, :sid_model, :region, :ram_expansion, :reu, :kernal, :datasette
|
|
6
|
+
# RAM expansion, the size of its REU, its KERNAL, whether it has a
|
|
7
|
+
# datasette and its board, the arguments Computer.new takes. A State
|
|
8
|
+
# starts with them, so a machine to restore it into can be built first.
|
|
9
|
+
Setup = Data.define(:vic_model, :cia_model, :sid_model, :region, :ram_expansion, :reu, :kernal, :datasette,
|
|
10
|
+
:board)
|
|
10
11
|
|
|
11
12
|
class Setup
|
|
12
13
|
SID_MODELS = %i[mos6581 mos8580].freeze
|
|
13
14
|
REGIONS = [Region::PAL, Region::NTSC, Region::NTSC_OLD, Region::DREAN].freeze
|
|
14
15
|
RAM_EXPANSIONS = [:none, *RAMExpansion::TYPES.keys].freeze
|
|
15
16
|
KERNALS = AddressBus::ROMs::KERNALS.keys.freeze
|
|
17
|
+
BOARDS = AddressBus::Fittings::BOARDS
|
|
16
18
|
|
|
17
19
|
def self.of(address_bus)
|
|
18
20
|
new(vic_model: address_bus.vic.model, cia_model: address_bus.cia1.model, sid_model: address_bus.sid.model,
|
|
19
21
|
region: address_bus.region, ram_expansion: address_bus.ram_expansion.type, reu: address_bus.reu&.size_kb,
|
|
20
|
-
kernal: address_bus.kernal, datasette: address_bus.datasette.connected
|
|
22
|
+
kernal: address_bus.kernal, datasette: address_bus.datasette.connected?, board: address_bus.board)
|
|
21
23
|
end
|
|
22
24
|
|
|
23
|
-
# Layout 3 has no KERNAL or datasette, and stands for the C64's
|
|
25
|
+
# Layout 3 has no KERNAL or datasette, and stands for the C64's, and
|
|
26
|
+
# layouts before 6 have no board, and stand for the C64's.
|
|
24
27
|
def self.read(input)
|
|
25
28
|
new(vic_model: VIC::MODELS.fetch(input.int), cia_model: CIA::MODELS.fetch(input.int),
|
|
26
29
|
sid_model: SID_MODELS.fetch(input.int), region: REGIONS.fetch(input.int),
|
|
27
30
|
ram_expansion: ram_expansion(RAM_EXPANSIONS.fetch(input.int)), reu: reu(input.int),
|
|
28
31
|
kernal: input.schema > 3 ? KERNALS.fetch(input.int) : :c64,
|
|
29
|
-
datasette: input.schema > 3 ? input.boolean? : true
|
|
32
|
+
datasette: input.schema > 3 ? input.boolean? : true,
|
|
33
|
+
board: input.schema > 5 ? BOARDS.fetch(input.int) : :c64)
|
|
30
34
|
rescue IndexError
|
|
31
|
-
raise FormatError, "the state names a chip model, region, RAM expansion or
|
|
35
|
+
raise FormatError, "the state names a chip model, region, RAM expansion, KERNAL or board badline doesn't know"
|
|
32
36
|
end
|
|
33
37
|
|
|
34
38
|
def self.ram_expansion(name) = name == :none ? nil : name
|
|
@@ -45,12 +49,12 @@ module Badline
|
|
|
45
49
|
out.int(VIC::MODELS.index(vic_model)).int(CIA::MODELS.index(cia_model))
|
|
46
50
|
out.int(SID_MODELS.index(sid_model)).int(REGIONS.index(region))
|
|
47
51
|
out.int(RAM_EXPANSIONS.index(ram_expansion || :none)).int(reu || 0)
|
|
48
|
-
out.int(KERNALS.index(kernal)).boolean(datasette)
|
|
52
|
+
out.int(KERNALS.index(kernal)).boolean(datasette).int(BOARDS.index(board))
|
|
49
53
|
end
|
|
50
54
|
|
|
51
55
|
# A machine built this way, at power-on.
|
|
52
56
|
def build
|
|
53
|
-
Computer.new(vic_model:, cia_model:, sid_model:, region:, ram_expansion:, reu:, kernal:, datasette:)
|
|
57
|
+
Computer.new(vic_model:, cia_model:, sid_model:, region:, ram_expansion:, reu:, kernal:, datasette:, board:)
|
|
54
58
|
end
|
|
55
59
|
|
|
56
60
|
# The chips, led by the model's name when they make one of
|
|
@@ -63,6 +67,7 @@ module Badline
|
|
|
63
67
|
parts << "a #{reu}K REU" if reu
|
|
64
68
|
parts << "the #{kernal} KERNAL" unless kernal == :c64
|
|
65
69
|
parts << "no datasette" unless datasette
|
|
70
|
+
parts << "the #{board} board" unless board == :c64
|
|
66
71
|
parts.join(", ")
|
|
67
72
|
end
|
|
68
73
|
end
|
|
@@ -14,10 +14,11 @@ module Badline
|
|
|
14
14
|
# The layout the chips write their fields in. It goes up whenever a
|
|
15
15
|
# save_state writes something else, so a State in another layout
|
|
16
16
|
# fails before anything is read into a machine. Layout 3, without the
|
|
17
|
-
# KERNAL and datasette in the setup,
|
|
18
|
-
#
|
|
19
|
-
|
|
20
|
-
|
|
17
|
+
# KERNAL and datasette in the setup, layout 4, without the VIA shift
|
|
18
|
+
# register's clock delay, and layout 5, without the board in the
|
|
19
|
+
# setup, still read.
|
|
20
|
+
SCHEMA = 6
|
|
21
|
+
READABLE = [3, 4, 5, SCHEMA].freeze
|
|
21
22
|
|
|
22
23
|
attr_reader :values, :strings
|
|
23
24
|
|
|
@@ -69,7 +69,8 @@ module Badline
|
|
|
69
69
|
reu = nil unless reu && REU1764.reads?(reu)
|
|
70
70
|
Setup.new(vic_model: model, cia_model: model == :mos8565 ? :mos6526a : :mos6526, sid_model: sid,
|
|
71
71
|
region: vic ? VICII.region(vic) : Region::PAL, ram_expansion: nil,
|
|
72
|
-
reu: reu ? REU1764.size_kb(reu) : nil, kernal: :c64, datasette: true
|
|
72
|
+
reu: reu ? REU1764.size_kb(reu) : nil, kernal: :c64, datasette: true,
|
|
73
|
+
board: :c64)
|
|
73
74
|
end
|
|
74
75
|
end
|
|
75
76
|
end
|
data/lib/badline/version.rb
CHANGED
data/lib/badline/vic/bank.rb
CHANGED
|
@@ -110,7 +110,8 @@ module Badline
|
|
|
110
110
|
private
|
|
111
111
|
|
|
112
112
|
# In Ultimax mode the cartridge ROMH replaces the character ROM
|
|
113
|
-
# shadow, visible at $3000-$3FFF of the window.
|
|
113
|
+
# shadow, visible at $3000-$3FFF of the window. The MAX is in
|
|
114
|
+
# Ultimax mode with no cartridge too.
|
|
114
115
|
def ultimax_peek(offset)
|
|
115
116
|
romh = @romh
|
|
116
117
|
if romh && offset.allbits?(0x3000)
|
data/lib/badline/vic.rb
CHANGED
|
@@ -24,9 +24,24 @@ module Badline
|
|
|
24
24
|
0x8a8a8a, 0xb3ec91, 0x867ade, 0xb3b3b3
|
|
25
25
|
].freeze
|
|
26
26
|
|
|
27
|
+
# The sixteen colours' luminances, out of 32, as Philip Timmermann
|
|
28
|
+
# (Pepto) measured them on the 6569.
|
|
29
|
+
LUMINANCES = [0, 32, 10, 20, 12, 16, 8, 24, 12, 8, 16, 10, 15, 24, 15, 20].freeze
|
|
30
|
+
|
|
31
|
+
# The sixteen colours on a green monochrome monitor, which shows only
|
|
32
|
+
# their luminance, as the PET 64's does.
|
|
33
|
+
GREEN_PALETTE = LUMINANCES.map do |luminance|
|
|
34
|
+
green = luminance * 255 / 32
|
|
35
|
+
((green / 4) << 16) | (green << 8) | (green / 4)
|
|
36
|
+
end.freeze
|
|
37
|
+
|
|
27
38
|
attr_reader :display, :width, :height, :vic_bank, :column,
|
|
28
39
|
:rasterline, :dirty_lines, :model, :region
|
|
29
40
|
|
|
41
|
+
# The RGB colours #display's palette indices stand for: PALETTE, or
|
|
42
|
+
# GREEN_PALETTE on a monochrome monitor.
|
|
43
|
+
attr_accessor :palette
|
|
44
|
+
|
|
30
45
|
# The parts a VICE snapshot reads and sets.
|
|
31
46
|
attr_reader :registers, :display_state, :sequencer, :sprites, :character_buffer, :color_buffer,
|
|
32
47
|
:fetch_d011
|
|
@@ -94,6 +109,7 @@ module Badline
|
|
|
94
109
|
@iie = %i[mos8566 mos8564].include?(model)
|
|
95
110
|
@core = model == :mos6569 ? :mos6569 : :mos8565
|
|
96
111
|
@region = region
|
|
112
|
+
@palette = PALETTE
|
|
97
113
|
@lightpen_extra = @core == :mos8565 ? 1 : 2
|
|
98
114
|
@grey_dots = @core == :mos8565
|
|
99
115
|
@delayed_fetch = @core == :mos8565
|
|
@@ -380,8 +396,6 @@ module Badline
|
|
|
380
396
|
@dirty_lines.fill(false)
|
|
381
397
|
end
|
|
382
398
|
|
|
383
|
-
def palette = PALETTE
|
|
384
|
-
|
|
385
399
|
private
|
|
386
400
|
|
|
387
401
|
def load_beam(input)
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: badline
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.10.
|
|
4
|
+
version: 0.10.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Inge Jørgensen
|
|
@@ -50,6 +50,7 @@ files:
|
|
|
50
50
|
- lib/badline.rb
|
|
51
51
|
- lib/badline/address_bus.rb
|
|
52
52
|
- lib/badline/address_bus/extra_sids.rb
|
|
53
|
+
- lib/badline/address_bus/fittings.rb
|
|
53
54
|
- lib/badline/address_bus/pla.rb
|
|
54
55
|
- lib/badline/address_bus/roms.rb
|
|
55
56
|
- lib/badline/address_bus/saved_state.rb
|
|
@@ -247,11 +248,13 @@ files:
|
|
|
247
248
|
- lib/badline/rom.rb
|
|
248
249
|
- lib/badline/roms/README
|
|
249
250
|
- lib/badline/roms/basic.rom
|
|
251
|
+
- lib/badline/roms/c64gs.rom
|
|
250
252
|
- lib/badline/roms/character.rom
|
|
251
253
|
- lib/badline/roms/dos1541.rom
|
|
252
254
|
- lib/badline/roms/eapi/LICENSE.md
|
|
253
255
|
- lib/badline/roms/eapi/README
|
|
254
256
|
- lib/badline/roms/eapi/eapi-am29f040-14
|
|
257
|
+
- lib/badline/roms/kernal-pet64.rom
|
|
255
258
|
- lib/badline/roms/kernal-sx64.rom
|
|
256
259
|
- lib/badline/roms/kernal.rom
|
|
257
260
|
- lib/badline/roms/vic20/basic.rom
|