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,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ class Cycleable
5
+ attr_reader :cycles
6
+
7
+ def initialize
8
+ @loop = Fiber.new { loop { main_loop } }
9
+ @cycles = 0
10
+ @pending_write = false
11
+ end
12
+
13
+ def cycle!
14
+ @loop.resume
15
+ nil
16
+ end
17
+
18
+ def pending_write? = @pending_write
19
+
20
+ private
21
+
22
+ def cycle(write: false)
23
+ @pending_write = write
24
+ Fiber.yield
25
+ result = yield if block_given?
26
+ @cycles += 1
27
+ @pending_write = false
28
+ result
29
+ end
30
+
31
+ def main_loop
32
+ Fiber.yield
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,94 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ module GUI
5
+ class Application
6
+ PAL_CLOCK_HZ = 985_248
7
+ TITLE = "Badline"
8
+ TOGGLE_SYM = SDL2::Key::TAB
9
+
10
+ SHARED_KEYS = %i[up left cursor_h cursor_v space].freeze
11
+
12
+ def initialize(media_path: nil, autostart: true, debug: false)
13
+ @computer = Computer.new(debug:)
14
+ puts Media.attach(@computer, media_path, autostart:) if media_path
15
+
16
+ @joystick_mode = false
17
+ @panes = [ScreenPane.new(@computer)]
18
+ @window = Window.new(
19
+ title: TITLE,
20
+ width: canvas_width, height: canvas_height,
21
+ vsync: ENV["NOVSYNC"].nil?
22
+ )
23
+
24
+ rate = @window.refresh_rate
25
+ @cycles_per_frame = PAL_CLOCK_HZ / rate
26
+ puts "Display #{rate} Hz -> #{@cycles_per_frame} cycles/frame"
27
+ end
28
+
29
+ def run
30
+ @running = true
31
+ while @running
32
+ handle_events
33
+ @cycles_per_frame.times { @computer.cycle! }
34
+ @window.draw(@panes)
35
+ end
36
+ ensure
37
+ puts @computer.cpu.inspect
38
+ end
39
+
40
+ private
41
+
42
+ def handle_events
43
+ while (event = SDL2::Event.poll)
44
+ case event
45
+ when SDL2::Event::Quit
46
+ @running = false
47
+ when SDL2::Event::KeyDown
48
+ handle_key_down(event)
49
+ when SDL2::Event::KeyUp
50
+ handle_key_up(event)
51
+ end
52
+ end
53
+ end
54
+
55
+ def handle_key_down(event)
56
+ if event.sym == TOGGLE_SYM
57
+ toggle_joystick_mode
58
+ elsif @joystick_mode && (dir = JoyMap.parse(event))
59
+ @computer.joystick2.press(dir)
60
+ else
61
+ @computer.keyboard.press(KeyMap.parse(event))
62
+ end
63
+ end
64
+
65
+ def handle_key_up(event)
66
+ return if event.sym == TOGGLE_SYM
67
+
68
+ if @joystick_mode && (dir = JoyMap.parse(event))
69
+ @computer.joystick2.release(dir)
70
+ else
71
+ @computer.keyboard.release(KeyMap.parse(event))
72
+ end
73
+ end
74
+
75
+ def toggle_joystick_mode
76
+ @joystick_mode = !@joystick_mode
77
+ if @joystick_mode
78
+ SHARED_KEYS.each { |key| @computer.keyboard.release(key) }
79
+ else
80
+ Joystick::DIRECTIONS.each_key { |dir| @computer.joystick2.release(dir) }
81
+ end
82
+ @window.title = @joystick_mode ? "#{TITLE} [JOY]" : TITLE
83
+ end
84
+
85
+ def canvas_width
86
+ @panes.map { |pane| pane.left + pane.width }.max
87
+ end
88
+
89
+ def canvas_height
90
+ @panes.map { |pane| pane.top + pane.height }.max
91
+ end
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ module GUI
5
+ class JoyMap
6
+ MAP = {
7
+ "Up" => :up,
8
+ "Down" => :down,
9
+ "Left" => :left,
10
+ "Right" => :right,
11
+ "Space" => :fire
12
+ }.freeze
13
+
14
+ def self.parse(event)
15
+ MAP[SDL2::Key.name_of(event.sym)]
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ module GUI
5
+ # Translates SDL key events into the emulator's key symbols.
6
+ #
7
+ # MAP is just the overrides, anything not covered simply falls though.
8
+ class KeyMap
9
+ MAP = {
10
+ "Backspace" => :delete,
11
+ "Return" => :return,
12
+ "Right" => :cursor_h,
13
+ "Down" => :cursor_v,
14
+ "Space" => :space,
15
+ "Left Ctrl" => :control,
16
+ "Left Alt" => :cbm,
17
+ "Left Shift" => :lshift,
18
+ "Right Shift" => :rshift,
19
+ "Home" => :clr_home,
20
+ "Escape" => :run_stop,
21
+ "Backslash" => :"@",
22
+ "'" => :":",
23
+ "End" => :£,
24
+ "Keypad +" => :+,
25
+ "Keypad *" => :*
26
+ }.freeze
27
+
28
+ def self.parse(event)
29
+ name = SDL2::Key.name_of(event.sym)
30
+ MAP[name] || name.downcase.to_sym
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ module GUI
5
+ class Palette
6
+ COLORS = [
7
+ "#000000", "#FFFFFF", "#924A40", "#84C5CC",
8
+ "#9351B6", "#72B14B", "#483AAA", "#D5DF7C",
9
+ "#675200", "#C33D00", "#C18178", "#606060",
10
+ "#8A8A8A", "#B3EC91", "#867ADE", "#B3B3B3"
11
+ ].freeze
12
+
13
+ attr_reader :dwords
14
+
15
+ def initialize
16
+ @dwords = COLORS.map { |hex| dword(hex) }.freeze
17
+ @entries = @dwords.map { |rgba| [rgba].pack("V") }.freeze
18
+ end
19
+
20
+ def [](color)
21
+ @entries[color]
22
+ end
23
+
24
+ private
25
+
26
+ # Color as a little-endian RGBA dword.
27
+ def dword(hex)
28
+ r = hex[1, 2].to_i(16)
29
+ g = hex[3, 2].to_i(16)
30
+ b = hex[5, 2].to_i(16)
31
+ (255 << 24) | (b << 16) | (g << 8) | r
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ module GUI
5
+ class Pane
6
+ attr_reader :left, :top, :width, :height
7
+
8
+ def initialize(width:, height:, left: 0, top: 0)
9
+ @width = width
10
+ @height = height
11
+ @left = left
12
+ @top = top
13
+ @rect = SDL2::Rect.new(left, top, width, height)
14
+ end
15
+
16
+ def render(_renderer)
17
+ raise NotImplementedError, "#{self.class} must implement #render"
18
+ end
19
+
20
+ private
21
+
22
+ def blit(renderer, pixels)
23
+ surface = SDL2::Surface.from_string(
24
+ pixels, width, height, 32, width * 4,
25
+ 0x0000_00ff, 0x0000_ff00, 0x00ff_0000, 0xff00_0000
26
+ )
27
+ texture = renderer.create_texture_from(surface)
28
+ renderer.copy(texture, nil, @rect)
29
+ ensure
30
+ texture&.destroy
31
+ surface&.destroy
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ module GUI
5
+ class ScreenPane < Pane
6
+ WIDTH = 384
7
+ HEIGHT = 272
8
+ COL_OFFSET = 96
9
+ ROW_OFFSET = 20
10
+
11
+ ROW_BYTES = WIDTH * 4
12
+
13
+ def initialize(computer, left: 0, top: 0, palette: Palette.new)
14
+ super(width: WIDTH, height: HEIGHT, left:, top:)
15
+ @computer = computer
16
+ @palette = palette.dwords
17
+ @buffer = ("\x00" * (HEIGHT * ROW_BYTES)).b
18
+ end
19
+
20
+ def render(renderer)
21
+ blit(renderer, framebuffer)
22
+ end
23
+
24
+ private
25
+
26
+ # Repack only the scanlines the VIC has touched since the last frame.
27
+ def framebuffer
28
+ vic = @computer.vic
29
+ display = vic.display
30
+ vic_width = vic.width
31
+ dirty = vic.dirty_lines
32
+
33
+ HEIGHT.times do |row|
34
+ next unless dirty[row + ROW_OFFSET]
35
+
36
+ @buffer[row * ROW_BYTES, ROW_BYTES] =
37
+ pack_row(display, vic_width, row)
38
+ end
39
+ vic.clear_dirty_lines!
40
+ @buffer
41
+ end
42
+
43
+ def pack_row(display, vic_width, row)
44
+ line = display[((row + ROW_OFFSET) * vic_width) + COL_OFFSET, WIDTH]
45
+ line.map! { |c| @palette[c] }
46
+ line.pack("V*")
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ module GUI
5
+ class Window
6
+ DEFAULT_REFRESH_RATE = 60
7
+
8
+ attr_reader :renderer
9
+
10
+ def initialize(title:, width:, height:, scale: 2, vsync: true)
11
+ SDL2.init(SDL2::INIT_VIDEO | SDL2::INIT_EVENTS)
12
+ SDL2::Hints["SDL_RENDER_SCALE_QUALITY"] = "0" # nearest-neighbour
13
+
14
+ @window = SDL2::Window.create(
15
+ title,
16
+ SDL2::Window::POS_CENTERED, SDL2::Window::POS_CENTERED,
17
+ width * scale, height * scale,
18
+ SDL2::Window::Flags::RESIZABLE
19
+ )
20
+
21
+ flags = SDL2::Renderer::Flags::ACCELERATED
22
+ flags |= SDL2::Renderer::Flags::PRESENTVSYNC if vsync
23
+ @renderer = @window.create_renderer(-1, flags)
24
+ @renderer.logical_size = [width, height]
25
+ end
26
+
27
+ def title=(title)
28
+ @window.title = title
29
+ end
30
+
31
+ def refresh_rate
32
+ rate = SDL2::Display.displays.first.current_mode.refresh_rate
33
+ rate.positive? ? rate : DEFAULT_REFRESH_RATE
34
+ rescue StandardError
35
+ DEFAULT_REFRESH_RATE
36
+ end
37
+
38
+ def draw(panes)
39
+ @renderer.draw_color = [0, 0, 0]
40
+ @renderer.clear
41
+ panes.each { |pane| pane.render(@renderer) }
42
+ @renderer.present
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "sdl2"
4
+
5
+ require "badline/gui/palette"
6
+ require "badline/gui/key_map"
7
+ require "badline/gui/joy_map"
8
+ require "badline/gui/pane"
9
+ require "badline/gui/screen_pane"
10
+ require "badline/gui/window"
11
+ require "badline/gui/application"
@@ -0,0 +1,334 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ class Instruction
5
+ attr_reader :name, :addressing_mode, :operand_length
6
+
7
+ def initialize(name, addressing_mode,
8
+ illegal: false, boundary_cycle: false)
9
+ @name = name
10
+ @addressing_mode = addressing_mode
11
+ @boundary_cycle = boundary_cycle
12
+ @illegal = illegal
13
+ @operand_length = compute_operand_length
14
+ end
15
+
16
+ class << self
17
+ def map
18
+ @map ||= {
19
+ 0x00 => new(:brk, :implied),
20
+ 0x01 => new(:ora, :indirect_x),
21
+ 0x02 => new(:jam, :implied, illegal: true),
22
+ 0x03 => new(:slo, :indirect_x, illegal: true),
23
+ 0x04 => new(:nop, :zeropage, illegal: true),
24
+ 0x05 => new(:ora, :zeropage),
25
+ 0x06 => new(:asl, :zeropage),
26
+ 0x07 => new(:slo, :zeropage, illegal: true),
27
+ 0x08 => new(:php, :implied),
28
+ 0x09 => new(:ora, :immediate),
29
+ 0x0a => new(:asl, :accumulator),
30
+ 0x0b => new(:anc, :immediate, illegal: true),
31
+ 0x0c => new(:nop, :absolute, illegal: true),
32
+ 0x0d => new(:ora, :absolute),
33
+ 0x0e => new(:asl, :absolute),
34
+ 0x0f => new(:slo, :absolute, illegal: true),
35
+
36
+ 0x10 => new(:bpl, :relative),
37
+ 0x11 => new(:ora, :indirect_y, boundary_cycle: true),
38
+ 0x12 => new(:jam, :implied, illegal: true),
39
+ 0x13 => new(:slo, :indirect_y, illegal: true),
40
+ 0x14 => new(:nop, :zeropage_x, illegal: true),
41
+ 0x15 => new(:ora, :zeropage_x),
42
+ 0x16 => new(:asl, :zeropage_x),
43
+ 0x17 => new(:slo, :zeropage_x, illegal: true),
44
+ 0x18 => new(:clc, :implied),
45
+ 0x19 => new(:ora, :absolute_y, boundary_cycle: true),
46
+ 0x1a => new(:nop, :implied, illegal: true),
47
+ 0x1b => new(:slo, :absolute_y, illegal: true),
48
+ 0x1c => new(:nop, :absolute_x, boundary_cycle: true, illegal: true),
49
+ 0x1d => new(:ora, :absolute_x, boundary_cycle: true),
50
+ 0x1e => new(:asl, :absolute_x),
51
+ 0x1f => new(:slo, :absolute_x, illegal: true),
52
+
53
+ 0x20 => new(:jsr, :absolute),
54
+ 0x21 => new(:and, :indirect_x),
55
+ 0x22 => new(:jam, :implied, illegal: true),
56
+ 0x23 => new(:rla, :indirect_x, illegal: true),
57
+ 0x24 => new(:bit, :zeropage),
58
+ 0x25 => new(:and, :zeropage),
59
+ 0x26 => new(:rol, :zeropage),
60
+ 0x27 => new(:rla, :zeropage, illegal: true),
61
+ 0x28 => new(:plp, :implied),
62
+ 0x29 => new(:and, :immediate),
63
+ 0x2a => new(:rol, :accumulator),
64
+ 0x2b => new(:anc, :immediate, illegal: true),
65
+ 0x2c => new(:bit, :absolute),
66
+ 0x2d => new(:and, :absolute),
67
+ 0x2e => new(:rol, :absolute),
68
+ 0x2f => new(:rla, :absolute, illegal: true),
69
+
70
+ 0x30 => new(:bmi, :relative),
71
+ 0x31 => new(:and, :indirect_y, boundary_cycle: true),
72
+ 0x32 => new(:jam, :implied, illegal: true),
73
+ 0x33 => new(:rla, :indirect_y, illegal: true),
74
+ 0x34 => new(:nop, :zeropage_x, illegal: true),
75
+ 0x35 => new(:and, :zeropage_x),
76
+ 0x36 => new(:rol, :zeropage_x),
77
+ 0x37 => new(:rla, :zeropage_x, illegal: true),
78
+ 0x38 => new(:sec, :implied),
79
+ 0x39 => new(:and, :absolute_y, boundary_cycle: true),
80
+ 0x3a => new(:nop, :implied, illegal: true),
81
+ 0x3b => new(:rla, :absolute_y, illegal: true),
82
+ 0x3c => new(:nop, :absolute_x, boundary_cycle: true, illegal: true),
83
+ 0x3d => new(:and, :absolute_x, boundary_cycle: true),
84
+ 0x3e => new(:rol, :absolute_x),
85
+ 0x3f => new(:rla, :absolute_x, illegal: true),
86
+
87
+ 0x40 => new(:rti, :implied),
88
+ 0x41 => new(:eor, :indirect_x),
89
+ 0x42 => new(:jam, :implied, illegal: true),
90
+ 0x43 => new(:sre, :indirect_x, illegal: true),
91
+ 0x44 => new(:nop, :zeropage, illegal: true),
92
+ 0x45 => new(:eor, :zeropage),
93
+ 0x46 => new(:lsr, :zeropage),
94
+ 0x47 => new(:sre, :zeropage, illegal: true),
95
+ 0x48 => new(:pha, :implied),
96
+ 0x49 => new(:eor, :immediate),
97
+ 0x4a => new(:lsr, :accumulator),
98
+ 0x4b => new(:alr, :immediate, illegal: true),
99
+ 0x4c => new(:jmp, :absolute),
100
+ 0x4d => new(:eor, :absolute),
101
+ 0x4e => new(:lsr, :absolute),
102
+ 0x4f => new(:sre, :absolute, illegal: true),
103
+
104
+ 0x50 => new(:bvc, :relative),
105
+ 0x51 => new(:eor, :indirect_y, boundary_cycle: true),
106
+ 0x52 => new(:jam, :implied, illegal: true),
107
+ 0x53 => new(:sre, :indirect_y, illegal: true),
108
+ 0x54 => new(:nop, :zeropage_x, illegal: true),
109
+ 0x55 => new(:eor, :zeropage_x),
110
+ 0x56 => new(:lsr, :zeropage_x),
111
+ 0x57 => new(:sre, :zeropage_x, illegal: true),
112
+ 0x58 => new(:cli, :implied),
113
+ 0x59 => new(:eor, :absolute_y, boundary_cycle: true),
114
+ 0x5a => new(:nop, :implied, illegal: true),
115
+ 0x5b => new(:sre, :absolute_y, illegal: true),
116
+ 0x5c => new(:nop, :absolute_x, boundary_cycle: true, illegal: true),
117
+ 0x5d => new(:eor, :absolute_x, boundary_cycle: true),
118
+ 0x5e => new(:lsr, :absolute_x),
119
+ 0x5f => new(:sre, :absolute_x, illegal: true),
120
+
121
+ 0x60 => new(:rts, :implied),
122
+ 0x61 => new(:adc, :indirect_x),
123
+ 0x62 => new(:jam, :implied, illegal: true),
124
+ 0x63 => new(:rra, :indirect_x, illegal: true),
125
+ 0x64 => new(:nop, :zeropage, illegal: true),
126
+ 0x65 => new(:adc, :zeropage),
127
+ 0x66 => new(:ror, :zeropage),
128
+ 0x67 => new(:rra, :zeropage, illegal: true),
129
+ 0x68 => new(:pla, :implied),
130
+ 0x69 => new(:adc, :immediate),
131
+ 0x6a => new(:ror, :accumulator),
132
+ 0x6b => new(:arr, :immediate, illegal: true),
133
+ 0x6c => new(:jmp, :indirect),
134
+ 0x6d => new(:adc, :absolute),
135
+ 0x6e => new(:ror, :absolute),
136
+ 0x6f => new(:rra, :absolute, illegal: true),
137
+
138
+ 0x70 => new(:bvs, :relative),
139
+ 0x71 => new(:adc, :indirect_y, boundary_cycle: true),
140
+ 0x72 => new(:jam, :implied, illegal: true),
141
+ 0x73 => new(:rra, :indirect_y, illegal: true),
142
+ 0x74 => new(:nop, :zeropage_x, illegal: true),
143
+ 0x75 => new(:adc, :zeropage_x),
144
+ 0x76 => new(:ror, :zeropage_x),
145
+ 0x77 => new(:rra, :zeropage_x, illegal: true),
146
+ 0x78 => new(:sei, :implied),
147
+ 0x79 => new(:adc, :absolute_y, boundary_cycle: true),
148
+ 0x7a => new(:nop, :implied, illegal: true),
149
+ 0x7b => new(:rra, :absolute_y, illegal: true),
150
+ 0x7c => new(:nop, :absolute_x, boundary_cycle: true, illegal: true),
151
+ 0x7d => new(:adc, :absolute_x, boundary_cycle: true),
152
+ 0x7e => new(:ror, :absolute_x),
153
+ 0x7f => new(:rra, :absolute_x, illegal: true),
154
+
155
+ 0x80 => new(:nop_nocycle, :immediate, illegal: true),
156
+ 0x81 => new(:sta, :indirect_x),
157
+ 0x82 => new(:nop_nocycle, :immediate, illegal: true),
158
+ 0x83 => new(:sax, :indirect_x, illegal: true),
159
+ 0x84 => new(:sty, :zeropage),
160
+ 0x85 => new(:sta, :zeropage),
161
+ 0x86 => new(:stx, :zeropage),
162
+ 0x87 => new(:sax, :zeropage, illegal: true),
163
+ 0x88 => new(:dey, :implied),
164
+ 0x89 => new(:nop_nocycle, :immediate, illegal: true),
165
+ 0x8a => new(:txa, :implied),
166
+ 0x8b => new(:ane, :immediate, illegal: true),
167
+ 0x8c => new(:sty, :absolute),
168
+ 0x8d => new(:sta, :absolute),
169
+ 0x8e => new(:stx, :absolute),
170
+ 0x8f => new(:sax, :absolute, illegal: true),
171
+
172
+ 0x90 => new(:bcc, :relative),
173
+ 0x91 => new(:sta, :indirect_y),
174
+ 0x92 => new(:jam, :implied, illegal: true),
175
+ 0x93 => new(:sha, :indirect_y, illegal: true),
176
+ 0x94 => new(:sty, :zeropage_x),
177
+ 0x95 => new(:sta, :zeropage_x),
178
+ 0x96 => new(:stx, :zeropage_y),
179
+ 0x97 => new(:sax, :zeropage_y, illegal: true),
180
+ 0x98 => new(:tya, :implied),
181
+ 0x99 => new(:sta, :absolute_y),
182
+ 0x9a => new(:txs, :implied),
183
+ 0x9b => new(:tas, :absolute_y, illegal: true),
184
+ 0x9c => new(:shy, :absolute_x, illegal: true),
185
+ 0x9d => new(:sta, :absolute_x),
186
+ 0x9e => new(:shx, :absolute_y, illegal: true),
187
+ 0x9f => new(:sha, :absolute_y, illegal: true),
188
+
189
+ 0xa0 => new(:ldy, :immediate),
190
+ 0xa1 => new(:lda, :indirect_x),
191
+ 0xa2 => new(:ldx, :immediate),
192
+ 0xa3 => new(:lax, :indirect_x, illegal: true),
193
+ 0xa4 => new(:ldy, :zeropage),
194
+ 0xa5 => new(:lda, :zeropage),
195
+ 0xa6 => new(:ldx, :zeropage),
196
+ 0xa7 => new(:lax, :zeropage, illegal: true),
197
+ 0xa8 => new(:tay, :implied),
198
+ 0xa9 => new(:lda, :immediate),
199
+ 0xaa => new(:tax, :implied),
200
+ 0xab => new(:lxa, :immediate, illegal: true),
201
+ 0xac => new(:ldy, :absolute),
202
+ 0xad => new(:lda, :absolute),
203
+ 0xae => new(:ldx, :absolute),
204
+ 0xaf => new(:lax, :absolute, illegal: true),
205
+
206
+ 0xb0 => new(:bcs, :relative),
207
+ 0xb1 => new(:lda, :indirect_y, boundary_cycle: true),
208
+ 0xb2 => new(:jam, :implied, illegal: true),
209
+ 0xb3 => new(:lax, :indirect_y, boundary_cycle: true, illegal: true),
210
+ 0xb4 => new(:ldy, :zeropage_x),
211
+ 0xb5 => new(:lda, :zeropage_x),
212
+ 0xb6 => new(:ldx, :zeropage_y),
213
+ 0xb7 => new(:lax, :zeropage_y, illegal: true),
214
+ 0xb8 => new(:clv, :implied),
215
+ 0xb9 => new(:lda, :absolute_y, boundary_cycle: true),
216
+ 0xba => new(:tsx, :implied),
217
+ 0xbb => new(:las, :absolute_y, boundary_cycle: true, illegal: true),
218
+ 0xbc => new(:ldy, :absolute_x, boundary_cycle: true),
219
+ 0xbd => new(:lda, :absolute_x, boundary_cycle: true),
220
+ 0xbe => new(:ldx, :absolute_y, boundary_cycle: true),
221
+ 0xbf => new(:lax, :absolute_y, boundary_cycle: true, illegal: true),
222
+
223
+ 0xc0 => new(:cpy, :immediate),
224
+ 0xc1 => new(:cmp, :indirect_x),
225
+ 0xc2 => new(:nop_nocycle, :immediate, illegal: true),
226
+ 0xc3 => new(:dcp, :indirect_x, illegal: true),
227
+ 0xc4 => new(:cpy, :zeropage),
228
+ 0xc5 => new(:cmp, :zeropage),
229
+ 0xc6 => new(:dec, :zeropage),
230
+ 0xc7 => new(:dcp, :zeropage, illegal: true),
231
+ 0xc8 => new(:iny, :implied),
232
+ 0xc9 => new(:cmp, :immediate),
233
+ 0xca => new(:dex, :implied),
234
+ 0xcb => new(:sbx, :immediate, illegal: true),
235
+ 0xcc => new(:cpy, :absolute),
236
+ 0xcd => new(:cmp, :absolute),
237
+ 0xce => new(:dec, :absolute),
238
+ 0xcf => new(:dcp, :absolute, illegal: true),
239
+
240
+ 0xd0 => new(:bne, :relative),
241
+ 0xd1 => new(:cmp, :indirect_y, boundary_cycle: true),
242
+ 0xd2 => new(:jam, :implied, illegal: true),
243
+ 0xd3 => new(:dcp, :indirect_y, illegal: true),
244
+ 0xd4 => new(:nop, :zeropage_x, illegal: true),
245
+ 0xd5 => new(:cmp, :zeropage_x),
246
+ 0xd6 => new(:dec, :zeropage_x),
247
+ 0xd7 => new(:dcp, :zeropage_x, illegal: true),
248
+ 0xd8 => new(:cld, :implied),
249
+ 0xd9 => new(:cmp, :absolute_y, boundary_cycle: true),
250
+ 0xda => new(:nop, :implied, illegal: true),
251
+ 0xdb => new(:dcp, :absolute_y, illegal: true),
252
+ 0xdc => new(:nop, :absolute_x, boundary_cycle: true, illegal: true),
253
+ 0xdd => new(:cmp, :absolute_x, boundary_cycle: true),
254
+ 0xde => new(:dec, :absolute_x),
255
+ 0xdf => new(:dcp, :absolute_x, illegal: true),
256
+
257
+ 0xe0 => new(:cpx, :immediate),
258
+ 0xe1 => new(:sbc, :indirect_x),
259
+ 0xe2 => new(:nop_nocycle, :immediate, illegal: true),
260
+ 0xe3 => new(:isc, :indirect_x, illegal: true),
261
+ 0xe4 => new(:cpx, :zeropage),
262
+ 0xe5 => new(:sbc, :zeropage),
263
+ 0xe6 => new(:inc, :zeropage),
264
+ 0xe7 => new(:isc, :zeropage, illegal: true),
265
+ 0xe8 => new(:inx, :implied),
266
+ 0xe9 => new(:sbc, :immediate),
267
+ 0xea => new(:nop, :implied),
268
+ 0xeb => new(:sbc, :immediate, illegal: true),
269
+ 0xec => new(:cpx, :absolute),
270
+ 0xed => new(:sbc, :absolute),
271
+ 0xee => new(:inc, :absolute),
272
+ 0xef => new(:isc, :absolute, illegal: true),
273
+
274
+ 0xf0 => new(:beq, :relative),
275
+ 0xf1 => new(:sbc, :indirect_y, boundary_cycle: true),
276
+ 0xf2 => new(:jam, :implied, illegal: true),
277
+ 0xf3 => new(:isc, :indirect_y, illegal: true),
278
+ 0xf4 => new(:nop, :zeropage_x, illegal: true),
279
+ 0xf5 => new(:sbc, :zeropage_x),
280
+ 0xf6 => new(:inc, :zeropage_x),
281
+ 0xf7 => new(:isc, :zeropage_x, illegal: true),
282
+ 0xf8 => new(:sed, :implied),
283
+ 0xf9 => new(:sbc, :absolute_y, boundary_cycle: true),
284
+ 0xfa => new(:nop, :implied, illegal: true),
285
+ 0xfb => new(:isc, :absolute_y, illegal: true),
286
+ 0xfc => new(:nop, :absolute_x, boundary_cycle: true, illegal: true),
287
+ 0xfd => new(:sbc, :absolute_x, boundary_cycle: true),
288
+ 0xfe => new(:inc, :absolute_x),
289
+ 0xff => new(:isc, :absolute_x, illegal: true)
290
+ }
291
+ end
292
+
293
+ def find(opcode)
294
+ table[opcode.to_i]
295
+ end
296
+
297
+ private
298
+
299
+ def table
300
+ @table ||= Array.new(256) { |opcode| map[opcode] }
301
+ end
302
+ end
303
+
304
+ def boundary_cycle?
305
+ @boundary_cycle
306
+ end
307
+
308
+ def illegal?
309
+ @illegal
310
+ end
311
+
312
+ def length
313
+ 1 + operand_length
314
+ end
315
+
316
+ def operand?
317
+ operand_length.positive?
318
+ end
319
+
320
+ private
321
+
322
+ def compute_operand_length
323
+ case addressing_mode
324
+ when :implied, :accumulator
325
+ 0
326
+ when :immediate, :relative, :zeropage, :zeropage_x, :zeropage_y,
327
+ :indirect_x, :indirect_y
328
+ 1
329
+ when :absolute, :absolute_x, :absolute_y, :indirect
330
+ 2
331
+ end
332
+ end
333
+ end
334
+ end