badline 0.5.1 → 0.6.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 (129) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +12 -0
  3. data/README.md +58 -0
  4. data/exe/badline-ruby +2 -1
  5. data/lib/badline/address_bus/saved_state.rb +72 -0
  6. data/lib/badline/address_bus.rb +1 -0
  7. data/lib/badline/audio/stream.rb +6 -0
  8. data/lib/badline/cartridge/action_replay.rb +15 -0
  9. data/lib/badline/cartridge/comal80.rb +8 -0
  10. data/lib/badline/cartridge/dinamic.rb +8 -0
  11. data/lib/badline/cartridge/easy_flash.rb +20 -1
  12. data/lib/badline/cartridge/epyx_fastload.rb +8 -0
  13. data/lib/badline/cartridge/final_cartridge3.rb +12 -0
  14. data/lib/badline/cartridge/flash.rb +41 -0
  15. data/lib/badline/cartridge/fun_play.rb +8 -0
  16. data/lib/badline/cartridge/g_mod2.rb +12 -0
  17. data/lib/badline/cartridge/game_system.rb +8 -0
  18. data/lib/badline/cartridge/geo_ram.rb +16 -0
  19. data/lib/badline/cartridge/magic_desk.rb +8 -0
  20. data/lib/badline/cartridge/ocean.rb +8 -0
  21. data/lib/badline/cartridge/pagefox.rb +12 -0
  22. data/lib/badline/cartridge/retro_replay.rb +44 -1
  23. data/lib/badline/cartridge/rgcd.rb +10 -0
  24. data/lib/badline/cartridge/super_games.rb +10 -0
  25. data/lib/badline/cartridge/zaxxon.rb +16 -0
  26. data/lib/badline/cartridge.rb +72 -0
  27. data/lib/badline/cia/interrupt_register.rb +17 -0
  28. data/lib/badline/cia/serial.rb +44 -0
  29. data/lib/badline/cia/state.rb +47 -0
  30. data/lib/badline/cia/timer.rb +33 -0
  31. data/lib/badline/cia.rb +8 -0
  32. data/lib/badline/computer/attachments.rb +132 -0
  33. data/lib/badline/computer/saved_state.rb +116 -0
  34. data/lib/badline/computer.rb +7 -30
  35. data/lib/badline/cpu/microcode.rb +8 -0
  36. data/lib/badline/cpu/saved_state.rb +115 -0
  37. data/lib/badline/cpu.rb +2 -0
  38. data/lib/badline/datasette.rb +27 -0
  39. data/lib/badline/drive1541/bus.rb +13 -0
  40. data/lib/badline/drive1541/disk.rb +7 -2
  41. data/lib/badline/drive1541/disk_state.rb +80 -0
  42. data/lib/badline/drive1541/mechanism.rb +2 -0
  43. data/lib/badline/drive1541/mechanism_state.rb +67 -0
  44. data/lib/badline/drive1541/rotation.rb +10 -0
  45. data/lib/badline/drive1541/serial_port.rb +10 -0
  46. data/lib/badline/drive1541.rb +34 -0
  47. data/lib/badline/gui/application.rb +41 -14
  48. data/lib/badline/gui/gamepads.rb +7 -0
  49. data/lib/badline/gui/snapshots.rb +67 -0
  50. data/lib/badline/gui.rb +1 -0
  51. data/lib/badline/kernal_trap/channel.rb +27 -0
  52. data/lib/badline/kernal_trap/drive/channels.rb +25 -0
  53. data/lib/badline/kernal_trap/drive/memory.rb +9 -0
  54. data/lib/badline/kernal_trap/drive/status.rb +8 -0
  55. data/lib/badline/kernal_trap/drive/write_file.rb +21 -0
  56. data/lib/badline/kernal_trap/drive.rb +19 -0
  57. data/lib/badline/kernal_trap/save.rb +8 -0
  58. data/lib/badline/kernal_trap/serial.rb +16 -0
  59. data/lib/badline/memory.rb +8 -0
  60. data/lib/badline/options.rb +3 -2
  61. data/lib/badline/ram_expansion/plus256k.rb +16 -0
  62. data/lib/badline/ram_expansion/plus60k.rb +12 -0
  63. data/lib/badline/ram_expansion/unexpanded.rb +6 -0
  64. data/lib/badline/reu/dma.rb +36 -0
  65. data/lib/badline/reu/ram.rb +15 -0
  66. data/lib/badline/reu/saved_state.rb +52 -0
  67. data/lib/badline/reu.rb +4 -0
  68. data/lib/badline/sdl/events.rb +2 -0
  69. data/lib/badline/sid/envelope/state.rb +68 -0
  70. data/lib/badline/sid/envelope.rb +7 -0
  71. data/lib/badline/sid/filter.rb +38 -0
  72. data/lib/badline/sid/voice.rb +10 -0
  73. data/lib/badline/sid/waveform/state.rb +66 -0
  74. data/lib/badline/sid/waveform.rb +2 -0
  75. data/lib/badline/sid.rb +34 -0
  76. data/lib/badline/snapshot/container.rb +117 -0
  77. data/lib/badline/snapshot/fields.rb +54 -0
  78. data/lib/badline/snapshot/image.rb +70 -0
  79. data/lib/badline/snapshot/machine_state.rb +77 -0
  80. data/lib/badline/snapshot/setup.rb +58 -0
  81. data/lib/badline/snapshot/state.rb +178 -0
  82. data/lib/badline/snapshot/vice/c64_mem.rb +53 -0
  83. data/lib/badline/snapshot/vice/cias.rb +89 -0
  84. data/lib/badline/snapshot/vice/cias_import.rb +70 -0
  85. data/lib/badline/snapshot/vice/main_cpu.rb +76 -0
  86. data/lib/badline/snapshot/vice/peripherals.rb +47 -0
  87. data/lib/badline/snapshot/vice/restore.rb +125 -0
  88. data/lib/badline/snapshot/vice/sid_extended.rb +96 -0
  89. data/lib/badline/snapshot/vice/sid_registers.rb +46 -0
  90. data/lib/badline/snapshot/vice/vicii.rb +119 -0
  91. data/lib/badline/snapshot/vice/vicii_import.rb +131 -0
  92. data/lib/badline/snapshot/vice.rb +74 -0
  93. data/lib/badline/snapshot.rb +52 -0
  94. data/lib/badline/storage/crt_file.rb +9 -3
  95. data/lib/badline/storage/d64_image.rb +2 -0
  96. data/lib/badline/storage/d71_image.rb +2 -0
  97. data/lib/badline/storage/d81_image.rb +2 -0
  98. data/lib/badline/storage/disk_image.rb +11 -2
  99. data/lib/badline/storage/host_directory.rb +5 -0
  100. data/lib/badline/storage/t64.rb +6 -0
  101. data/lib/badline/storage/tap.rb +10 -1
  102. data/lib/badline/storage/unavailable.rb +20 -0
  103. data/lib/badline/storage.rb +34 -0
  104. data/lib/badline/time_of_day.rb +56 -0
  105. data/lib/badline/version.rb +1 -1
  106. data/lib/badline/via/control_lines.rb +15 -0
  107. data/lib/badline/via/interrupt_register.rb +9 -0
  108. data/lib/badline/via/shift_register.rb +13 -0
  109. data/lib/badline/via/state.rb +42 -0
  110. data/lib/badline/via/timer1.rb +13 -0
  111. data/lib/badline/via/timer2.rb +18 -0
  112. data/lib/badline/via.rb +2 -0
  113. data/lib/badline/vic/border_mask.rb +10 -0
  114. data/lib/badline/vic/collisions.rb +11 -0
  115. data/lib/badline/vic/color_patches.rb +11 -0
  116. data/lib/badline/vic/display_state.rb +29 -1
  117. data/lib/badline/vic/graphics_shifter.rb +26 -0
  118. data/lib/badline/vic/register_log.rb +18 -0
  119. data/lib/badline/vic/registers.rb +13 -2
  120. data/lib/badline/vic/sequencer.rb +12 -0
  121. data/lib/badline/vic/sequencer_state.rb +89 -0
  122. data/lib/badline/vic/sprite/internal_bus.rb +11 -0
  123. data/lib/badline/vic/sprite/saved_state.rb +64 -0
  124. data/lib/badline/vic/sprite.rb +2 -0
  125. data/lib/badline/vic/sprites.rb +3 -0
  126. data/lib/badline/vic/sprites_state.rb +57 -0
  127. data/lib/badline/vic.rb +94 -10
  128. data/lib/badline.rb +1 -0
  129. metadata +35 -1
@@ -92,6 +92,19 @@ module Badline
92
92
  @counter = @reload ? 0xffff : @latch - phase + 1
93
93
  end
94
94
 
95
+ def save_state(out)
96
+ out.int(@counter).int(@latch).boolean(@hold).boolean(@reload).boolean(@armed).boolean(@pb7)
97
+ end
98
+
99
+ def load_state(input)
100
+ @counter = input.int
101
+ @latch = input.int
102
+ @hold = input.boolean?
103
+ @reload = input.boolean?
104
+ @armed = input.boolean?
105
+ @pb7 = input.boolean?
106
+ end
107
+
95
108
  # What the counter and PB7 don't hold, for comparing the timer at two
96
109
  # points.
97
110
  def state = [@latch, @armed]
@@ -84,6 +84,24 @@ module Badline
84
84
  @low_underflowed = @counter.allbits?(0xff)
85
85
  end
86
86
 
87
+ def save_state(out)
88
+ out.int(@counter).int(@latch_low)
89
+ [@hold, @low_reload, @armed, @low_underflowed, @count_pulses, @counting_pulses].each do |flag|
90
+ out.boolean(flag)
91
+ end
92
+ end
93
+
94
+ def load_state(input)
95
+ @counter = input.int
96
+ @latch_low = input.int
97
+ @hold = input.boolean?
98
+ @low_reload = input.boolean?
99
+ @armed = input.boolean?
100
+ @low_underflowed = input.boolean?
101
+ @count_pulses = input.boolean?
102
+ @counting_pulses = input.boolean?
103
+ end
104
+
87
105
  # What the counter and the low byte's underflow don't hold, for
88
106
  # comparing the timer at two points.
89
107
  def state = [@latch_low, @armed, @hold, @low_reload, @count_pulses, @counting_pulses]
data/lib/badline/via.rb CHANGED
@@ -4,6 +4,7 @@ require "badline/via/control_lines"
4
4
  require "badline/via/fast_forward"
5
5
  require "badline/via/interrupt_register"
6
6
  require "badline/via/shift_register"
7
+ require "badline/via/state"
7
8
  require "badline/via/timer1"
8
9
  require "badline/via/timer2"
9
10
 
@@ -18,6 +19,7 @@ module Badline
18
19
  class VIA
19
20
  include Addressable
20
21
  include FastForward
22
+ include State
21
23
 
22
24
  attr_reader :start, :peripheral, :shift_register, :acr, :pcr
23
25
 
@@ -27,6 +27,16 @@ module Badline
27
27
  @groups.fill(FULL)
28
28
  end
29
29
 
30
+ def save_state(out)
31
+ out.ints(@groups).booleans(@mask).blob(@snapshot)
32
+ end
33
+
34
+ def load_state(input)
35
+ input.ints_into(@groups)
36
+ input.booleans_into(@mask)
37
+ input.blob_into(@snapshot)
38
+ end
39
+
30
40
  def at?(pixel_x)
31
41
  case @groups[pixel_x >> 3]
32
42
  when FULL then true
@@ -36,6 +36,17 @@ module Badline
36
36
  @cleared_data = 0
37
37
  end
38
38
 
39
+ def save_state(out)
40
+ out.blob(@hits).int(@folded).int(@cleared_sprite).int(@cleared_data)
41
+ end
42
+
43
+ def load_state(input)
44
+ input.blob_into(@hits)
45
+ @folded = input.int
46
+ @cleared_sprite = input.int
47
+ @cleared_data = input.int
48
+ end
49
+
39
50
  def start_line
40
51
  @folded = 0
41
52
  @cleared_sprite = carry(@cleared_sprite)
@@ -17,6 +17,17 @@ module Badline
17
17
 
18
18
  def clear = @patches.clear
19
19
 
20
+ # Each patch as its four numbers: the boundary pixel, the register,
21
+ # the old colour and the new.
22
+ def save_state(out)
23
+ out.int(@patches.length)
24
+ @patches.each { |patch| out.ints(patch) }
25
+ end
26
+
27
+ def load_state(input)
28
+ @patches.replace(Array.new(input.int) { input.ints })
29
+ end
30
+
20
31
  def log(reg, old, value, boundary_x)
21
32
  @patches << [boundary_x, reg, @grey_dots ? GREY_DOT : old & 0x0f, value & 0x0f]
22
33
  end
@@ -30,7 +30,7 @@ module Badline
30
30
 
31
31
  GRAPHICS_COLUMNS = Array.new(65) { |column| column.between?(GRAPHICS_FIRST, GRAPHICS_LAST) }.freeze
32
32
 
33
- attr_reader :vc_base, :vc, :vmli, :rc
33
+ attr_reader :vc_base, :vc, :vmli, :rc, :bad_lines_enabled
34
34
 
35
35
  # The wrap column is the last column of a rasterline, 62 on the 6569,
36
36
  # which already compares against the line about to start, the same
@@ -50,6 +50,15 @@ module Badline
50
50
  end
51
51
 
52
52
  def display? = @display
53
+
54
+ # Sets VCBASE, VC, RC and VMLI, in that order, and the display and
55
+ # bad-line flags, as a VICE snapshot gives them.
56
+ def restore_counters(counters, display, bad_lines_enabled)
57
+ @vc_base, @vc, @rc, @vmli = counters
58
+ @display = display
59
+ @bad_lines_enabled = bad_lines_enabled
60
+ end
61
+
53
62
  def idle? = !@display
54
63
  def bad_line? = !@ba.nil?
55
64
 
@@ -63,6 +72,25 @@ module Badline
63
72
  @matched && column >= FETCH_FIRST && column <= DMA_LAST
64
73
  end
65
74
 
75
+ def save_state(out)
76
+ out.int(@vc_base).int(@vc).int(@vmli).int(@rc).boolean(@display).boolean(@bad_lines_enabled)
77
+ out.boolean(@matched).optional_int(@ba).int(@line_bits).boolean(@den_line).boolean(@in_window)
78
+ end
79
+
80
+ def load_state(input)
81
+ @vc_base = input.int
82
+ @vc = input.int
83
+ @vmli = input.int
84
+ @rc = input.int
85
+ @display = input.boolean?
86
+ @bad_lines_enabled = input.boolean?
87
+ @matched = input.boolean?
88
+ @ba = input.optional_int
89
+ @line_bits = input.int
90
+ @den_line = input.boolean?
91
+ @in_window = input.boolean?
92
+ end
93
+
66
94
  def new_frame
67
95
  @vc_base = 0
68
96
  @bad_lines_enabled = false
@@ -50,6 +50,24 @@ module Badline
50
50
  @late_read = false
51
51
  end
52
52
 
53
+ def save_state(out)
54
+ out.int(@next_ecm_bmm).int(@black_below).ints(@colors).booleans(@fg)
55
+ out.int(@gbuf).int(@vbuf).int(@cbuf).int(@flop).int(@pixel)
56
+ out.int(@mode_lookup).int(@mode_read).boolean(@late_mcm).boolean(@late_read)
57
+ end
58
+
59
+ def load_state(input)
60
+ @next_ecm_bmm = input.int
61
+ @black_below = input.int
62
+ input.ints_into(@colors)
63
+ input.booleans_into(@fg)
64
+ load_buffers(input)
65
+ @mode_lookup = input.int
66
+ @mode_read = input.int
67
+ @late_mcm = input.boolean?
68
+ @late_read = input.boolean?
69
+ end
70
+
53
71
  # Rebuilds the state at the end of a group that painted whole bytes:
54
72
  # the byte loaded at pixel shift under an unchanging mode.
55
73
  def prime(data, screencode, color, shift, mode)
@@ -78,6 +96,14 @@ module Badline
78
96
 
79
97
  private
80
98
 
99
+ def load_buffers(input)
100
+ @gbuf = input.int
101
+ @vbuf = input.int
102
+ @cbuf = input.int
103
+ @flop = input.int
104
+ @pixel = input.int
105
+ end
106
+
81
107
  def step_mode(pixel, mode)
82
108
  case pixel
83
109
  when 0 then start_group
@@ -19,6 +19,24 @@ module Badline
19
19
  # The pixel at which the next logged write takes effect.
20
20
  attr_reader :next_x
21
21
 
22
+ # The entries as pixel, register and value in turn, the seed with -1
23
+ # for a register the line hasn't logged, and a next pixel past the
24
+ # last entry as -1.
25
+ def save_state(out)
26
+ out.ints(@entries.flatten)
27
+ out.ints(@seed.map { |value| value.nil? ? -1 : value })
28
+ out.ints(@values).int(@cursor).int(@next_x.infinite? ? -1 : @next_x)
29
+ end
30
+
31
+ def load_state(input)
32
+ @entries.replace(input.ints.each_slice(3).to_a)
33
+ @seed.replace(input.ints.map { |value| value.negative? ? nil : value })
34
+ input.ints_into(@values)
35
+ @cursor = input.int
36
+ next_x = input.int
37
+ @next_x = next_x.negative? ? Float::INFINITY : next_x
38
+ end
39
+
22
40
  def empty? = @entries.empty?
23
41
  def length = @entries.length
24
42
 
@@ -143,6 +143,15 @@ module Badline
143
143
 
144
144
  def latch_raster_irq! = latch_irq!(0x01)
145
145
 
146
+ def save_state(out)
147
+ out.blob(@bytes).boolean(@irq_line)
148
+ end
149
+
150
+ def load_state(input)
151
+ input.blob_into(@bytes)
152
+ @irq_line = input.boolean?
153
+ end
154
+
146
155
  def collide!(reg, bits)
147
156
  return false if bits.zero?
148
157
 
@@ -151,12 +160,14 @@ module Badline
151
160
  was_zero
152
161
  end
153
162
 
154
- private
155
-
163
+ # Works the IRQ line out from $D019 and $D01A again, after the bytes
164
+ # were set directly.
156
165
  def update_irq_line
157
166
  @irq_line = (@bytes[0x19] & @bytes[0x1a]).anybits?(0x0f)
158
167
  end
159
168
 
169
+ private
170
+
160
171
  def read_clear(reg)
161
172
  @bytes[reg].tap { @bytes[reg] = 0 }
162
173
  end
@@ -5,6 +5,7 @@ require "badline/vic/color_patches"
5
5
  require "badline/vic/graphics_mode"
6
6
  require "badline/vic/graphics_shifter"
7
7
  require "badline/vic/sequencer_output"
8
+ require "badline/vic/sequencer_state"
8
9
 
9
10
  module Badline
10
11
  class VIC < Cycleable
@@ -13,6 +14,7 @@ module Badline
13
14
  # Turns fetched graphics data into output pixels.
14
15
  class Sequencer
15
16
  include Output
17
+ include SavedState
16
18
 
17
19
  BORDER_Y_BOUNDS = [
18
20
  [55, 247].freeze,
@@ -149,6 +151,16 @@ module Badline
149
151
  # slot before is the byte the previous group loaded.
150
152
  attr_reader :ring_data, :ring_char, :ring_color
151
153
 
154
+ # The border flip-flops: vertical, the vertical one armed to set, and
155
+ # the main one.
156
+ def borders = [@vertical_border, @vertical_armed, @main_border]
157
+
158
+ def restore_borders(vertical, armed, main)
159
+ @vertical_border = vertical
160
+ @vertical_armed = armed
161
+ @main_border = main
162
+ end
163
+
152
164
  # Paints the byte in the slot through the current mode. An idle access
153
165
  # latches 0/0 and one that made no access passes on zero data with the
154
166
  # kept values; under a closed border neither has foreground. A group
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ class VIC < Cycleable
5
+ class Sequencer
6
+ # The sequencer's save_state and load_state.
7
+ module SavedState
8
+ FG_SHIFTER = -1
9
+ PAIR_CODES = 256
10
+
11
+ # The line buffers, the border flip-flops, the g-access ring and the
12
+ # pixel pipeline. The foreground masks the groups point at go as
13
+ # FG_SHIFTER for the shift register's, or their data byte in
14
+ # GraphicsMode's HIRES_FG, or that plus 256 in its PAIR_FG. The left
15
+ # vertical border compare goes as -1 before it has run.
16
+ def save_state(out)
17
+ out.int(@line).boolean(@first_csel).int(@left_vertical_border.nil? ? -1 : bit(@left_vertical_border))
18
+ out.blob(@colors).booleans(@fg)
19
+ @border_mask.save_state(out)
20
+ out.ints(@cur_colors).int(fg_code(@cur_fg)).ints(@prev_colors).int(fg_code(@prev_fg))
21
+ out.boolean(@vertical_border).boolean(@vertical_armed).boolean(@main_border)
22
+ @color_patches.save_state(out)
23
+ @shifter.save_state(out)
24
+ out.int(@xscroll).int(@last_shift).int(@last_mode).boolean(@settling).boolean(@prev_fresh).boolean(@check)
25
+ out.ints(@ring_data).ints(@ring_char).ints(@ring_color)
26
+ end
27
+
28
+ def load_state(input)
29
+ load_line(input)
30
+ @border_mask.load_state(input)
31
+ input.ints_into(@cur_colors)
32
+ @cur_fg = fg_pattern(input.int)
33
+ input.ints_into(@prev_colors)
34
+ @prev_fg = fg_pattern(input.int)
35
+ @vertical_border = input.boolean?
36
+ @vertical_armed = input.boolean?
37
+ @main_border = input.boolean?
38
+ @color_patches.load_state(input)
39
+ @shifter.load_state(input)
40
+ load_pipeline(input)
41
+ end
42
+
43
+ private
44
+
45
+ def bit(flag) = flag ? 1 : 0
46
+
47
+ def load_line(input)
48
+ @line = input.int
49
+ @first_csel = input.boolean?
50
+ left = input.int
51
+ @left_vertical_border = left.negative? ? nil : left == 1
52
+ input.blob_into(@colors)
53
+ input.booleans_into(@fg)
54
+ end
55
+
56
+ def load_pipeline(input)
57
+ @xscroll = input.int
58
+ @last_shift = input.int
59
+ @last_mode = input.int
60
+ @settling = input.boolean?
61
+ @prev_fresh = input.boolean?
62
+ @check = input.boolean?
63
+ input.ints_into(@ring_data)
64
+ input.ints_into(@ring_char)
65
+ input.ints_into(@ring_color)
66
+ end
67
+
68
+ def fg_code(pattern)
69
+ return FG_SHIFTER if pattern.equal?(@shifter.fg)
70
+
71
+ hires = GraphicsMode::HIRES_FG.index { |fg| fg.equal?(pattern) }
72
+ return hires if hires
73
+
74
+ pair = GraphicsMode::PAIR_FG.index { |fg| fg.equal?(pattern) }
75
+ raise ArgumentError, "a foreground mask the sequencer doesn't know" unless pair
76
+
77
+ PAIR_CODES + pair
78
+ end
79
+
80
+ def fg_pattern(code)
81
+ return @shifter.fg if code == FG_SHIFTER
82
+ return GraphicsMode::HIRES_FG.fetch(code) if code < PAIR_CODES
83
+
84
+ GraphicsMode::PAIR_FG.fetch(code - PAIR_CODES)
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
@@ -25,6 +25,17 @@ module Badline
25
25
  @line = Array.new(columns, -1)
26
26
  end
27
27
 
28
+ def save_state(out)
29
+ out.int(@lines).int(@ghost).ints(@value).ints(@line)
30
+ end
31
+
32
+ def load_state(input)
33
+ @lines = input.int
34
+ @ghost = input.int
35
+ input.ints_into(@value)
36
+ input.ints_into(@line)
37
+ end
38
+
28
39
  # Sprites 3-7 fetch in Bauer cycles 1-10, so the idle byte is taken
29
40
  # as the line starts.
30
41
  def start_line(sprites_enabled)
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ class VIC < Cycleable
5
+ class Sprite
6
+ # The sprite's save_state and load_state.
7
+ module SavedState
8
+ # The shift register, MC, MCBASE, the expansion flip-flop and whether
9
+ # the sprite shows, for a VICE snapshot.
10
+ attr_reader :sr, :mc, :mcbase, :exp_ff, :display_on
11
+
12
+ # Sets MC, MCBASE, the expansion flip-flop, whether the sprite's DMA
13
+ # runs and whether it shows, as a VICE snapshot gives them.
14
+ def restore_counters(counter, base, expanded, dma, shows)
15
+ @mc = counter
16
+ @mcbase = base
17
+ @exp_ff = expanded
18
+ @dma = dma
19
+ @display_on = shows
20
+ end
21
+
22
+ def save_state(out)
23
+ out.boolean(@dma).boolean(@display_on).int(@mcbase).int(@mc).boolean(@exp_ff).optional_int(@crunched_mc)
24
+ out.int(@bits).boolean(@row_ready).optional_int(@prev_bits).boolean(@first_byte_lost).boolean(@blind)
25
+ out.int(@show_from).optional_int(@stop_x)
26
+ out.ints(@codes).int(@leftmost).int(@span).ints(@reload_codes).int(@reload_leftmost).int(@reload_span)
27
+ out.int(@sr).int(@latch).boolean(@mc_flop).boolean(@xe_flop)
28
+ end
29
+
30
+ def load_state(input)
31
+ load_counters(input)
32
+ @show_from = input.int
33
+ @stop_x = input.optional_int
34
+ input.ints_into(@codes)
35
+ @leftmost = input.int
36
+ @span = input.int
37
+ input.ints_into(@reload_codes)
38
+ @reload_leftmost = input.int
39
+ @reload_span = input.int
40
+ @sr = input.int
41
+ @latch = input.int
42
+ @mc_flop = input.boolean?
43
+ @xe_flop = input.boolean?
44
+ end
45
+
46
+ private
47
+
48
+ def load_counters(input)
49
+ @dma = input.boolean?
50
+ @display_on = input.boolean?
51
+ @mcbase = input.int
52
+ @mc = input.int
53
+ @exp_ff = input.boolean?
54
+ @crunched_mc = input.optional_int
55
+ @bits = input.int
56
+ @row_ready = input.boolean?
57
+ @prev_bits = input.optional_int
58
+ @first_byte_lost = input.boolean?
59
+ @blind = input.boolean?
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -2,6 +2,7 @@
2
2
 
3
3
  require "badline/vic/sprite/internal_bus"
4
4
  require "badline/vic/sprite/shifter"
5
+ require "badline/vic/sprite/saved_state"
5
6
  require "badline/vic/sprite/timing"
6
7
 
7
8
  module Badline
@@ -51,6 +52,7 @@ module Badline
51
52
  DISPLAY_OFF_X = 460
52
53
 
53
54
  include Shifter
55
+ include SavedState
54
56
 
55
57
  attr_reader :index, :leftmost, :span, :codes,
56
58
  :reload_leftmost, :reload_span, :reload_codes
@@ -3,10 +3,13 @@
3
3
  require "badline/vic/collisions"
4
4
  require "badline/vic/register_log"
5
5
  require "badline/vic/sprite"
6
+ require "badline/vic/sprites_state"
6
7
 
7
8
  module Badline
8
9
  class VIC < Cycleable
9
10
  class Sprites
11
+ include SavedState
12
+
10
13
  # Pixels between the start of the cycle following a write and the
11
14
  # point the new value shows. Each signal reaches the output on its own
12
15
  # path: the colors feed the final mux, the sequencer inputs — X
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ class VIC < Cycleable
5
+ class Sprites
6
+ # The sprites' save_state and load_state.
7
+ module SavedState
8
+ # Whether any sprite's DMA runs, set after Sprite#restore_counters.
9
+ def update_any_dma
10
+ @any_dma = @sprites.any?(&:displaying?)
11
+ end
12
+
13
+ # The sprites, their shared bus, the collisions and the register log,
14
+ # then the line's coverage and the pixels carried into the next line,
15
+ # each as x, colour and priority.
16
+ def save_state(out)
17
+ out.boolean(@any_dma).boolean(@stopped_dma).boolean(@carried).int(@lo).int(@hi).int(@sequenced)
18
+ out.blob(@win_color).booleans(@win_priority).ints(@palette)
19
+ [@carry, @carry_next].each { |carry| carry.each { |pixels| save_carry(pixels, out) } }
20
+ @sprites.each { |sprite| sprite.save_state(out) }
21
+ @bus.save_state(out)
22
+ @collisions.save_state(out)
23
+ @log.save_state(out)
24
+ end
25
+
26
+ def load_state(input)
27
+ @any_dma = input.boolean?
28
+ @stopped_dma = input.boolean?
29
+ @carried = input.boolean?
30
+ @lo = input.int
31
+ @hi = input.int
32
+ @sequenced = input.int
33
+ input.blob_into(@win_color)
34
+ input.booleans_into(@win_priority)
35
+ input.ints_into(@palette)
36
+ [@carry, @carry_next].each { |carry| carry.each { |pixels| load_carry(pixels, input) } }
37
+ @sprites.each { |sprite| sprite.load_state(input) }
38
+ @bus.load_state(input)
39
+ @collisions.load_state(input)
40
+ @log.load_state(input)
41
+ end
42
+
43
+ private
44
+
45
+ def save_carry(pixels, out)
46
+ out.int(pixels.length / 3)
47
+ pixels.each_slice(3) { |x, color, priority| out.int(x).int(color).boolean(priority) }
48
+ end
49
+
50
+ def load_carry(pixels, input)
51
+ pixels.clear
52
+ input.int.times { pixels.push(input.int, input.int, input.boolean?) }
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end