badline 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. checksums.yaml +4 -4
  2. data/.claude/hooks/session-start.sh +57 -0
  3. data/.claude/settings.json +14 -0
  4. data/CHANGELOG.md +58 -0
  5. data/CLAUDE.md +45 -16
  6. data/CONTRIBUTING.md +95 -3
  7. data/README.md +111 -61
  8. data/SECURITY.md +9 -0
  9. data/doc/pinned-behaviour.md +275 -33
  10. data/exe/badline-ruby +59 -0
  11. data/lib/badline/address_bus.rb +22 -10
  12. data/lib/badline/audio/aiff.rb +8 -3
  13. data/lib/badline/audio/cli.rb +17 -12
  14. data/lib/badline/audio/console.rb +12 -58
  15. data/lib/badline/audio/playback.rb +3 -0
  16. data/lib/badline/audio/sdl_sink.rb +20 -52
  17. data/lib/badline/audio/terminal.rb +90 -0
  18. data/lib/badline/audio.rb +2 -2
  19. data/lib/badline/cartridge/geo_ram.rb +63 -0
  20. data/lib/badline/cartridge.rb +1 -0
  21. data/lib/badline/cia/interrupt_register.rb +34 -10
  22. data/lib/badline/cia.rb +15 -8
  23. data/lib/badline/computer.rb +28 -13
  24. data/lib/badline/gui/application.rb +33 -21
  25. data/lib/badline/gui/gamepads.rb +22 -22
  26. data/lib/badline/gui/joy_map.rb +1 -1
  27. data/lib/badline/gui/key_map.rb +6 -1
  28. data/lib/badline/gui/pane.rb +7 -6
  29. data/lib/badline/gui/screen_pane.rb +9 -13
  30. data/lib/badline/gui/window.rb +35 -20
  31. data/lib/badline/gui.rb +1 -1
  32. data/lib/badline/input/mouse1351.rb +21 -6
  33. data/lib/badline/input/paddles.rb +11 -6
  34. data/lib/badline/kernal_trap/channel.rb +3 -0
  35. data/lib/badline/kernal_trap/drive/block_commands.rb +33 -4
  36. data/lib/badline/kernal_trap/drive/status.rb +3 -0
  37. data/lib/badline/kernal_trap/drive/write_file.rb +41 -0
  38. data/lib/badline/kernal_trap/drive/writes.rb +92 -0
  39. data/lib/badline/kernal_trap/drive.rb +51 -51
  40. data/lib/badline/kernal_trap/file.rb +6 -9
  41. data/lib/badline/kernal_trap/save.rb +9 -4
  42. data/lib/badline/keyboard.rb +19 -3
  43. data/lib/badline/media.rb +42 -11
  44. data/lib/badline/options.rb +209 -0
  45. data/lib/badline/ram_expansion/banking.rb +30 -0
  46. data/lib/badline/ram_expansion/plus256k.rb +38 -0
  47. data/lib/badline/ram_expansion/plus60k.rb +31 -0
  48. data/lib/badline/ram_expansion/unexpanded.rb +22 -0
  49. data/lib/badline/ram_expansion.rb +21 -0
  50. data/lib/badline/region.rb +51 -0
  51. data/lib/badline/sdl/events.rb +47 -0
  52. data/lib/badline/sdl/functions.rb +73 -0
  53. data/lib/badline/sdl.rb +67 -0
  54. data/lib/badline/sid/waveform/noise_writeback.rb +32 -14
  55. data/lib/badline/sid/waveform.rb +1 -1
  56. data/lib/badline/sid.rb +3 -1
  57. data/lib/badline/storage/d64_image.rb +10 -0
  58. data/lib/badline/storage/d71_image.rb +12 -0
  59. data/lib/badline/storage/d81_image.rb +9 -0
  60. data/lib/badline/storage/disk_image/bam.rb +111 -0
  61. data/lib/badline/storage/disk_image/directory.rb +79 -0
  62. data/lib/badline/storage/disk_image/writing.rb +110 -0
  63. data/lib/badline/storage/disk_image.rb +27 -4
  64. data/lib/badline/storage/host_directory.rb +2 -1
  65. data/lib/badline/storage.rb +15 -0
  66. data/lib/badline/time_of_day.rb +4 -2
  67. data/lib/badline/version.rb +1 -1
  68. data/lib/badline/via/control_lines.rb +104 -0
  69. data/lib/badline/via/interrupt_register.rb +58 -0
  70. data/lib/badline/via/shift_register.rb +135 -0
  71. data/lib/badline/via/timer1.rb +82 -0
  72. data/lib/badline/via/timer2.rb +84 -0
  73. data/lib/badline/via.rb +262 -0
  74. data/lib/badline/vic/bank.rb +4 -3
  75. data/lib/badline/vic/color_patches.rb +6 -3
  76. data/lib/badline/vic/display_state.rb +4 -0
  77. data/lib/badline/vic/graphics_shifter.rb +42 -7
  78. data/lib/badline/vic/registers.rb +3 -0
  79. data/lib/badline/vic/sequencer.rb +18 -18
  80. data/lib/badline/vic/sequencer_output.rb +4 -4
  81. data/lib/badline/vic/sprite/internal_bus.rb +4 -0
  82. data/lib/badline/vic/sprite/shifter.rb +25 -3
  83. data/lib/badline/vic/sprite.rb +5 -4
  84. data/lib/badline/vic/sprites.rb +8 -3
  85. data/lib/badline/vic.rb +93 -44
  86. data/lib/badline.rb +4 -0
  87. data/packaging/homebrew/badline.rb +34 -0
  88. metadata +30 -20
  89. data/exe/badline +0 -78
  90. data/exe/badline-sid +0 -34
  91. data/lib/badline/audio/options.rb +0 -137
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.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Inge Jørgensen
@@ -23,20 +23,6 @@ dependencies:
23
23
  - - "~>"
24
24
  - !ruby/object:Gem::Version
25
25
  version: '1.1'
26
- - !ruby/object:Gem::Dependency
27
- name: ruby-sdl2
28
- requirement: !ruby/object:Gem::Requirement
29
- requirements:
30
- - - "~>"
31
- - !ruby/object:Gem::Version
32
- version: '0.3'
33
- type: :runtime
34
- prerelease: false
35
- version_requirements: !ruby/object:Gem::Requirement
36
- requirements:
37
- - - "~>"
38
- - !ruby/object:Gem::Version
39
- version: '0.3'
40
26
  description: Badline is a Commodore 64 emulator written in Ruby, implementing cycle-accurate
41
27
  timing and hardware behavior. Supports PRG/P00 programs, D64/D71/D81 disk images,
42
28
  T64 archives, TAP tapes, CRT cartridges, SID tunes and host directories mounted
@@ -44,20 +30,21 @@ description: Badline is a Commodore 64 emulator written in Ruby, implementing cy
44
30
  email:
45
31
  - inge@elektronaut.no
46
32
  executables:
47
- - badline
48
- - badline-sid
33
+ - badline-ruby
49
34
  extensions: []
50
35
  extra_rdoc_files: []
51
36
  files:
37
+ - ".claude/hooks/session-start.sh"
38
+ - ".claude/settings.json"
52
39
  - CHANGELOG.md
53
40
  - CLAUDE.md
54
41
  - CODE_OF_CONDUCT.md
55
42
  - CONTRIBUTING.md
56
43
  - MIT-LICENSE
57
44
  - README.md
45
+ - SECURITY.md
58
46
  - doc/pinned-behaviour.md
59
- - exe/badline
60
- - exe/badline-sid
47
+ - exe/badline-ruby
61
48
  - lib/badline.rb
62
49
  - lib/badline/address_bus.rb
63
50
  - lib/badline/addressable.rb
@@ -68,12 +55,12 @@ files:
68
55
  - lib/badline/audio/console.rb
69
56
  - lib/badline/audio/jukebox.rb
70
57
  - lib/badline/audio/machine_player.rb
71
- - lib/badline/audio/options.rb
72
58
  - lib/badline/audio/pcm_writer.rb
73
59
  - lib/badline/audio/playback.rb
74
60
  - lib/badline/audio/renderer.rb
75
61
  - lib/badline/audio/sdl_sink.rb
76
62
  - lib/badline/audio/stream.rb
63
+ - lib/badline/audio/terminal.rb
77
64
  - lib/badline/audio/wav.rb
78
65
  - lib/badline/cartridge.rb
79
66
  - lib/badline/cartridge/action_replay.rb
@@ -89,6 +76,7 @@ files:
89
76
  - lib/badline/cartridge/fun_play.rb
90
77
  - lib/badline/cartridge/g_mod2.rb
91
78
  - lib/badline/cartridge/game_system.rb
79
+ - lib/badline/cartridge/geo_ram.rb
92
80
  - lib/badline/cartridge/kcs_power.rb
93
81
  - lib/badline/cartridge/mach5.rb
94
82
  - lib/badline/cartridge/magic_desk.rb
@@ -152,6 +140,8 @@ files:
152
140
  - lib/badline/kernal_trap/drive/memory.rb
153
141
  - lib/badline/kernal_trap/drive/parameters.rb
154
142
  - lib/badline/kernal_trap/drive/status.rb
143
+ - lib/badline/kernal_trap/drive/write_file.rb
144
+ - lib/badline/kernal_trap/drive/writes.rb
155
145
  - lib/badline/kernal_trap/file.rb
156
146
  - lib/badline/kernal_trap/load.rb
157
147
  - lib/badline/kernal_trap/routine.rb
@@ -161,6 +151,13 @@ files:
161
151
  - lib/badline/keyboard_buffer.rb
162
152
  - lib/badline/media.rb
163
153
  - lib/badline/memory.rb
154
+ - lib/badline/options.rb
155
+ - lib/badline/ram_expansion.rb
156
+ - lib/badline/ram_expansion/banking.rb
157
+ - lib/badline/ram_expansion/plus256k.rb
158
+ - lib/badline/ram_expansion/plus60k.rb
159
+ - lib/badline/ram_expansion/unexpanded.rb
160
+ - lib/badline/region.rb
164
161
  - lib/badline/rom.rb
165
162
  - lib/badline/roms/README
166
163
  - lib/badline/roms/basic.rom
@@ -169,6 +166,9 @@ files:
169
166
  - lib/badline/roms/eapi/README
170
167
  - lib/badline/roms/eapi/eapi-am29f040-14
171
168
  - lib/badline/roms/kernal.rom
169
+ - lib/badline/sdl.rb
170
+ - lib/badline/sdl/events.rb
171
+ - lib/badline/sdl/functions.rb
172
172
  - lib/badline/sid.rb
173
173
  - lib/badline/sid/catch_up.rb
174
174
  - lib/badline/sid/decimator.rb
@@ -186,6 +186,9 @@ files:
186
186
  - lib/badline/storage/d71_image.rb
187
187
  - lib/badline/storage/d81_image.rb
188
188
  - lib/badline/storage/disk_image.rb
189
+ - lib/badline/storage/disk_image/bam.rb
190
+ - lib/badline/storage/disk_image/directory.rb
191
+ - lib/badline/storage/disk_image/writing.rb
189
192
  - lib/badline/storage/host_directory.rb
190
193
  - lib/badline/storage/p00.rb
191
194
  - lib/badline/storage/sid_file.rb
@@ -195,6 +198,12 @@ files:
195
198
  - lib/badline/time_of_day.rb
196
199
  - lib/badline/traps.rb
197
200
  - lib/badline/version.rb
201
+ - lib/badline/via.rb
202
+ - lib/badline/via/control_lines.rb
203
+ - lib/badline/via/interrupt_register.rb
204
+ - lib/badline/via/shift_register.rb
205
+ - lib/badline/via/timer1.rb
206
+ - lib/badline/via/timer2.rb
198
207
  - lib/badline/vic.rb
199
208
  - lib/badline/vic/bank.rb
200
209
  - lib/badline/vic/border_mask.rb
@@ -211,6 +220,7 @@ files:
211
220
  - lib/badline/vic/sprite/internal_bus.rb
212
221
  - lib/badline/vic/sprite/shifter.rb
213
222
  - lib/badline/vic/sprites.rb
223
+ - packaging/homebrew/badline.rb
214
224
  - vendor/.gitkeep
215
225
  homepage: https://github.com/elektronaut/badline
216
226
  licenses:
data/exe/badline DELETED
@@ -1,78 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- lib = File.expand_path("../lib", __dir__)
5
- $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
6
-
7
- require "optparse"
8
-
9
- options = { autostart: true, jit: true, sound: false }
10
-
11
- parser = OptionParser.new do |opts|
12
- opts.banner = <<~BANNER
13
- Usage: badline [options] [media]
14
-
15
- Media can be a .prg/.p00 program, a .d64/.d71/.d81 disk image,
16
- a .t64 tape archive, a .tap tape, a .crt cartridge, a .sid tune, or
17
- a directory to mount as device 8.
18
-
19
- Options:
20
- BANNER
21
-
22
- opts.on("--no-autostart", "Boot to READY. instead of running the program") do
23
- options[:autostart] = false
24
- end
25
-
26
- opts.on("--song N", Integer, "Subtune of a .sid to play (default: the tune's own)") do |n|
27
- options[:song] = n
28
- end
29
-
30
- opts.on("--sid MODEL", { "6581" => :mos6581, "8580" => :mos8580 },
31
- "SID to fit: 6581 or 8580 (default: a .sid tune's own, else 6581)") do |model|
32
- options[:sid_model] = model
33
- end
34
-
35
- opts.on("--sound", "Play the SID through the host's audio device (F10 mutes)") { options[:sound] = true }
36
-
37
- opts.on("--disable-jit", "Run without enabling YJIT") do
38
- options[:jit] = false
39
- end
40
-
41
- opts.on("-h", "--help", "Show this help") do
42
- puts opts
43
- exit
44
- end
45
- end
46
-
47
- begin
48
- parser.parse!
49
- rescue OptionParser::ParseError => e
50
- warn "badline: #{e.message}"
51
- warn "Try 'badline --help' for more information."
52
- exit 1
53
- end
54
-
55
- media_path = ARGV[0]
56
- if media_path && !File.exist?(media_path)
57
- warn "badline: no such file or directory: #{media_path}"
58
- exit 1
59
- end
60
-
61
- jit_available = defined?(RubyVM::YJIT) && !RubyVM::YJIT.enabled?
62
- RubyVM::YJIT.enable if options[:jit] && jit_available
63
-
64
- require "badline"
65
- require "badline/gui"
66
-
67
- begin
68
- application = Badline::GUI::Application.new(media_path:, autostart: options[:autostart],
69
- song: options[:song], sid_model: options[:sid_model],
70
- sound: options[:sound])
71
- rescue Badline::Storage::SIDFile::FormatError, Badline::Storage::T64::FormatError,
72
- Badline::Storage::TAP::FormatError, Badline::Storage::CRTFile::FormatError,
73
- Badline::Cartridge::UnsupportedTypeError => e
74
- warn "badline: #{media_path}: #{e.message}"
75
- exit 1
76
- end
77
-
78
- application.run
data/exe/badline-sid DELETED
@@ -1,34 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- lib = File.expand_path("../lib", __dir__)
5
- $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
6
-
7
- require "badline/audio/options"
8
-
9
- begin
10
- options = Badline::Audio::Options.parse(ARGV)
11
- rescue Badline::Audio::Options::Error => e
12
- warn "badline-sid: #{e.message}"
13
- warn "Try 'badline-sid --help' for more information."
14
- exit 1
15
- end
16
-
17
- if options.help?
18
- puts options.help
19
- exit
20
- end
21
-
22
- jit_available = defined?(RubyVM::YJIT) && !RubyVM::YJIT.enabled?
23
- RubyVM::YJIT.enable if options.jit? && jit_available
24
-
25
- require "badline"
26
-
27
- begin
28
- Badline::Audio::CLI.new(options).run
29
- rescue Badline::Audio::CLI::Error => e
30
- warn "badline-sid: #{e.message}"
31
- exit 1
32
- rescue Interrupt
33
- exit 130
34
- end
@@ -1,137 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "optparse"
4
-
5
- module Badline
6
- module Audio
7
- # The command line of `badline-sid`. It loads nothing else from badline,
8
- # so the executable can parse and reject its arguments before paying for
9
- # the emulator.
10
- class Options
11
- class Error < StandardError; end
12
-
13
- FALLBACK_SECONDS = 60.0
14
-
15
- DEFAULT_RATE = 44_100
16
-
17
- SID_MODELS = { "6581" => :mos6581, "8580" => :mos8580 }.freeze
18
-
19
- BANNER = <<~BANNER.freeze
20
- Usage: badline-sid [options] tune.sid [-o output]
21
-
22
- Plays a .sid tune on the host's audio device, or with an output file
23
- renders it to 16-bit PCM instead. The container follows the output
24
- file's extension, .wav or .aiff.
25
-
26
- A .sid file carries no length of its own. Without --seconds the tune is
27
- looked up by MD5 in HVSC's Songlengths.md5, taken from --songlengths, from
28
- the DOCUMENTS directory of an HVSC collection above the tune, or from
29
- $HVSC_BASE. Failing all of those it runs for #{FALLBACK_SECONDS.to_i} seconds.
30
-
31
- Played on a terminal, n or → skips to the next song, p or ← to the
32
- previous one, space pauses and q quits.
33
-
34
- PSID tunes run on a bare CPU and SID, faster than real time. RSID
35
- tunes boot the whole machine and run at about half real time, so
36
- they stutter when played. --filter-chunk 1 runs the filter cycle by
37
- cycle, which is exact but takes about twice as long.
38
-
39
- Options:
40
- BANNER
41
-
42
- attr_reader :tune_path, :output, :song, :seconds, :songlengths, :sid_model, :filter_chunk
43
-
44
- def self.parse(argv) = new.parse(argv)
45
-
46
- def initialize
47
- @jit = true
48
- @quiet = false
49
- @tui = true
50
- @help = false
51
- end
52
-
53
- def parse(argv)
54
- arguments = parser.parse(argv)
55
- @tune_path, positional_output, *extra = arguments
56
- raise Error, "unexpected argument: #{extra.first}" if extra.any?
57
-
58
- @output ||= positional_output
59
- validate unless help?
60
- self
61
- rescue OptionParser::ParseError => e
62
- raise Error, e.message
63
- end
64
-
65
- def render? = !output.nil?
66
-
67
- def rate = @rate || DEFAULT_RATE
68
-
69
- def rate_given? = !@rate.nil?
70
-
71
- def help = parser.help
72
-
73
- def help? = @help
74
-
75
- def quiet? = @quiet
76
-
77
- def tui? = @tui
78
-
79
- def jit? = @jit
80
-
81
- private
82
-
83
- def validate
84
- raise Error, "no tune given" unless tune_path
85
-
86
- validate_numbers
87
- validate_paths
88
- end
89
-
90
- def validate_numbers
91
- raise Error, "invalid argument: --song #{song}" if song&.< 1
92
- raise Error, "invalid argument: --seconds #{seconds}" unless seconds.nil? || seconds.positive?
93
- raise Error, "invalid argument: --rate #{rate}" unless rate.positive?
94
- raise Error, "invalid argument: --filter-chunk #{filter_chunk}" if filter_chunk&.< 1
95
- end
96
-
97
- def validate_paths
98
- [tune_path, songlengths].compact.each do |path|
99
- raise Error, "no such file: #{path}" unless File.exist?(path)
100
- end
101
- end
102
-
103
- def parser
104
- @parser ||= OptionParser.new do |opts|
105
- opts.banner = BANNER
106
- define_tune_options(opts)
107
- define_output_options(opts)
108
- define_run_options(opts)
109
- end
110
- end
111
-
112
- def define_tune_options(opts)
113
- opts.on("-s", "--song N", Integer, "Subtune to play, from 1 (default: the tune's own)") { |n| @song = n }
114
- opts.on("--seconds N", Float, "Length to play (default: the tune's own)") { |n| @seconds = n }
115
- opts.on("--songlengths PATH", "HVSC Songlengths.md5 to take the length from") { |path| @songlengths = path }
116
- opts.on("--sid MODEL", SID_MODELS,
117
- "SID to play on: 6581 or 8580 (default: the tune's own, else 6581)") { |model| @sid_model = model }
118
- end
119
-
120
- def define_output_options(opts)
121
- opts.on("-o", "--output PATH", "Render to a .wav or .aiff file instead of playing") { |path| @output = path }
122
- opts.on("--rate HZ", Integer,
123
- "Sample rate (default: #{DEFAULT_RATE}, or the audio device's own)") { |n| @rate = n }
124
- opts.on("--filter-chunk N", Integer, "Filter step in cycles, 1 is exact (default: 4)") do |n|
125
- @filter_chunk = n
126
- end
127
- end
128
-
129
- def define_run_options(opts)
130
- opts.on("--quiet", "Don't report progress") { @quiet = true }
131
- opts.on("--no-tui", "Play without the interactive display, even on a terminal") { @tui = false }
132
- opts.on("--disable-jit", "Run without enabling YJIT") { @jit = false }
133
- opts.on("-h", "--help", "Show this help") { @help = true }
134
- end
135
- end
136
- end
137
- end