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,119 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ module InstructionSet
5
+ module Arithmetic
6
+ # Add memory with carry to accumulator.
7
+ #
8
+ # Opcodes:
9
+ # $61 - indirect_x - 6 cycles
10
+ # $65 - zeropage - 3 cycles
11
+ # $69 - immediate - 2 cycles
12
+ # $6D - absolute - 4 cycles
13
+ # $71 - indirect_y - 5+ cycles
14
+ # $75 - zeropage_x - 4 cycles
15
+ # $79 - absolute_y - 4+ cycles
16
+ # $7D - absolute_x - 4+ cycles
17
+ def adc(_addr, value)
18
+ v = resolve(value)
19
+ result = a + v + status.carry
20
+ status.zero = result.nobits?(0xff)
21
+
22
+ if status.decimal?
23
+ result = (a & 0x0f) + (v & 0x0f) + status.carry
24
+ result += 0x06 if result > 0x09
25
+ c = result > 0x0f ? 1 : 0
26
+ result = (a & 0xf0) + (v & 0xf0) + (c << 4) + (result & 0x0f)
27
+ update_calculation_flags(v, result)
28
+ result += 0x60 if result > 0x9f
29
+ else
30
+ update_calculation_flags(v, result)
31
+ end
32
+
33
+ status.carry = result > 0xff
34
+ @a = result & 0xff
35
+ end
36
+
37
+ # Compare memory with accumulator.
38
+ #
39
+ # Opcodes:
40
+ # $C1 - indirect_x - 6 cycles
41
+ # $C5 - zeropage - 3 cycles
42
+ # $C9 - immediate - 2 cycles
43
+ # $CD - absolute - 4 cycles
44
+ # $D1 - indirect_y - 5+ cycles
45
+ # $D5 - zeropage_x - 4 cycles
46
+ # $D9 - absolute_y - 4+ cycles
47
+ # $DD - absolute_x - 4+ cycles
48
+ def cmp(_addr, value)
49
+ v = resolve(value)
50
+ status.carry = (@a >= v)
51
+ update_number_flags(@a - v)
52
+ end
53
+
54
+ # Compare memory with X register.
55
+ #
56
+ # Opcodes:
57
+ # $E0 - immediate - 2 cycles
58
+ # $E4 - zeropage - 3 cycles
59
+ # $EC - absolute - 4 cycles
60
+ def cpx(_addr, value)
61
+ v = resolve(value)
62
+ status.carry = @x >= v
63
+ update_number_flags(@x - v)
64
+ end
65
+
66
+ # Compare memory with Y register.
67
+ #
68
+ # Opcodes:
69
+ # $C0 - immediate - 2 cycles
70
+ # $C4 - zeropage - 3 cycles
71
+ # $CC - absolute - 4 cycles
72
+ def cpy(_addr, value)
73
+ v = resolve(value)
74
+ status.carry = @y >= v
75
+ update_number_flags(@y - v)
76
+ end
77
+
78
+ # Subtract memory from accumulator with borrow.
79
+ #
80
+ # Opcodes:
81
+ # $E1 - indirect_x - 6 cycles
82
+ # $E5 - zeropage - 3 cycles
83
+ # $E9 - immediate - 2 cycles
84
+ # $ED - absolute - 4 cycles
85
+ # $F1 - indirect_y - 5+ cycles
86
+ # $F5 - zeropage_x - 4 cycles
87
+ # $F9 - absolute_y - 4+ cycles
88
+ # $FD - absolute_x - 4+ cycles
89
+ def sbc(_addr, value)
90
+ v = resolve(value)
91
+ v_inv = ~v & 0xff
92
+ carry = status.carry? ? 0 : -1
93
+
94
+ result = a + v_inv + status.carry
95
+ status.zero = result.nobits?(0xff)
96
+ status.carry = result > 0xff
97
+ update_calculation_flags(v_inv, result)
98
+
99
+ if status.decimal?
100
+ al = (a & 0x0f) - (v & 0x0f) + carry
101
+ al = ((al - 0x06) & 0x0F) - 0x10 if al.negative?
102
+
103
+ result = (a & 0xf0) - (v & 0xf0) + al
104
+ result -= 0x60 if result.negative?
105
+ end
106
+
107
+ @a = result & 0xff
108
+ end
109
+
110
+ private
111
+
112
+ def update_calculation_flags(value, result)
113
+ status.negative = result.anybits?(0x80)
114
+ status.overflow = (a ^ value).nobits?(0x80) &&
115
+ (a ^ result).anybits?(0x80)
116
+ end
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,131 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ module InstructionSet
5
+ module Bitwise
6
+ # Performs bitwise AND on accumulator with memory.
7
+ #
8
+ # Opcodes:
9
+ # $21 - indirect_x - 6 cycles
10
+ # $25 - zeropage - 3 cycles
11
+ # $29 - immediate - 2 cycles
12
+ # $2D - absolute - 4 cycles
13
+ # $31 - indirect_y - 5+ cycles
14
+ # $35 - zeropage_x - 4 cycles
15
+ # $39 - absolute_y - 4+ cycles
16
+ # $3D - absolute_x - 4+ cycles
17
+ def and(_addr, value)
18
+ @a &= resolve(value)
19
+ update_number_flags(@a)
20
+ end
21
+
22
+ # Arithmetic shift left. Shifts bits left by one position.
23
+ #
24
+ # Opcodes:
25
+ # $06 - zeropage - 5 cycles
26
+ # $0A - accumulator- 2 cycles
27
+ # $0E - absolute - 6 cycles
28
+ # $16 - zeropage_x - 6 cycles
29
+ # $1E - absolute_x - 7 cycles
30
+ def asl(addr, value)
31
+ v = resolve(value)
32
+ result = (v << 1) & 0xff
33
+ status.carry = v[7]
34
+ write_modified(addr, v, result)
35
+ update_number_flags(result)
36
+ end
37
+
38
+ # Tests bits in memory with accumulator.
39
+ #
40
+ # Opcodes:
41
+ # $24 - zeropage - 3 cycles
42
+ # $2C - absolute - 4 cycles
43
+ def bit(_addr, value)
44
+ v = resolve(value)
45
+ status.value = ((status.value & 0b00111111) +
46
+ (v & 0b11000000)).to_i
47
+ status.zero = (a & v).nobits?(0xff)
48
+ end
49
+
50
+ # Performs bitwise XOR on accumulator with memory.
51
+ #
52
+ # Opcodes:
53
+ # $41 - indirect_x - 6 cycles
54
+ # $45 - zeropage - 3 cycles
55
+ # $49 - immediate - 2 cycles
56
+ # $4D - absolute - 4 cycles
57
+ # $51 - indirect_y - 5+ cycles
58
+ # $55 - zeropage_x - 4 cycles
59
+ # $59 - absolute_y - 4+ cycles
60
+ # $5D - absolute_x - 4+ cycles
61
+ def eor(_addr, value)
62
+ @a = (@a ^ resolve(value)) & 0xff
63
+ update_number_flags(@a)
64
+ end
65
+
66
+ # Logical shift right. Shifts bits right by one position.
67
+ #
68
+ # Opcodes:
69
+ # $46 - zeropage - 5 cycles
70
+ # $4A - accumulator- 2 cycles
71
+ # $4E - absolute - 6 cycles
72
+ # $56 - zeropage_x - 6 cycles
73
+ # $5E - absolute_x - 7 cycles
74
+ def lsr(addr, value)
75
+ v = resolve(value)
76
+ result = (v >> 1) & 0xff
77
+ status.carry = v[0]
78
+ write_modified(addr, v, result)
79
+ update_number_flags(result)
80
+ end
81
+
82
+ # Performs bitwise OR on accumulator with memory.
83
+ #
84
+ # Opcodes:
85
+ # $01 - indirect_x - 6 cycles
86
+ # $05 - zeropage - 3 cycles
87
+ # $09 - immediate - 2 cycles
88
+ # $0D - absolute - 4 cycles
89
+ # $11 - indirect_y - 5+ cycles
90
+ # $15 - zeropage_x - 4 cycles
91
+ # $19 - absolute_y - 4+ cycles
92
+ # $1D - absolute_x - 4+ cycles
93
+ def ora(_addr, value)
94
+ @a |= resolve(value)
95
+ update_number_flags(@a)
96
+ end
97
+
98
+ # Rotate left through carry.
99
+ #
100
+ # Opcodes:
101
+ # $26 - zeropage - 5 cycles
102
+ # $2A - accumulator- 2 cycles
103
+ # $2E - absolute - 6 cycles
104
+ # $36 - zeropage_x - 6 cycles
105
+ # $3E - absolute_x - 7 cycles
106
+ def rol(addr, value)
107
+ v = resolve(value)
108
+ result = ((v << 1) + status.carry) & 0xff
109
+ status.carry = v[7]
110
+ write_modified(addr, v, result)
111
+ update_number_flags(result)
112
+ end
113
+
114
+ # Rotate right through carry.
115
+ #
116
+ # Opcodes:
117
+ # $66 - zeropage - 5 cycles
118
+ # $6A - accumulator- 2 cycles
119
+ # $6E - absolute - 6 cycles
120
+ # $76 - zeropage_x - 6 cycles
121
+ # $7E - absolute_x - 7 cycles
122
+ def ror(addr, value)
123
+ v = resolve(value)
124
+ result = ((v >> 1) + (status.carry? ? 0x80 : 0)) & 0xff
125
+ status.carry = v[0]
126
+ write_modified(addr, v, result)
127
+ update_number_flags(result)
128
+ end
129
+ end
130
+ end
131
+ end
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ module InstructionSet
5
+ module Branch
6
+ # Branch if carry clear (C=0).
7
+ #
8
+ # Opcodes:
9
+ # $90 - relative - 2+ cycles
10
+ def bcc(addr, _value)
11
+ branch(addr) unless status.carry?
12
+ end
13
+
14
+ # Branch if carry set (C=1).
15
+ #
16
+ # Opcodes:
17
+ # $B0 - relative - 2+ cycles
18
+ def bcs(addr, _value)
19
+ branch(addr) if status.carry?
20
+ end
21
+
22
+ # Branch if equal (Z=1).
23
+ #
24
+ # Opcodes:
25
+ # $F0 - relative - 2+ cycles
26
+ def beq(addr, _value)
27
+ branch(addr) if status.zero?
28
+ end
29
+
30
+ # Branch if minus (N=1).
31
+ #
32
+ # Opcodes:
33
+ # $30 - relative - 2+ cycles
34
+ def bmi(addr, _value)
35
+ branch(addr) if status.negative?
36
+ end
37
+
38
+ # Branch if not equal (Z=0).
39
+ #
40
+ # Opcodes:
41
+ # $D0 - relative - 2+ cycles
42
+ def bne(addr, _value)
43
+ branch(addr) unless status.zero?
44
+ end
45
+
46
+ # Branch if plus (N=0).
47
+ #
48
+ # Opcodes:
49
+ # $10 - relative - 2+ cycles
50
+ def bpl(addr, _value)
51
+ branch(addr) unless status.negative?
52
+ end
53
+
54
+ # Branch if overflow clear (V=0).
55
+ #
56
+ # Opcodes:
57
+ # $50 - relative - 2+ cycles
58
+ def bvc(addr, _value)
59
+ branch(addr) unless status.overflow?
60
+ end
61
+
62
+ # Branch if overflow set (V=1).
63
+ #
64
+ # Opcodes:
65
+ # $70 - relative - 2+ cycles
66
+ def bvs(addr, _value)
67
+ branch(addr) if status.overflow?
68
+ end
69
+
70
+ private
71
+
72
+ def branch(addr)
73
+ cycle if high_byte(addr) != high_byte(@program_counter)
74
+ cycle { @program_counter = addr }
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ module InstructionSet
5
+ module Flag
6
+ # Clear carry flag.
7
+ #
8
+ # Opcodes:
9
+ # $18 - implied - 2 cycles
10
+ def clc(_addr, _value)
11
+ cycle { status.carry = false }
12
+ end
13
+
14
+ # Clear decimal mode flag.
15
+ #
16
+ # Opcodes:
17
+ # $D8 - implied - 2 cycles
18
+ def cld(_addr, _value)
19
+ cycle { status.decimal = false }
20
+ end
21
+
22
+ # Clear interrupt disable flag.
23
+ #
24
+ # Opcodes:
25
+ # $58 - implied - 2 cycles
26
+ def cli(_addr, _value)
27
+ cycle { status.interrupt = false }
28
+ end
29
+
30
+ # Clear overflow flag.
31
+ #
32
+ # Opcodes:
33
+ # $B8 - implied - 2 cycles
34
+ def clv(_addr, _value)
35
+ cycle { status.overflow = false }
36
+ end
37
+
38
+ # Set carry flag.
39
+ #
40
+ # Opcodes:
41
+ # $38 - implied - 2 cycles
42
+ def sec(_addr, _value)
43
+ cycle { status.carry = true }
44
+ end
45
+
46
+ # Set decimal mode flag.
47
+ #
48
+ # Opcodes:
49
+ # $F8 - implied - 2 cycles
50
+ def sed(_addr, _value)
51
+ cycle { status.decimal = true }
52
+ end
53
+
54
+ # Set interrupt disable flag.
55
+ #
56
+ # Opcodes:
57
+ # $78 - implied - 2 cycles
58
+ def sei(_addr, _value)
59
+ cycle { status.interrupt = true }
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,278 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Badline
4
+ module InstructionSet
5
+ # Illegal opcodes
6
+ module Illegal
7
+ # Combination of AND and LSR.
8
+ #
9
+ # Opcodes:
10
+ # $4B - immediate - 2 cycles
11
+ def alr(_addr, value)
12
+ @a &= resolve(value)
13
+ status.carry = @a[0]
14
+ @a = (@a >> 1) & 0xff
15
+ update_number_flags(@a)
16
+ end
17
+
18
+ # Combination of AND where carry is set to bit 7 of result.
19
+ #
20
+ # Opcodes:
21
+ # $0B, $2B - immediate - 2 cycles
22
+ def anc(addr, value)
23
+ self.and(addr, value)
24
+ status.carry = status.negative
25
+ end
26
+
27
+ # Unstable instruction. AND's value with A|magic_const and X.
28
+ # Magic constant varies by CPU.
29
+ #
30
+ # Opcodes:
31
+ # $8B - immediate - 2 cycles
32
+ def ane(_addr, value)
33
+ magic_const = 0xee
34
+ @a = (a | magic_const) & x & resolve(value)
35
+ update_number_flags(@a)
36
+ end
37
+
38
+ # Combination of AND and ROR.
39
+ #
40
+ # Opcodes:
41
+ # $6B - immediate - 2 cycles
42
+ def arr(_addr, value)
43
+ tmp = a & resolve(value)
44
+ result = (tmp >> 1) | (status.carry? ? 0x80 : 0)
45
+ status.zero = result.zero?
46
+ status.negative = status.carry
47
+
48
+ if status.decimal?
49
+ status.overflow = (result ^ tmp).anybits?(0x40)
50
+
51
+ # Low nibble
52
+ result = (result & 0xf0) | ((result + 0x06) & 0x0f) if ((tmp & 0x0f) + (tmp & 0x01)) > 0x05
53
+
54
+ # High nibble + carry
55
+ if ((tmp & 0xf0) + (tmp & 0x10)) > 0x50
56
+ result = (result & 0x0f) | ((result + 0x60) & 0xf0)
57
+ status.carry = true
58
+ else
59
+ status.carry = false
60
+ end
61
+ else
62
+ status.carry = tmp[7] == 1
63
+ status.overflow = tmp[7] ^ tmp[6]
64
+ end
65
+
66
+ @a = result
67
+ end
68
+
69
+ # Combination of DEC and CMP operations.
70
+ #
71
+ # Opcodes:
72
+ # $C3 - indirect_x - 8 cycles
73
+ # $C7 - zeropage - 5 cycles
74
+ # $CF - absolute - 6 cycles
75
+ # $D3 - indirect_y - 8 cycles
76
+ # $D7 - zeropage_x - 6 cycles
77
+ # $DB - absolute_y - 7 cycles
78
+ # $DF - absolute_x - 7 cycles
79
+ def dcp(addr, value)
80
+ cmp(addr, dec(addr, value))
81
+ end
82
+
83
+ # Combination of INC and SBC operations.
84
+ #
85
+ # Opcodes:
86
+ # $E3 - indirect_x - 8 cycles
87
+ # $E7 - zeropage - 5 cycles
88
+ # $EF - absolute - 6 cycles
89
+ # $F3 - indirect_y - 8 cycles
90
+ # $F7 - zeropage_x - 6 cycles
91
+ # $FB - absolute_y - 7 cycles
92
+ # $FF - absolute_x - 7 cycles
93
+ def isc(addr, value)
94
+ sbc(addr, inc(addr, value))
95
+ end
96
+
97
+ # Freezes the CPU by forcing an infinite loop.
98
+ #
99
+ # Opcodes:
100
+ # $02, $12, $22, $32, $42, $52, $62, $72, $92, $B2, $D2, $F2
101
+ def jam(_addr, _value)
102
+ # TODO: Handle jam
103
+ # It is possible to implement with loop { cycle }, but makes
104
+ # testing problematic.
105
+ 10.times { cycle }
106
+ end
107
+
108
+ # Loads A and S with value AND stack pointer.
109
+ #
110
+ # Opcodes:
111
+ # $BB - absolute_y - 4 cycles
112
+ def las(_addr, value)
113
+ @a = @x = @stack_pointer = resolve(value) & stack_pointer
114
+ update_number_flags(@a)
115
+ end
116
+
117
+ # Combination of LDA and LDX with same value.
118
+ #
119
+ # Opcodes:
120
+ # $A3 - indirect_x - 6 cycles
121
+ # $A7 - zeropage - 3 cycles
122
+ # $AF - absolute - 4 cycles
123
+ # $B3 - indirect_y - 5+ cycles
124
+ # $B7 - zeropage_y - 4 cycles
125
+ # $BF - absolute_y - 4+ cycles
126
+ def lax(_addr, value)
127
+ @a = @x = resolve(value)
128
+
129
+ update_number_flags(@a)
130
+ end
131
+
132
+ # Unstable immediate form of LAX. Loads A and X with
133
+ # (A | magic_const) AND value. Magic constant varies by CPU.
134
+ #
135
+ # Opcodes:
136
+ # $AB - immediate - 2 cycles
137
+ def lxa(_addr, value)
138
+ magic_const = 0xee
139
+ @a = @x = (a | magic_const) & resolve(value)
140
+ update_number_flags(@a)
141
+ end
142
+
143
+ # Special no-operation instruction that does not consume a CPU cycle,
144
+ # unlike the standard NOP. Used by some illegal opcodes.
145
+ #
146
+ # Opcodes:
147
+ # $80, $82, $89, $C2, $E2 - immediate - 2 cycles
148
+ def nop_nocycle(_addr, _value); end
149
+
150
+ # Combination of ROL and AND operations.
151
+ #
152
+ # Opcodes:
153
+ # $23 - indirect_x - 8 cycles
154
+ # $27 - zeropage - 5 cycles
155
+ # $2F - absolute - 6 cycles
156
+ # $33 - indirect_y - 8 cycles
157
+ # $37 - zeropage_x - 6 cycles
158
+ # $3B - absolute_y - 7 cycles
159
+ # $3F - absolute_x - 7 cycles
160
+ def rla(addr, value)
161
+ self.and(addr, rol(addr, value))
162
+ end
163
+
164
+ # Combination of ROR and ADC operations.
165
+ #
166
+ # Opcodes:
167
+ # $63 - indirect_x - 8 cycles
168
+ # $67 - zeropage - 5 cycles
169
+ # $6F - absolute - 6 cycles
170
+ # $73 - indirect_y - 8 cycles
171
+ # $77 - zeropage_x - 6 cycles
172
+ # $7B - absolute_y - 7 cycles
173
+ # $7F - absolute_x - 7 cycles
174
+ def rra(addr, value)
175
+ adc(addr, ror(addr, value))
176
+ end
177
+
178
+ # Store A AND X.
179
+ #
180
+ # Opcodes:
181
+ # $83 - indirect_x - 6 cycles
182
+ # $87 - zeropage - 3 cycles
183
+ # $8F - absolute - 4 cycles
184
+ # $97 - zeropage_y - 4 cycles
185
+ def sax(addr, _value)
186
+ write_byte(addr, @a & @x)
187
+ end
188
+
189
+ # AND X register with accumulator and subtract value.
190
+ #
191
+ # Opcodes:
192
+ # $CB - immediate - 2 cycles
193
+ def sbx(_addr, value)
194
+ v = resolve(value)
195
+ result = (@x & @a) - v
196
+ status.carry = result >= 0
197
+ @x = result & 0xff
198
+ update_number_flags(@x)
199
+ end
200
+
201
+ # Stores A AND X AND (high byte of base address + 1) at addr.
202
+ #
203
+ # Opcodes:
204
+ # $93 - indirect_y - 6 cycles
205
+ # $9F - absolute_y - 5 cycles
206
+ def sha(addr, _value)
207
+ store_high_and(a & x, addr)
208
+ end
209
+
210
+ # Stores X AND (high byte of base address + 1) at addr.
211
+ #
212
+ # Opcodes:
213
+ # $9E - absolute_y - 5 cycles
214
+ def shx(addr, _value)
215
+ store_high_and(x, addr)
216
+ end
217
+
218
+ # Stores Y AND (high byte of base address + 1) at addr.
219
+ #
220
+ # Opcodes:
221
+ # $9C - absolute_x - 5 cycles
222
+ def shy(addr, _value)
223
+ store_high_and(y, addr)
224
+ end
225
+
226
+ # Combination of ASL and ORA operations.
227
+ #
228
+ # Opcodes:
229
+ # $03 - indirect_x - 8 cycles
230
+ # $07 - zeropage - 5 cycles
231
+ # $0F - absolute - 6 cycles
232
+ # $13 - indirect_y - 8 cycles
233
+ # $17 - zeropage_x - 6 cycles
234
+ # $1B - absolute_y - 7 cycles
235
+ # $1F - absolute_x - 7 cycles
236
+ def slo(addr, value)
237
+ ora(addr, asl(addr, value))
238
+ end
239
+
240
+ # Combination of LSR and EOR operations.
241
+ #
242
+ # Opcodes:
243
+ # $43 - indirect_x - 8 cycles
244
+ # $47 - zeropage - 5 cycles
245
+ # $4F - absolute - 6 cycles
246
+ # $53 - indirect_y - 8 cycles
247
+ # $57 - zeropage_x - 6 cycles
248
+ # $5B - absolute_y - 7 cycles
249
+ # $5F - absolute_x - 7 cycles
250
+ def sre(addr, value)
251
+ eor(addr, lsr(addr, value))
252
+ end
253
+
254
+ # Stores A AND X in SP, then stores SP AND (high byte of base + 1) at
255
+ # addr.
256
+ #
257
+ # Opcodes:
258
+ # $9B - absolute_y - 5 cycles
259
+ def tas(addr, _value)
260
+ @stack_pointer = @a & @x
261
+ store_high_and(stack_pointer, addr)
262
+ end
263
+
264
+ private
265
+
266
+ def store_high_and(register, addr)
267
+ base_high = if boundary_crossed
268
+ (high_byte(addr) - 1) & 0xff
269
+ else
270
+ high_byte(addr)
271
+ end
272
+ result = register & ((base_high + 1) & 0xff)
273
+ target = boundary_crossed ? uint16(low_byte(addr), result) : addr
274
+ write_byte(target, result)
275
+ end
276
+ end
277
+ end
278
+ end