badline 0.5.1 → 0.6.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.
Files changed (173) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +52 -0
  3. data/CLAUDE.md +10 -9
  4. data/README.md +82 -10
  5. data/doc/pinned-behaviour.md +131 -30
  6. data/exe/badline-ruby +20 -23
  7. data/lib/badline/address_bus/saved_state.rb +72 -0
  8. data/lib/badline/address_bus.rb +11 -1
  9. data/lib/badline/audio/cli.rb +18 -5
  10. data/lib/badline/audio/console.rb +2 -1
  11. data/lib/badline/audio.rb +0 -3
  12. data/lib/badline/cartridge/action_replay.rb +15 -0
  13. data/lib/badline/cartridge/comal80.rb +8 -0
  14. data/lib/badline/cartridge/dinamic.rb +8 -0
  15. data/lib/badline/cartridge/easy_flash.rb +20 -1
  16. data/lib/badline/cartridge/epyx_fastload.rb +8 -0
  17. data/lib/badline/cartridge/final_cartridge3.rb +12 -0
  18. data/lib/badline/cartridge/flash.rb +41 -0
  19. data/lib/badline/cartridge/fun_play.rb +8 -0
  20. data/lib/badline/cartridge/g_mod2.rb +12 -0
  21. data/lib/badline/cartridge/game_system.rb +8 -0
  22. data/lib/badline/cartridge/geo_ram.rb +16 -0
  23. data/lib/badline/cartridge/magic_desk.rb +8 -0
  24. data/lib/badline/cartridge/ocean.rb +8 -0
  25. data/lib/badline/cartridge/pagefox.rb +12 -0
  26. data/lib/badline/cartridge/retro_replay.rb +44 -1
  27. data/lib/badline/cartridge/rgcd.rb +10 -0
  28. data/lib/badline/cartridge/super_games.rb +10 -0
  29. data/lib/badline/cartridge/zaxxon.rb +16 -0
  30. data/lib/badline/cartridge.rb +72 -0
  31. data/lib/badline/cia/interrupt_register.rb +17 -0
  32. data/lib/badline/cia/serial.rb +44 -0
  33. data/lib/badline/cia/state.rb +47 -0
  34. data/lib/badline/cia/timer.rb +33 -0
  35. data/lib/badline/cia.rb +8 -0
  36. data/lib/badline/computer/attachments.rb +132 -0
  37. data/lib/badline/computer/saved_state.rb +116 -0
  38. data/lib/badline/computer.rb +13 -33
  39. data/lib/badline/cpu/boundary.rb +54 -0
  40. data/lib/badline/cpu/core.rb +165 -0
  41. data/lib/badline/cpu/microcode.rb +8 -0
  42. data/lib/badline/cpu/saved_state.rb +115 -0
  43. data/lib/badline/cpu.rb +3 -212
  44. data/lib/badline/datasette.rb +27 -0
  45. data/lib/badline/drive1541/bus.rb +13 -0
  46. data/lib/badline/drive1541/cpu.rb +29 -0
  47. data/lib/badline/drive1541/disk.rb +7 -2
  48. data/lib/badline/drive1541/disk_state.rb +80 -0
  49. data/lib/badline/drive1541/mechanism.rb +2 -0
  50. data/lib/badline/drive1541/mechanism_state.rb +67 -0
  51. data/lib/badline/drive1541/rotation.rb +10 -0
  52. data/lib/badline/drive1541/serial_port.rb +10 -0
  53. data/lib/badline/drive1541.rb +35 -0
  54. data/lib/badline/ffi.rb +157 -0
  55. data/lib/badline/frontend/app.rb +225 -0
  56. data/lib/badline/frontend/audio_sink.rb +72 -0
  57. data/lib/badline/frontend/boot.rb +52 -0
  58. data/lib/badline/frontend/controls.rb +163 -0
  59. data/lib/badline/frontend/drive_led.rb +51 -0
  60. data/lib/badline/frontend/frame_rate.rb +83 -0
  61. data/lib/badline/frontend/gamepads.rb +76 -0
  62. data/lib/badline/frontend/keys.rb +48 -0
  63. data/lib/badline/frontend/pacer.rb +100 -0
  64. data/lib/badline/frontend/pad_port.rb +61 -0
  65. data/lib/badline/frontend/screen.rb +65 -0
  66. data/lib/badline/frontend/screenshot.rb +22 -0
  67. data/lib/badline/frontend/snapshots.rb +60 -0
  68. data/lib/badline/frontend/sound.rb +153 -0
  69. data/lib/badline/frontend/timeline.rb +171 -0
  70. data/lib/badline/frontend.rb +18 -0
  71. data/lib/badline/kernal_trap/channel.rb +27 -0
  72. data/lib/badline/kernal_trap/drive/channels.rb +25 -0
  73. data/lib/badline/kernal_trap/drive/memory.rb +9 -0
  74. data/lib/badline/kernal_trap/drive/status.rb +8 -0
  75. data/lib/badline/kernal_trap/drive/write_file.rb +21 -0
  76. data/lib/badline/kernal_trap/drive.rb +19 -0
  77. data/lib/badline/kernal_trap/save.rb +8 -0
  78. data/lib/badline/kernal_trap/serial.rb +16 -0
  79. data/lib/badline/memory.rb +8 -0
  80. data/lib/badline/options/event.rb +94 -0
  81. data/lib/badline/options/help.rb +78 -0
  82. data/lib/badline/options/option.rb +29 -0
  83. data/lib/badline/options/table.rb +62 -0
  84. data/lib/badline/options.rb +181 -144
  85. data/lib/badline/ram_expansion/plus256k.rb +16 -0
  86. data/lib/badline/ram_expansion/plus60k.rb +12 -0
  87. data/lib/badline/ram_expansion/unexpanded.rb +6 -0
  88. data/lib/badline/reu/dma/saved_state.rb +50 -0
  89. data/lib/badline/reu/dma.rb +56 -11
  90. data/lib/badline/reu/ram.rb +15 -0
  91. data/lib/badline/reu/saved_state.rb +53 -0
  92. data/lib/badline/reu.rb +23 -10
  93. data/lib/badline/sdl.rb +123 -51
  94. data/lib/badline/sid/envelope/state.rb +68 -0
  95. data/lib/badline/sid/envelope.rb +7 -0
  96. data/lib/badline/sid/filter.rb +38 -0
  97. data/lib/badline/sid/voice.rb +10 -0
  98. data/lib/badline/sid/waveform/noise_writeback.rb +33 -11
  99. data/lib/badline/sid/waveform/state.rb +66 -0
  100. data/lib/badline/sid/waveform.rb +4 -3
  101. data/lib/badline/sid.rb +34 -0
  102. data/lib/badline/snapshot/container.rb +117 -0
  103. data/lib/badline/snapshot/fields.rb +54 -0
  104. data/lib/badline/snapshot/image.rb +70 -0
  105. data/lib/badline/snapshot/machine_state.rb +77 -0
  106. data/lib/badline/snapshot/setup.rb +58 -0
  107. data/lib/badline/snapshot/state.rb +178 -0
  108. data/lib/badline/snapshot/vice/c64_mem.rb +53 -0
  109. data/lib/badline/snapshot/vice/cias.rb +89 -0
  110. data/lib/badline/snapshot/vice/cias_import.rb +70 -0
  111. data/lib/badline/snapshot/vice/main_cpu.rb +76 -0
  112. data/lib/badline/snapshot/vice/peripherals.rb +47 -0
  113. data/lib/badline/snapshot/vice/restore.rb +125 -0
  114. data/lib/badline/snapshot/vice/sid_extended.rb +96 -0
  115. data/lib/badline/snapshot/vice/sid_registers.rb +46 -0
  116. data/lib/badline/snapshot/vice/vicii.rb +119 -0
  117. data/lib/badline/snapshot/vice/vicii_import.rb +131 -0
  118. data/lib/badline/snapshot/vice.rb +74 -0
  119. data/lib/badline/snapshot.rb +52 -0
  120. data/lib/badline/storage/crt_file.rb +9 -3
  121. data/lib/badline/storage/d64_image.rb +2 -0
  122. data/lib/badline/storage/d71_image.rb +2 -0
  123. data/lib/badline/storage/d81_image.rb +2 -0
  124. data/lib/badline/storage/disk_image.rb +11 -2
  125. data/lib/badline/storage/host_directory.rb +5 -0
  126. data/lib/badline/storage/t64.rb +6 -0
  127. data/lib/badline/storage/tap.rb +10 -1
  128. data/lib/badline/storage/unavailable.rb +20 -0
  129. data/lib/badline/storage.rb +34 -0
  130. data/lib/badline/time_of_day.rb +56 -0
  131. data/lib/badline/version.rb +1 -1
  132. data/lib/badline/via/control_lines.rb +15 -0
  133. data/lib/badline/via/interrupt_register.rb +9 -0
  134. data/lib/badline/via/shift_register.rb +13 -0
  135. data/lib/badline/via/state.rb +42 -0
  136. data/lib/badline/via/timer1.rb +13 -0
  137. data/lib/badline/via/timer2.rb +18 -0
  138. data/lib/badline/via.rb +2 -0
  139. data/lib/badline/vic/bank.rb +21 -0
  140. data/lib/badline/vic/border_mask.rb +10 -0
  141. data/lib/badline/vic/collisions.rb +11 -0
  142. data/lib/badline/vic/color_patches.rb +11 -0
  143. data/lib/badline/vic/display_state.rb +29 -1
  144. data/lib/badline/vic/graphics_shifter.rb +26 -0
  145. data/lib/badline/vic/register_log.rb +18 -0
  146. data/lib/badline/vic/registers.rb +13 -2
  147. data/lib/badline/vic/sequencer.rb +12 -0
  148. data/lib/badline/vic/sequencer_state.rb +89 -0
  149. data/lib/badline/vic/sprite/internal_bus.rb +11 -0
  150. data/lib/badline/vic/sprite/saved_state.rb +64 -0
  151. data/lib/badline/vic/sprite.rb +2 -0
  152. data/lib/badline/vic/sprites.rb +3 -0
  153. data/lib/badline/vic/sprites_state.rb +57 -0
  154. data/lib/badline/vic.rb +109 -16
  155. data/lib/badline.rb +1 -0
  156. metadata +59 -18
  157. data/.claude/hooks/session-start.sh +0 -57
  158. data/.claude/settings.json +0 -14
  159. data/lib/badline/audio/sdl_sink.rb +0 -70
  160. data/lib/badline/audio/stream.rb +0 -117
  161. data/lib/badline/gui/application.rb +0 -216
  162. data/lib/badline/gui/drive_led_pane.rb +0 -29
  163. data/lib/badline/gui/gamepads.rb +0 -97
  164. data/lib/badline/gui/joy_map.rb +0 -27
  165. data/lib/badline/gui/key_map.rb +0 -39
  166. data/lib/badline/gui/palette.rb +0 -35
  167. data/lib/badline/gui/pane.rb +0 -36
  168. data/lib/badline/gui/screen_pane.rb +0 -46
  169. data/lib/badline/gui/window.rb +0 -61
  170. data/lib/badline/gui.rb +0 -13
  171. data/lib/badline/sdl/events.rb +0 -47
  172. data/lib/badline/sdl/functions.rb +0 -74
  173. data/vendor/.gitkeep +0 -0
data/lib/badline/vic.rb CHANGED
@@ -17,6 +17,10 @@ module Badline
17
17
  attr_reader :address_bus, :display, :width, :height, :vic_bank, :column,
18
18
  :rasterline, :dirty_lines, :model, :region
19
19
 
20
+ # The parts a VICE snapshot reads and sets.
21
+ attr_reader :registers, :display_state, :sequencer, :sprites, :character_buffer, :color_buffer,
22
+ :fetch_d011
23
+
20
24
  # Returns the byte the CPU's halted read would see, for the c-accesses
21
25
  # that run before AEC. Without it they read colour RAM.
22
26
  attr_writer :open_bus
@@ -82,6 +86,7 @@ module Badline
82
86
  @grey_dots = model == :mos8565
83
87
  @delayed_fetch = model == :mos8565
84
88
  @dma_delay_idle = model == :mos6569
89
+ @bank_swaps = model == :mos8565
85
90
  @address_bus = address_bus || AddressBus.new
86
91
  @vic_bank = VIC::Bank.new(@address_bus)
87
92
  @debug = debug
@@ -139,6 +144,36 @@ module Badline
139
144
  @raster_match = false
140
145
  end
141
146
 
147
+ # Everything the VIC holds between cycles: the beam, the fetch and
148
+ # g-access pipeline, the light pen and raster latches, the finished
149
+ # lines, the registers and the display, sequencer and sprite logic.
150
+ # Whether it renders is the host's, and every line comes back dirty so
151
+ # a front end repaints.
152
+ def save_state(out)
153
+ out.marker("VIC")
154
+ out.int(@column).int(@rasterline).int(@cycles).boolean(@pending_write)
155
+ out.int(@g_tick).ints(@g_kind).int(@g_kept_char).int(@g_kept_color).boolean(@g_display)
156
+ out.int(@fetch_d011).boolean(@lp_triggered).boolean(@lp_low).boolean(@raster_match)
157
+ out.int(@vic_bank.lines)
158
+ out.blob(@character_buffer).blob(@color_buffer).booleans(@sprite_ba)
159
+ @lines.each { |line| out.blob(line) }
160
+ @registers.save_state(out)
161
+ @display_state.save_state(out)
162
+ @sequencer.save_state(out)
163
+ @sprites.save_state(out)
164
+ end
165
+
166
+ def load_state(input)
167
+ input.marker("VIC")
168
+ load_beam(input)
169
+ load_buffers(input)
170
+ load_lines(input)
171
+ @registers.load_state(input)
172
+ @display_state.load_state(input)
173
+ @sequencer.load_state(input)
174
+ @sprites.load_state(input)
175
+ end
176
+
142
177
  def cycle!
143
178
  start_line! if @column.zero?
144
179
 
@@ -227,14 +262,19 @@ module Badline
227
262
  @column > DisplayState::DMA_FIRST && @column <= DisplayState::DMA_LAST + 1
228
263
  end
229
264
 
230
- # BA as an REU's DMA sees it. On the line whose raster matches sprite
231
- # 0's Y, where the sprite's DMA starts, the REU doesn't see BA fall on
232
- # the first cycle of the sprite's window. Pinned by
265
+ # Whether this is the first cycle of sprite 0's BA window on the line
266
+ # whose raster matches its Y, where the sprite's DMA starts. An REU
267
+ # that moved a byte on the cycle before doesn't see BA fall here. Pinned by
233
268
  # REU/bonzai/spritetiming.
234
- def reu_ba_low?
235
- return false if @column == SPRITE_BA_FIRST + @sprite_shift && sprite_zero_starting?
269
+ def reu_ba_late?
270
+ @column == SPRITE_BA_FIRST + @sprite_shift && sprite_zero_starting?
271
+ end
236
272
 
237
- ba_low?
273
+ # Whether BA passes straight from a bad line's window to sprite 0's on
274
+ # this cycle, the first of sprite 0's. An REU sees the bad line's DMA
275
+ # end here. Pinned by REU/reutiming2/e4-m2 and e6-m2.
276
+ def reu_ba_handed_on?
277
+ @column == DisplayState::DMA_LAST + 2 && @sprite_ba[@column] && @display_state.bad_line_condition?
238
278
  end
239
279
 
240
280
  # Light pen input level (CIA1 PB4). A falling edge triggers the latch.
@@ -288,6 +328,36 @@ module Badline
288
328
  @sequencer.render = on
289
329
  end
290
330
 
331
+ # Puts the beam at the start of `line`.
332
+ def restore_line(line)
333
+ @rasterline = line
334
+ @column = 0
335
+ end
336
+
337
+ # The latches a VICE snapshot holds: the raster match, the light pen's
338
+ # line held low and its trigger, as bits 0, 1 and 2.
339
+ def latch_bits = (@raster_match ? 1 : 0) | (@lp_low ? 2 : 0) | (@lp_triggered ? 4 : 0)
340
+
341
+ # Sets the latches from latch_bits, and the $D011 the next g-access
342
+ # holds.
343
+ def restore_latches(bits, fetch_d011)
344
+ @raster_match = bits.anybits?(1)
345
+ @lp_low = bits.anybits?(2)
346
+ @lp_triggered = bits.anybits?(4)
347
+ @fetch_d011 = fetch_d011
348
+ end
349
+
350
+ # Marks the columns BA is low for the sprites whose DMA runs.
351
+ def rebuild_sprite_ba
352
+ @sprite_ba.fill(false)
353
+ 8.times do |n|
354
+ next unless @sprites[n].displaying?
355
+
356
+ @sprite_ba_tail[n].each { |c| @sprite_ba[c] = true }
357
+ @sprite_ba_head[n].each { |c| @sprite_ba[c] = true }
358
+ end
359
+ end
360
+
291
361
  # Reset the dirty flags once the frontend has consumed them.
292
362
  def clear_dirty_lines!
293
363
  @dirty_lines.fill(false)
@@ -295,6 +365,38 @@ module Badline
295
365
 
296
366
  private
297
367
 
368
+ def load_beam(input)
369
+ @column = input.int
370
+ @rasterline = input.int
371
+ @cycles = input.int
372
+ @pending_write = input.boolean?
373
+ @g_tick = input.int
374
+ input.ints_into(@g_kind)
375
+ @g_kept_char = input.int
376
+ @g_kept_color = input.int
377
+ @g_display = input.boolean?
378
+ @fetch_d011 = input.int
379
+ @lp_triggered = input.boolean?
380
+ @lp_low = input.boolean?
381
+ @raster_match = input.boolean?
382
+ @vic_bank.lines = input.int
383
+ end
384
+
385
+ def load_buffers(input)
386
+ input.blob_into(@character_buffer)
387
+ input.blob_into(@color_buffer)
388
+ input.booleans_into(@sprite_ba)
389
+ end
390
+
391
+ # The display is the finished lines one after another.
392
+ def load_lines(input)
393
+ @lines.each_with_index do |line, number|
394
+ input.blob_into(line)
395
+ @display[number * @width, @width] = line
396
+ end
397
+ @dirty_lines.fill(true)
398
+ end
399
+
298
400
  # The raster counter as $D011/$D012 read it. Every line advances on the
299
401
  # CPU cycle paired with column 62 except line 0, which the counter
300
402
  # reaches a cycle later, together with the line 0 raster compare.
@@ -407,16 +509,6 @@ module Badline
407
509
  @registers[0x15].anybits?(0x01) && @registers[0x01] == (@rasterline & 0xff)
408
510
  end
409
511
 
410
- def rebuild_sprite_ba
411
- @sprite_ba.fill(false)
412
- 8.times do |n|
413
- next unless @sprites[n].displaying?
414
-
415
- @sprite_ba_tail[n].each { |c| @sprite_ba[c] = true }
416
- @sprite_ba_head[n].each { |c| @sprite_ba[c] = true }
417
- end
418
- end
419
-
420
512
  # Splits the sprite BA windows at the end of the line and marks the hook
421
513
  # columns, for a line of the region's length: 14, 15, 53, 54 and 57, and
422
514
  # the line's last column, which starts the vertical border for the line
@@ -452,6 +544,7 @@ module Badline
452
544
  # on zero data with the screen byte and colour the last g-access
453
545
  # latched, which an idle g-access clears.
454
546
  def draw!
547
+ @vic_bank.sample_lines if @bank_swaps
455
548
  slot = @g_tick = (@g_tick + 1) & 3
456
549
  display_state = @display_state
457
550
  access = display_state.graphics_column?(@column) && !@sequencer.vertical_closed?
data/lib/badline.rb CHANGED
@@ -39,4 +39,5 @@ require "badline/cartridge"
39
39
  require "badline/kernal_trap"
40
40
  require "badline/chrout_trap"
41
41
  require "badline/media"
42
+ require "badline/snapshot"
42
43
  require "badline/audio"
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.5.1
4
+ version: 0.6.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Inge Jørgensen
@@ -34,8 +34,6 @@ executables:
34
34
  extensions: []
35
35
  extra_rdoc_files: []
36
36
  files:
37
- - ".claude/hooks/session-start.sh"
38
- - ".claude/settings.json"
39
37
  - CHANGELOG.md
40
38
  - CLAUDE.md
41
39
  - CODE_OF_CONDUCT.md
@@ -47,6 +45,7 @@ files:
47
45
  - exe/badline-ruby
48
46
  - lib/badline.rb
49
47
  - lib/badline/address_bus.rb
48
+ - lib/badline/address_bus/saved_state.rb
50
49
  - lib/badline/address_bus/ultimax_pages.rb
51
50
  - lib/badline/addressable.rb
52
51
  - lib/badline/audio.rb
@@ -59,8 +58,6 @@ files:
59
58
  - lib/badline/audio/pcm_writer.rb
60
59
  - lib/badline/audio/playback.rb
61
60
  - lib/badline/audio/renderer.rb
62
- - lib/badline/audio/sdl_sink.rb
63
- - lib/badline/audio/stream.rb
64
61
  - lib/badline/audio/terminal.rb
65
62
  - lib/badline/audio/wav.rb
66
63
  - lib/badline/cartridge.rb
@@ -96,41 +93,57 @@ files:
96
93
  - lib/badline/cia.rb
97
94
  - lib/badline/cia/interrupt_register.rb
98
95
  - lib/badline/cia/serial.rb
96
+ - lib/badline/cia/state.rb
99
97
  - lib/badline/cia/timer.rb
100
98
  - lib/badline/color_memory.rb
101
99
  - lib/badline/computer.rb
100
+ - lib/badline/computer/attachments.rb
101
+ - lib/badline/computer/saved_state.rb
102
102
  - lib/badline/control_ports.rb
103
103
  - lib/badline/cpu.rb
104
104
  - lib/badline/cpu/addressing.rb
105
+ - lib/badline/cpu/boundary.rb
106
+ - lib/badline/cpu/core.rb
105
107
  - lib/badline/cpu/microcode.rb
106
108
  - lib/badline/cpu/operations.rb
109
+ - lib/badline/cpu/saved_state.rb
107
110
  - lib/badline/cpu/stack_operations.rb
108
111
  - lib/badline/cycleable.rb
109
112
  - lib/badline/datasette.rb
110
113
  - lib/badline/debug_register.rb
111
114
  - lib/badline/drive1541.rb
112
115
  - lib/badline/drive1541/bus.rb
116
+ - lib/badline/drive1541/cpu.rb
113
117
  - lib/badline/drive1541/disk.rb
118
+ - lib/badline/drive1541/disk_state.rb
114
119
  - lib/badline/drive1541/disk_via.rb
115
120
  - lib/badline/drive1541/gcr.rb
116
121
  - lib/badline/drive1541/idle.rb
117
122
  - lib/badline/drive1541/mechanism.rb
123
+ - lib/badline/drive1541/mechanism_state.rb
118
124
  - lib/badline/drive1541/orbit.rb
119
125
  - lib/badline/drive1541/rotation.rb
120
126
  - lib/badline/drive1541/sector_reader.rb
121
127
  - lib/badline/drive1541/serial_port.rb
122
128
  - lib/badline/drive1541/sleep.rb
123
129
  - lib/badline/drive1541/track.rb
124
- - lib/badline/gui.rb
125
- - lib/badline/gui/application.rb
126
- - lib/badline/gui/drive_led_pane.rb
127
- - lib/badline/gui/gamepads.rb
128
- - lib/badline/gui/joy_map.rb
129
- - lib/badline/gui/key_map.rb
130
- - lib/badline/gui/palette.rb
131
- - lib/badline/gui/pane.rb
132
- - lib/badline/gui/screen_pane.rb
133
- - lib/badline/gui/window.rb
130
+ - lib/badline/ffi.rb
131
+ - lib/badline/frontend.rb
132
+ - lib/badline/frontend/app.rb
133
+ - lib/badline/frontend/audio_sink.rb
134
+ - lib/badline/frontend/boot.rb
135
+ - lib/badline/frontend/controls.rb
136
+ - lib/badline/frontend/drive_led.rb
137
+ - lib/badline/frontend/frame_rate.rb
138
+ - lib/badline/frontend/gamepads.rb
139
+ - lib/badline/frontend/keys.rb
140
+ - lib/badline/frontend/pacer.rb
141
+ - lib/badline/frontend/pad_port.rb
142
+ - lib/badline/frontend/screen.rb
143
+ - lib/badline/frontend/screenshot.rb
144
+ - lib/badline/frontend/snapshots.rb
145
+ - lib/badline/frontend/sound.rb
146
+ - lib/badline/frontend/timeline.rb
134
147
  - lib/badline/iec_bus.rb
135
148
  - lib/badline/input.rb
136
149
  - lib/badline/input/mouse1351.rb
@@ -169,6 +182,10 @@ files:
169
182
  - lib/badline/media/true_drive.rb
170
183
  - lib/badline/memory.rb
171
184
  - lib/badline/options.rb
185
+ - lib/badline/options/event.rb
186
+ - lib/badline/options/help.rb
187
+ - lib/badline/options/option.rb
188
+ - lib/badline/options/table.rb
172
189
  - lib/badline/ram_expansion.rb
173
190
  - lib/badline/ram_expansion/banking.rb
174
191
  - lib/badline/ram_expansion/plus256k.rb
@@ -177,7 +194,9 @@ files:
177
194
  - lib/badline/region.rb
178
195
  - lib/badline/reu.rb
179
196
  - lib/badline/reu/dma.rb
197
+ - lib/badline/reu/dma/saved_state.rb
180
198
  - lib/badline/reu/ram.rb
199
+ - lib/badline/reu/saved_state.rb
181
200
  - lib/badline/reu/trigger.rb
182
201
  - lib/badline/rom.rb
183
202
  - lib/badline/roms/README
@@ -189,18 +208,36 @@ files:
189
208
  - lib/badline/roms/eapi/eapi-am29f040-14
190
209
  - lib/badline/roms/kernal.rom
191
210
  - lib/badline/sdl.rb
192
- - lib/badline/sdl/events.rb
193
- - lib/badline/sdl/functions.rb
194
211
  - lib/badline/sid.rb
195
212
  - lib/badline/sid/catch_up.rb
196
213
  - lib/badline/sid/decimator.rb
197
214
  - lib/badline/sid/envelope.rb
215
+ - lib/badline/sid/envelope/state.rb
198
216
  - lib/badline/sid/filter.rb
199
217
  - lib/badline/sid/voice.rb
200
218
  - lib/badline/sid/waveform.rb
201
219
  - lib/badline/sid/waveform/combined.rb
202
220
  - lib/badline/sid/waveform/fast_forward.rb
203
221
  - lib/badline/sid/waveform/noise_writeback.rb
222
+ - lib/badline/sid/waveform/state.rb
223
+ - lib/badline/snapshot.rb
224
+ - lib/badline/snapshot/container.rb
225
+ - lib/badline/snapshot/fields.rb
226
+ - lib/badline/snapshot/image.rb
227
+ - lib/badline/snapshot/machine_state.rb
228
+ - lib/badline/snapshot/setup.rb
229
+ - lib/badline/snapshot/state.rb
230
+ - lib/badline/snapshot/vice.rb
231
+ - lib/badline/snapshot/vice/c64_mem.rb
232
+ - lib/badline/snapshot/vice/cias.rb
233
+ - lib/badline/snapshot/vice/cias_import.rb
234
+ - lib/badline/snapshot/vice/main_cpu.rb
235
+ - lib/badline/snapshot/vice/peripherals.rb
236
+ - lib/badline/snapshot/vice/restore.rb
237
+ - lib/badline/snapshot/vice/sid_extended.rb
238
+ - lib/badline/snapshot/vice/sid_registers.rb
239
+ - lib/badline/snapshot/vice/vicii.rb
240
+ - lib/badline/snapshot/vice/vicii_import.rb
204
241
  - lib/badline/status.rb
205
242
  - lib/badline/storage.rb
206
243
  - lib/badline/storage/crt_file.rb
@@ -220,6 +257,7 @@ files:
220
257
  - lib/badline/storage/song_lengths.rb
221
258
  - lib/badline/storage/t64.rb
222
259
  - lib/badline/storage/tap.rb
260
+ - lib/badline/storage/unavailable.rb
223
261
  - lib/badline/time_of_day.rb
224
262
  - lib/badline/traps.rb
225
263
  - lib/badline/version.rb
@@ -228,6 +266,7 @@ files:
228
266
  - lib/badline/via/fast_forward.rb
229
267
  - lib/badline/via/interrupt_register.rb
230
268
  - lib/badline/via/shift_register.rb
269
+ - lib/badline/via/state.rb
231
270
  - lib/badline/via/timer1.rb
232
271
  - lib/badline/via/timer2.rb
233
272
  - lib/badline/vic.rb
@@ -242,13 +281,15 @@ files:
242
281
  - lib/badline/vic/registers.rb
243
282
  - lib/badline/vic/sequencer.rb
244
283
  - lib/badline/vic/sequencer_output.rb
284
+ - lib/badline/vic/sequencer_state.rb
245
285
  - lib/badline/vic/sprite.rb
246
286
  - lib/badline/vic/sprite/internal_bus.rb
287
+ - lib/badline/vic/sprite/saved_state.rb
247
288
  - lib/badline/vic/sprite/shifter.rb
248
289
  - lib/badline/vic/sprite/timing.rb
249
290
  - lib/badline/vic/sprites.rb
291
+ - lib/badline/vic/sprites_state.rb
250
292
  - packaging/homebrew/badline.rb
251
- - vendor/.gitkeep
252
293
  homepage: https://github.com/elektronaut/badline
253
294
  licenses:
254
295
  - MIT
@@ -1,57 +0,0 @@
1
- #!/bin/bash
2
- # Prepares a Claude Code on the web container to run `bundle exec rubocop`
3
- # and `bundle exec rspec`. The regression suites need vendor/ and aren't set
4
- # up here.
5
- set -euo pipefail
6
-
7
- if [ "${CLAUDE_CODE_REMOTE:-}" != "true" ]; then
8
- exit 0
9
- fi
10
-
11
- cd "${CLAUDE_PROJECT_DIR:-$(dirname "$0")/../..}"
12
-
13
- # The SDL specs open libSDL2 through Fiddle, so only the runtime library is
14
- # needed, as in CI.
15
- if ! ldconfig -p | grep 'libSDL2-2\.0\.so\.0' >/dev/null; then
16
- export DEBIAN_FRONTEND=noninteractive
17
- apt-get update -qq
18
- apt-get install -y -qq libsdl2-2.0-0 >/dev/null
19
- fi
20
-
21
- # The image ships Ruby 3.3 and an rbenv. Install the newest Ruby 4.0.x, as
22
- # CI's ruby-version "4.0" picks, from the prebuilt binaries setup-ruby uses,
23
- # unless a 4.0 is installed already.
24
- export RBENV_ROOT=/opt/rbenv
25
- export PATH="$RBENV_ROOT/bin:$RBENV_ROOT/shims:$PATH"
26
- ruby_version=$(rbenv versions --bare | grep -E '^4\.0\.[0-9]+$' | sort -V | tail -1 || true)
27
- if [ -z "$ruby_version" ]; then
28
- ruby_version=$(curl -fsSL https://raw.githubusercontent.com/ruby/setup-ruby/master/ruby-builder-versions.json |
29
- ruby -rjson -e 'puts JSON.parse($stdin.read)["ruby"].grep(/\A4\.0\.\d+\z/).max_by { Gem::Version.new(_1) }')
30
- # The binaries only run from the prefix they were built with, so unpack
31
- # there and link it into rbenv.
32
- . /etc/os-release
33
- prefix="/opt/hostedtoolcache/Ruby/$ruby_version/x64"
34
- mkdir -p "$prefix"
35
- if curl -fsSL "https://github.com/ruby/ruby-builder/releases/download/ruby-$ruby_version/ruby-$ruby_version-ubuntu-$VERSION_ID-x64.tar.gz" |
36
- tar -xz -C "$prefix" --strip-components=1; then
37
- ln -sfn "$prefix" "$RBENV_ROOT/versions/$ruby_version"
38
- else
39
- # No binary for this Ubuntu release: build from source, which takes minutes.
40
- rm -rf "$prefix"
41
- git -C "$RBENV_ROOT/plugins/ruby-build" pull --ff-only -q || true
42
- MAKE_OPTS="-j$(nproc)" rbenv install --skip-existing "$ruby_version"
43
- fi
44
- fi
45
- rbenv global "$ruby_version"
46
- rbenv rehash
47
-
48
- # Put rbenv's shims ahead of /usr/local/bin/ruby for the session.
49
- if [ -n "${CLAUDE_ENV_FILE:-}" ]; then
50
- {
51
- echo "export RBENV_ROOT=$RBENV_ROOT"
52
- echo "export PATH=\"$RBENV_ROOT/bin:$RBENV_ROOT/shims:\$PATH\""
53
- } >> "$CLAUDE_ENV_FILE"
54
- fi
55
-
56
- # Bundler switches to the version Gemfile.lock was bundled with.
57
- bundle install --jobs "$(nproc)"
@@ -1,14 +0,0 @@
1
- {
2
- "hooks": {
3
- "SessionStart": [
4
- {
5
- "hooks": [
6
- {
7
- "type": "command",
8
- "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/session-start.sh"
9
- }
10
- ]
11
- }
12
- ]
13
- }
14
- }
@@ -1,70 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Badline
4
- module Audio
5
- # The host's audio device, fed through SDL's queue: mono signed 16-bit
6
- # samples go in at the device's rate and SDL plays them out behind us.
7
- class SDLSink
8
- class Error < Playback::DeviceError; end
9
-
10
- # int freq; Uint16 format; Uint8 channels, silence; Uint16 samples,
11
- # padding; Uint32 size; then the callback and userdata pointers.
12
- SPEC_LAYOUT = "lSCCSSLQQ"
13
-
14
- attr_reader :rate
15
-
16
- # With `exact_rate` false SDL may pick the device's own rate instead,
17
- # which #rate then reports.
18
- def initialize(rate:, exact_rate: false, buffer: 1024)
19
- load_sdl
20
- check(SDL::InitSubSystem.call(SDL::INIT_AUDIO))
21
- @device = open_device(rate, exact_rate ? 0 : SDL::AUDIO_ALLOW_FREQUENCY_CHANGE, buffer)
22
- end
23
-
24
- def queue(samples)
25
- data = samples.pack("s*")
26
- check(SDL::QueueAudio.call(@device, data, data.bytesize))
27
- end
28
-
29
- def queued_seconds = SDL::GetQueuedAudioSize.call(@device).fdiv(2 * rate)
30
-
31
- def start = SDL::PauseAudioDevice.call(@device, 0)
32
-
33
- def pause = SDL::PauseAudioDevice.call(@device, 1)
34
-
35
- def clear = SDL::ClearQueuedAudio.call(@device)
36
-
37
- def close
38
- return unless @device
39
-
40
- SDL::CloseAudioDevice.call(@device)
41
- SDL::QuitSubSystem.call(SDL::INIT_AUDIO)
42
- @device = nil
43
- end
44
-
45
- private
46
-
47
- def open_device(rate, allowed_changes, buffer)
48
- wanted = [rate, SDL::AUDIO_S16SYS, 1, 0, buffer, 0, 0, 0, 0].pack(SPEC_LAYOUT)
49
- obtained = "\0".b * 32
50
- device = SDL::OpenAudioDevice.call(nil, 0, wanted, obtained, allowed_changes)
51
- raise Error, SDL.error if device.zero?
52
-
53
- @rate = obtained.unpack1("l")
54
- device
55
- end
56
-
57
- def load_sdl
58
- require "badline/sdl"
59
- rescue SDL::Error => e
60
- raise Error, e.message
61
- end
62
-
63
- def check(result)
64
- raise Error, SDL.error if result.negative?
65
-
66
- result
67
- end
68
- end
69
- end
70
- end
@@ -1,117 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Badline
4
- module Audio
5
- # Plays a running machine's SID through a sink, for the window: #feed
6
- # queues whatever the SID recorded since the last call, once a frame,
7
- # and #pace holds the machine to the device's clock.
8
- #
9
- # The device starts once the queue holds `ahead` seconds. Below real time
10
- # the queue runs dry, and the device then stops until it holds `ahead`
11
- # again, so the sound stutters with silent gaps rather than slowing
12
- # down; `on_underrun` hears about the first time. A frame that would
13
- # take the queue past `limit` seconds, as an unpaced machine above real
14
- # time makes, is dropped whole.
15
- class Stream
16
- AHEAD = 0.08
17
- LIMIT = 0.25
18
-
19
- attr_reader :underruns, :dropped
20
-
21
- # What #pace reads the time from and sleeps on: #now and #sleep.
22
- attr_writer :clock
23
-
24
- def initialize(sink, sid, ahead: AHEAD, limit: LIMIT, on_underrun: nil)
25
- @sink = sink
26
- @sid = sid
27
- @ahead = ahead
28
- @limit = limit
29
- @on_underrun = on_underrun
30
- @clock = Clock.new
31
- @next_frame = nil
32
- @started = false
33
- @muted = false
34
- @underruns = 0
35
- @dropped = 0
36
- sid.record(rate: sink.rate)
37
- end
38
-
39
- def rate = @sink.rate
40
-
41
- def muted? = @muted
42
-
43
- def playing? = @started
44
-
45
- # Muting drops the queue; unmuting fills it to `ahead` again before
46
- # the device restarts.
47
- def toggle_mute
48
- @muted = !@muted
49
- return unless @muted
50
-
51
- halt
52
- @sink.clear
53
- end
54
-
55
- def feed
56
- samples = @sid.drain_samples
57
- return if @muted || samples.empty?
58
-
59
- queued = @sink.queued_seconds
60
- return @dropped += samples.size if queued + samples.size.fdiv(rate) > @limit
61
-
62
- underrun! if @started && queued.zero?
63
- @sink.queue(samples)
64
- start if @sink.queued_seconds >= @ahead
65
- end
66
-
67
- # Waits out the frame just run. While the device plays, that is until
68
- # it has played the queue down to `ahead`, so the machine runs at the
69
- # device's rate and the queue neither drifts dry nor grows. Muted or
70
- # filling up, the clock paces frames `seconds` apart instead.
71
- def pace(seconds)
72
- return wait_for_device if playing?
73
-
74
- now = @clock.now
75
- @next_frame = now if @next_frame.nil? || @next_frame < now
76
- @clock.sleep(@next_frame - now)
77
- @next_frame += seconds
78
- end
79
-
80
- def close = @sink.close
81
-
82
- private
83
-
84
- def start
85
- return if @started
86
-
87
- @started = true
88
- @sink.start
89
- end
90
-
91
- def wait_for_device
92
- @next_frame = nil
93
- while (excess = @sink.queued_seconds - @ahead).positive?
94
- @clock.sleep([excess, 0.001].max)
95
- end
96
- end
97
-
98
- def halt
99
- @started = false
100
- @sink.pause
101
- end
102
-
103
- def underrun!
104
- @underruns += 1
105
- @on_underrun&.call if @underruns == 1
106
- halt
107
- end
108
-
109
- # The host's monotonic clock.
110
- class Clock
111
- def now = Process.clock_gettime(Process::CLOCK_MONOTONIC)
112
-
113
- def sleep(seconds) = Kernel.sleep(seconds)
114
- end
115
- end
116
- end
117
- end