badline 0.1.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 (74) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +40 -0
  4. data/exe/badline +55 -0
  5. data/lib/badline/address_bus.rb +206 -0
  6. data/lib/badline/addressable.rb +61 -0
  7. data/lib/badline/cartridge/magic_desk.rb +23 -0
  8. data/lib/badline/cartridge/ocean.rb +33 -0
  9. data/lib/badline/cartridge/standard.rb +31 -0
  10. data/lib/badline/cartridge.rb +75 -0
  11. data/lib/badline/chrout_trap.rb +39 -0
  12. data/lib/badline/cia/timer.rb +122 -0
  13. data/lib/badline/cia.rb +189 -0
  14. data/lib/badline/color_memory.rb +16 -0
  15. data/lib/badline/computer.rb +100 -0
  16. data/lib/badline/control_ports.rb +24 -0
  17. data/lib/badline/cpu.rb +239 -0
  18. data/lib/badline/cycleable.rb +35 -0
  19. data/lib/badline/gui/application.rb +94 -0
  20. data/lib/badline/gui/joy_map.rb +19 -0
  21. data/lib/badline/gui/key_map.rb +34 -0
  22. data/lib/badline/gui/palette.rb +35 -0
  23. data/lib/badline/gui/pane.rb +35 -0
  24. data/lib/badline/gui/screen_pane.rb +50 -0
  25. data/lib/badline/gui/window.rb +46 -0
  26. data/lib/badline/gui.rb +11 -0
  27. data/lib/badline/instruction.rb +334 -0
  28. data/lib/badline/instruction_set/arithmetic.rb +119 -0
  29. data/lib/badline/instruction_set/bitwise.rb +131 -0
  30. data/lib/badline/instruction_set/branch.rb +78 -0
  31. data/lib/badline/instruction_set/flag.rb +63 -0
  32. data/lib/badline/instruction_set/illegal.rb +278 -0
  33. data/lib/badline/instruction_set/inc_dec.rb +71 -0
  34. data/lib/badline/instruction_set/stack.rb +104 -0
  35. data/lib/badline/instruction_set/transfer.rb +137 -0
  36. data/lib/badline/instruction_set.rb +77 -0
  37. data/lib/badline/integer_helper.rb +39 -0
  38. data/lib/badline/joystick.rb +25 -0
  39. data/lib/badline/kernal_trap/file.rb +54 -0
  40. data/lib/badline/kernal_trap/load.rb +63 -0
  41. data/lib/badline/kernal_trap/save.rb +42 -0
  42. data/lib/badline/kernal_trap.rb +5 -0
  43. data/lib/badline/keyboard.rb +58 -0
  44. data/lib/badline/keyboard_buffer.rb +33 -0
  45. data/lib/badline/media.rb +59 -0
  46. data/lib/badline/memory.rb +43 -0
  47. data/lib/badline/rom.rb +23 -0
  48. data/lib/badline/roms/README +18 -0
  49. data/lib/badline/roms/basic.rom +0 -0
  50. data/lib/badline/roms/character.rom +0 -0
  51. data/lib/badline/roms/kernal.rom +0 -0
  52. data/lib/badline/sid.rb +25 -0
  53. data/lib/badline/status.rb +56 -0
  54. data/lib/badline/storage/crt_file.rb +53 -0
  55. data/lib/badline/storage/d64_image.rb +21 -0
  56. data/lib/badline/storage/d71_image.rb +13 -0
  57. data/lib/badline/storage/d81_image.rb +14 -0
  58. data/lib/badline/storage/disk_image.rb +71 -0
  59. data/lib/badline/storage/host_directory.rb +49 -0
  60. data/lib/badline/storage/p00.rb +24 -0
  61. data/lib/badline/storage.rb +28 -0
  62. data/lib/badline/time_of_day.rb +101 -0
  63. data/lib/badline/traps.rb +15 -0
  64. data/lib/badline/version.rb +5 -0
  65. data/lib/badline/vic/bank.rb +65 -0
  66. data/lib/badline/vic/display_state.rb +78 -0
  67. data/lib/badline/vic/graphics_mode.rb +139 -0
  68. data/lib/badline/vic/registers.rb +170 -0
  69. data/lib/badline/vic/sequencer.rb +237 -0
  70. data/lib/badline/vic/sprite.rb +121 -0
  71. data/lib/badline/vic/sprites.rb +112 -0
  72. data/lib/badline/vic.rb +192 -0
  73. data/lib/badline.rb +29 -0
  74. metadata +131 -0
@@ -0,0 +1,121 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ class VIC < Cycleable
5
+ class Sprite
6
+ X_OFFSET = 104
7
+ ROWS = 21
8
+
9
+ attr_reader :index, :line_pixels
10
+
11
+ def initialize(index, registers, bank, width)
12
+ @index = index
13
+ @registers = registers
14
+ @bank = bank
15
+ @width = width
16
+ @bit = 1 << index
17
+ @displaying = false
18
+ @counter = 0
19
+ @bits = nil
20
+ @line_pixels = nil
21
+ @pixel_buffer = Array.new(48)
22
+ end
23
+
24
+ def displaying? = @displaying
25
+
26
+ def enabled? = @registers[0x15].anybits?(@bit)
27
+ def multicolor? = @registers[0x1c].anybits?(@bit)
28
+ def x_expanded? = @registers[0x1d].anybits?(@bit)
29
+ def y_expanded? = @registers[0x17].anybits?(@bit)
30
+ def priority? = @registers[0x1b].anybits?(@bit)
31
+
32
+ def x
33
+ msb = @registers[0x10].anybits?(@bit) ? 0x100 : 0
34
+ msb | @registers[index * 2]
35
+ end
36
+
37
+ def y = @registers[(index * 2) + 1]
38
+ def color = @registers[0x27 + index] & 0x0f
39
+
40
+ def leftmost = (x + X_OFFSET) % @width
41
+ def pixel_width = x_expanded? ? 48 : 24
42
+
43
+ def start_line(line)
44
+ if !@displaying && enabled? && line == y
45
+ @displaying = true
46
+ @counter = 0
47
+ end
48
+
49
+ return @line_pixels = nil unless @displaying
50
+
51
+ row = y_expanded? ? @counter / 2 : @counter
52
+ if row >= ROWS
53
+ @displaying = false
54
+ return @line_pixels = nil
55
+ end
56
+
57
+ @counter += 1
58
+ fetch(row)
59
+ decode_line
60
+ end
61
+
62
+ def pixel(raster_x)
63
+ return nil unless @line_pixels
64
+
65
+ dist = raster_x - leftmost
66
+ dist += @width if dist.negative?
67
+ dist < pixel_width ? @line_pixels[dist] : nil
68
+ end
69
+
70
+ private
71
+
72
+ # Decode the fetched 24 data bits into a buffer of pixel colors (nil is
73
+ # transparent), so compositing can read pixels without re-deriving them.
74
+ def decode_line(pixels = @pixel_buffer)
75
+ @line_pixels = pixels
76
+ if multicolor?
77
+ decode_multicolor(pixels, x_expanded?)
78
+ else
79
+ decode_hires(pixels, x_expanded?)
80
+ end
81
+ end
82
+
83
+ def decode_hires(pixels, expanded)
84
+ own = color
85
+ last = expanded ? 48 : 24
86
+ i = 0
87
+ while i < last
88
+ offset = expanded ? i >> 1 : i
89
+ pixels[i] = (@bits >> (23 - offset)).anybits?(1) ? own : nil
90
+ i += 1
91
+ end
92
+ end
93
+
94
+ def decode_multicolor(pixels, expanded)
95
+ shared1 = @registers[0x25] & 0x0f
96
+ shared2 = @registers[0x26] & 0x0f
97
+ own = color
98
+ last = expanded ? 48 : 24
99
+ i = 0
100
+ while i < last
101
+ offset = expanded ? i >> 1 : i
102
+ pixels[i] = case (@bits >> (22 - (offset & ~1))) & 0b11
103
+ when 0b01 then shared1
104
+ when 0b10 then own
105
+ when 0b11 then shared2
106
+ end
107
+ i += 1
108
+ end
109
+ end
110
+
111
+ def fetch(row)
112
+ base = (pointer * 64) + (row * 3)
113
+ @bits = (@bank.peek(base) << 16) |
114
+ (@bank.peek(base + 1) << 8) |
115
+ @bank.peek(base + 2)
116
+ end
117
+
118
+ def pointer = @bank.peek(@registers.screen_base + 0x3f8 + index)
119
+ end
120
+ end
121
+ end
@@ -0,0 +1,112 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "badline/vic/sprite"
4
+
5
+ module Badline
6
+ class VIC < Cycleable
7
+ class Sprites
8
+ # $D019 latch bits raised by the collision registers.
9
+ SPRITE_COLLISION_IRQ = 0x04 # IMMC, mirrors $D01E
10
+ DATA_COLLISION_IRQ = 0x02 # IMBC, mirrors $D01F
11
+
12
+ def initialize(registers, bank, width)
13
+ @registers = registers
14
+ @bank = bank
15
+ @width = width
16
+ @sprites = Array.new(8) { |i| Sprite.new(i, registers, bank, width) }
17
+ @hits = Array.new(width, 0)
18
+ @win_color = Array.new(width, 0)
19
+ @win_priority = Array.new(width, false)
20
+ end
21
+
22
+ def [](index) = @sprites[index]
23
+
24
+ def start_line(line)
25
+ @sprites.each { |sprite| sprite.start_line(line) }
26
+ end
27
+
28
+ def active? = @sprites.any?(&:displaying?)
29
+
30
+ # Merge each displaying sprite's line into the scratch buffers, then
31
+ # apply the winners over the background in a single pass over the
32
+ # touched span.
33
+ def composite(colors, mask)
34
+ @sprite_clash = 0
35
+ @data_clash = 0
36
+ lo = @width
37
+ hi = 0
38
+
39
+ @sprites.each do |sprite|
40
+ next unless sprite.displaying?
41
+
42
+ seg_lo, seg_hi = merge(sprite, mask)
43
+ lo = seg_lo if seg_lo < lo
44
+ hi = seg_hi if seg_hi > hi
45
+ end
46
+
47
+ apply(colors, mask, lo, hi)
48
+ register_collisions
49
+ end
50
+
51
+ private
52
+
53
+ # Write one sprite's pixels into the scratch line. The first sprite to
54
+ # claim a pixel wins (lowest index has priority); later hits only
55
+ # accumulate collision bits.
56
+ def merge(sprite, mask)
57
+ left = sprite.leftmost
58
+ pixels = sprite.line_pixels
59
+ last = sprite.pixel_width
60
+ bit = 1 << sprite.index
61
+ priority = sprite.priority?
62
+
63
+ i = 0
64
+ while i < last
65
+ color = pixels[i]
66
+ if color
67
+ x = left + i
68
+ x -= @width if x >= @width
69
+ merge_pixel(x, color, bit, priority, mask)
70
+ end
71
+ i += 1
72
+ end
73
+
74
+ right = left + last
75
+ right > @width ? [0, @width] : [left, right]
76
+ end
77
+
78
+ def merge_pixel(pos, color, bit, priority, mask)
79
+ bits = @hits[pos]
80
+ if bits.zero?
81
+ @win_color[pos] = color
82
+ @win_priority[pos] = priority
83
+ else
84
+ @sprite_clash |= bits | bit
85
+ end
86
+ @hits[pos] = bits | bit
87
+ @data_clash |= bit if mask[pos]
88
+ end
89
+
90
+ def apply(colors, mask, from, upto)
91
+ pos = from
92
+ while pos < upto
93
+ if @hits[pos].nonzero?
94
+ colors[pos] = @win_color[pos] unless @win_priority[pos] && mask[pos]
95
+ @hits[pos] = 0
96
+ end
97
+ pos += 1
98
+ end
99
+ end
100
+
101
+ def register_collisions
102
+ if @sprite_clash.nonzero? && @registers.collide!(0x1e, @sprite_clash)
103
+ @registers.latch_irq!(SPRITE_COLLISION_IRQ)
104
+ end
105
+ return if @data_clash.zero?
106
+ return unless @registers.collide!(0x1f, @data_clash)
107
+
108
+ @registers.latch_irq!(DATA_COLLISION_IRQ)
109
+ end
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,192 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "badline/vic/bank"
4
+ require "badline/vic/registers"
5
+ require "badline/vic/display_state"
6
+ require "badline/vic/sequencer"
7
+ require "badline/vic/sprites"
8
+
9
+ module Badline
10
+ class VIC < Cycleable
11
+ include Addressable
12
+ include IntegerHelper
13
+
14
+ attr_reader :address_bus, :display, :width, :height, :vic_bank, :column,
15
+ :rasterline, :dirty_lines
16
+
17
+ SPRITE_BA_RANGES = [
18
+ 55..59, 57..61, 59..62,
19
+ 0..2, 0..4, 2..6, 4..8, 6..10
20
+ ].freeze
21
+
22
+ def initialize(address_bus = nil, debug: false)
23
+ addressable_at(0xd000, length: 2**10)
24
+ @address_bus = address_bus || AddressBus.new
25
+ @vic_bank = VIC::Bank.new(@address_bus)
26
+ @debug = debug
27
+
28
+ @width = 504
29
+ @height = 312
30
+
31
+ @registers = VIC::Registers.new
32
+ @display_state = VIC::DisplayState.new(@registers)
33
+ @sequencer = VIC::Sequencer.new(@width, @registers, @vic_bank)
34
+ @sprites = VIC::Sprites.new(@registers, @vic_bank, @width)
35
+ @display = Array.new(@width * @height, 0)
36
+ @dirty_lines = Array.new(@height, true)
37
+
38
+ @column = 0
39
+ @rasterline = 0
40
+ @columns_per_line = @width / 8
41
+ @last_line = @height - 1
42
+
43
+ @character_buffer = Array.new(40, 0)
44
+ @color_buffer = Array.new(40, 0)
45
+ @sprite_ba = Array.new(@width / 8, false)
46
+
47
+ super()
48
+ end
49
+
50
+ def cycle!
51
+ if @column.zero?
52
+ @display_state.new_frame if @rasterline.zero?
53
+ check_raster_irq!
54
+ @sequencer.new_line(@rasterline)
55
+ @sprites.start_line(@rasterline)
56
+ rebuild_sprite_ba
57
+ @display_state.new_line
58
+ end
59
+
60
+ @display_state.cycle(@rasterline, @column)
61
+
62
+ fetch_character_data! if dma_active?
63
+
64
+ draw!
65
+
66
+ @column += 1
67
+ if @column == @columns_per_line
68
+ finish_line!
69
+ @column = 0
70
+ @rasterline = @rasterline == @last_line ? 0 : @rasterline + 1
71
+ end
72
+ nil
73
+ end
74
+
75
+ # The IRQ line is held asserted while any enabled latch bit is set in
76
+ # $D019/$D01A, until the program acknowledges it by writing to $D019.
77
+ def interrupted?
78
+ @registers.irq_line?
79
+ end
80
+
81
+ def peek(addr)
82
+ i = index(addr) % (2**6)
83
+ case i
84
+ when 0x11 then (@registers[0x11] & 0x7f) | ((rasterline & 0x100) >> 1)
85
+ when 0x12 then rasterline & 0xff
86
+ when 0x19 then irq_status # Latch + master IRQ bit, unused bits read 1
87
+ else @registers.read(i)
88
+ end
89
+ end
90
+
91
+ def poke(addr, value)
92
+ @registers.write(index(addr) % (2**6), value)
93
+ end
94
+
95
+ def position
96
+ (@rasterline * @width) + (@column * 8)
97
+ end
98
+
99
+ def dma_active?
100
+ @display_state.bad_line? && @column >= 15 && @column < 55
101
+ end
102
+
103
+ def ba_low?
104
+ return true if @sprite_ba[@column]
105
+
106
+ @display_state.bad_line? && @column >= 13 && @column < 56
107
+ end
108
+
109
+ def hblank?
110
+ @column < 10 || @column > 60
111
+ end
112
+
113
+ def vblank?
114
+ @rasterline < 16 || @rasterline > 299
115
+ end
116
+
117
+ def blanking?
118
+ @rasterline < 16 || @rasterline > 299 || @column < 10 || @column > 60
119
+ end
120
+
121
+ # Reset the dirty flags once the frontend has consumed them.
122
+ def clear_dirty_lines!
123
+ @dirty_lines.fill(false)
124
+ end
125
+
126
+ private
127
+
128
+ def rebuild_sprite_ba
129
+ @sprite_ba.fill(false)
130
+ SPRITE_BA_RANGES.each_with_index do |range, n|
131
+ next unless @sprites[n].displaying?
132
+
133
+ range.each { |c| @sprite_ba[c] = true }
134
+ end
135
+ end
136
+
137
+ def draw!
138
+ return if blanking?
139
+
140
+ col = @column - 16
141
+ unless @display_state.display?
142
+ @sequencer.emit_idle(col)
143
+ return
144
+ end
145
+
146
+ cell = (@display_state.vc_base + col) & 0x3ff
147
+ screencode = @character_buffer[col] || 0
148
+ @sequencer.emit(screencode, @color_buffer[col] || 1, col,
149
+ cell, @display_state.rc)
150
+ end
151
+
152
+ def finish_line!
153
+ return if vblank?
154
+
155
+ # Composite the active sprites over the finished background line
156
+ # and copy the line into the frame display.
157
+ if @sprites.active?
158
+ @sprites.composite(@sequencer.colors, @sequencer.fg)
159
+ @sequencer.apply_border
160
+ end
161
+ base = @rasterline * @width
162
+ colors = @sequencer.colors
163
+ return if @display[base, @width] == colors
164
+
165
+ @display[base, @width] = colors
166
+ @dirty_lines[@rasterline] = true
167
+ end
168
+
169
+ def video_matrix(index)
170
+ vic_bank.peek(@registers.screen_base + index)
171
+ end
172
+
173
+ def check_raster_irq!
174
+ return unless @rasterline == @registers.raster_target
175
+
176
+ # Latch the raster IRQ flag. The line asserts via #interrupted? when the
177
+ # matching mask bit in $D01A is set.
178
+ @registers.latch_raster_irq!
179
+ end
180
+
181
+ def irq_status
182
+ (@registers[0x19] & 0x0f) | 0x70 | (interrupted? ? 0x80 : 0)
183
+ end
184
+
185
+ def fetch_character_data!
186
+ vmli = @column - 15
187
+ vc = (@display_state.vc_base + vmli) & 0x3ff
188
+ @character_buffer[vmli] = video_matrix(vc)
189
+ @color_buffer[vmli] = vic_bank.peek_color(vc)
190
+ end
191
+ end
192
+ end
data/lib/badline.rb ADDED
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "badline/version"
4
+ require "badline/integer_helper"
5
+ require "badline/addressable"
6
+ require "badline/memory"
7
+ require "badline/color_memory"
8
+ require "badline/rom"
9
+ require "badline/address_bus"
10
+ require "badline/instruction"
11
+ require "badline/instruction_set"
12
+ require "badline/status"
13
+ require "badline/keyboard"
14
+ require "badline/joystick"
15
+ require "badline/control_ports"
16
+ require "badline/cycleable"
17
+ require "badline/time_of_day"
18
+ require "badline/cia"
19
+ require "badline/sid"
20
+ require "badline/traps"
21
+ require "badline/cpu"
22
+ require "badline/vic"
23
+ require "badline/keyboard_buffer"
24
+ require "badline/computer"
25
+ require "badline/storage"
26
+ require "badline/cartridge"
27
+ require "badline/kernal_trap"
28
+ require "badline/chrout_trap"
29
+ require "badline/media"
metadata ADDED
@@ -0,0 +1,131 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: badline
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Inge Jørgensen
8
+ bindir: exe
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: ruby-sdl2
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: '0.3'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - "~>"
24
+ - !ruby/object:Gem::Version
25
+ version: '0.3'
26
+ description: Badline is a Commodore 64 emulator written in Ruby, implementing cycle-accurate
27
+ timing and hardware behavior. Supports PRG/P00 programs, D64/D71/D81 disk images
28
+ and CRT cartridges.
29
+ email:
30
+ - inge@elektronaut.no
31
+ executables:
32
+ - badline
33
+ extensions: []
34
+ extra_rdoc_files: []
35
+ files:
36
+ - MIT-LICENSE
37
+ - README.md
38
+ - exe/badline
39
+ - lib/badline.rb
40
+ - lib/badline/address_bus.rb
41
+ - lib/badline/addressable.rb
42
+ - lib/badline/cartridge.rb
43
+ - lib/badline/cartridge/magic_desk.rb
44
+ - lib/badline/cartridge/ocean.rb
45
+ - lib/badline/cartridge/standard.rb
46
+ - lib/badline/chrout_trap.rb
47
+ - lib/badline/cia.rb
48
+ - lib/badline/cia/timer.rb
49
+ - lib/badline/color_memory.rb
50
+ - lib/badline/computer.rb
51
+ - lib/badline/control_ports.rb
52
+ - lib/badline/cpu.rb
53
+ - lib/badline/cycleable.rb
54
+ - lib/badline/gui.rb
55
+ - lib/badline/gui/application.rb
56
+ - lib/badline/gui/joy_map.rb
57
+ - lib/badline/gui/key_map.rb
58
+ - lib/badline/gui/palette.rb
59
+ - lib/badline/gui/pane.rb
60
+ - lib/badline/gui/screen_pane.rb
61
+ - lib/badline/gui/window.rb
62
+ - lib/badline/instruction.rb
63
+ - lib/badline/instruction_set.rb
64
+ - lib/badline/instruction_set/arithmetic.rb
65
+ - lib/badline/instruction_set/bitwise.rb
66
+ - lib/badline/instruction_set/branch.rb
67
+ - lib/badline/instruction_set/flag.rb
68
+ - lib/badline/instruction_set/illegal.rb
69
+ - lib/badline/instruction_set/inc_dec.rb
70
+ - lib/badline/instruction_set/stack.rb
71
+ - lib/badline/instruction_set/transfer.rb
72
+ - lib/badline/integer_helper.rb
73
+ - lib/badline/joystick.rb
74
+ - lib/badline/kernal_trap.rb
75
+ - lib/badline/kernal_trap/file.rb
76
+ - lib/badline/kernal_trap/load.rb
77
+ - lib/badline/kernal_trap/save.rb
78
+ - lib/badline/keyboard.rb
79
+ - lib/badline/keyboard_buffer.rb
80
+ - lib/badline/media.rb
81
+ - lib/badline/memory.rb
82
+ - lib/badline/rom.rb
83
+ - lib/badline/roms/README
84
+ - lib/badline/roms/basic.rom
85
+ - lib/badline/roms/character.rom
86
+ - lib/badline/roms/kernal.rom
87
+ - lib/badline/sid.rb
88
+ - lib/badline/status.rb
89
+ - lib/badline/storage.rb
90
+ - lib/badline/storage/crt_file.rb
91
+ - lib/badline/storage/d64_image.rb
92
+ - lib/badline/storage/d71_image.rb
93
+ - lib/badline/storage/d81_image.rb
94
+ - lib/badline/storage/disk_image.rb
95
+ - lib/badline/storage/host_directory.rb
96
+ - lib/badline/storage/p00.rb
97
+ - lib/badline/time_of_day.rb
98
+ - lib/badline/traps.rb
99
+ - lib/badline/version.rb
100
+ - lib/badline/vic.rb
101
+ - lib/badline/vic/bank.rb
102
+ - lib/badline/vic/display_state.rb
103
+ - lib/badline/vic/graphics_mode.rb
104
+ - lib/badline/vic/registers.rb
105
+ - lib/badline/vic/sequencer.rb
106
+ - lib/badline/vic/sprite.rb
107
+ - lib/badline/vic/sprites.rb
108
+ homepage: https://github.com/elektronaut/badline
109
+ licenses:
110
+ - MIT
111
+ metadata:
112
+ source_code_uri: https://github.com/elektronaut/badline
113
+ rubygems_mfa_required: 'true'
114
+ rdoc_options: []
115
+ require_paths:
116
+ - lib
117
+ required_ruby_version: !ruby/object:Gem::Requirement
118
+ requirements:
119
+ - - ">="
120
+ - !ruby/object:Gem::Version
121
+ version: '4.0'
122
+ required_rubygems_version: !ruby/object:Gem::Requirement
123
+ requirements:
124
+ - - ">="
125
+ - !ruby/object:Gem::Version
126
+ version: '0'
127
+ requirements: []
128
+ rubygems_version: 4.0.3
129
+ specification_version: 4
130
+ summary: A cycle-accurate Commodore 64 emulator
131
+ test_files: []