amaterasu 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 (158) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +14 -0
  3. data/.rspec +1 -0
  4. data/.rubocop.yml +54 -0
  5. data/Gemfile +32 -0
  6. data/Gemfile.lock +267 -0
  7. data/LICENSE +21 -0
  8. data/README.md +115 -0
  9. data/Steepfile +7 -0
  10. data/exe/amaterasu +23 -0
  11. data/lib/amaterasu/cartridge/mbc1.rb +56 -0
  12. data/lib/amaterasu/cartridge/rom.rb +118 -0
  13. data/lib/amaterasu/cartridge.rb +68 -0
  14. data/lib/amaterasu/cli.rb +60 -0
  15. data/lib/amaterasu/emulator.rb +121 -0
  16. data/lib/amaterasu/game_boy/apu.rb +12 -0
  17. data/lib/amaterasu/game_boy/bus.rb +161 -0
  18. data/lib/amaterasu/game_boy/cpu/instructions/adc.rb +64 -0
  19. data/lib/amaterasu/game_boy/cpu/instructions/add16.rb +73 -0
  20. data/lib/amaterasu/game_boy/cpu/instructions/add8.rb +63 -0
  21. data/lib/amaterasu/game_boy/cpu/instructions/and.rb +62 -0
  22. data/lib/amaterasu/game_boy/cpu/instructions/base.rb +38 -0
  23. data/lib/amaterasu/game_boy/cpu/instructions/call.rb +48 -0
  24. data/lib/amaterasu/game_boy/cpu/instructions/cb_bit.rb +52 -0
  25. data/lib/amaterasu/game_boy/cpu/instructions/cb_res.rb +49 -0
  26. data/lib/amaterasu/game_boy/cpu/instructions/cb_rl.rb +70 -0
  27. data/lib/amaterasu/game_boy/cpu/instructions/cb_rlc.rb +68 -0
  28. data/lib/amaterasu/game_boy/cpu/instructions/cb_rr.rb +70 -0
  29. data/lib/amaterasu/game_boy/cpu/instructions/cb_rrc.rb +68 -0
  30. data/lib/amaterasu/game_boy/cpu/instructions/cb_set.rb +51 -0
  31. data/lib/amaterasu/game_boy/cpu/instructions/cb_sla.rb +69 -0
  32. data/lib/amaterasu/game_boy/cpu/instructions/cb_sra.rb +71 -0
  33. data/lib/amaterasu/game_boy/cpu/instructions/cb_srl.rb +69 -0
  34. data/lib/amaterasu/game_boy/cpu/instructions/cb_swap.rb +67 -0
  35. data/lib/amaterasu/game_boy/cpu/instructions/cp.rb +61 -0
  36. data/lib/amaterasu/game_boy/cpu/instructions/daa.rb +59 -0
  37. data/lib/amaterasu/game_boy/cpu/instructions/dec.rb +64 -0
  38. data/lib/amaterasu/game_boy/cpu/instructions/di.rb +21 -0
  39. data/lib/amaterasu/game_boy/cpu/instructions/ei.rb +19 -0
  40. data/lib/amaterasu/game_boy/cpu/instructions/halt.rb +19 -0
  41. data/lib/amaterasu/game_boy/cpu/instructions/inc.rb +64 -0
  42. data/lib/amaterasu/game_boy/cpu/instructions/jp.rb +54 -0
  43. data/lib/amaterasu/game_boy/cpu/instructions/jr.rb +45 -0
  44. data/lib/amaterasu/game_boy/cpu/instructions/ld16.rb +79 -0
  45. data/lib/amaterasu/game_boy/cpu/instructions/ld8.rb +210 -0
  46. data/lib/amaterasu/game_boy/cpu/instructions/ldh.rb +61 -0
  47. data/lib/amaterasu/game_boy/cpu/instructions/misc.rb +53 -0
  48. data/lib/amaterasu/game_boy/cpu/instructions/nop.rb +19 -0
  49. data/lib/amaterasu/game_boy/cpu/instructions/or.rb +56 -0
  50. data/lib/amaterasu/game_boy/cpu/instructions/pop.rb +39 -0
  51. data/lib/amaterasu/game_boy/cpu/instructions/push.rb +43 -0
  52. data/lib/amaterasu/game_boy/cpu/instructions/ret.rb +70 -0
  53. data/lib/amaterasu/game_boy/cpu/instructions/rotate.rb +120 -0
  54. data/lib/amaterasu/game_boy/cpu/instructions/rst.rb +33 -0
  55. data/lib/amaterasu/game_boy/cpu/instructions/sbc.rb +64 -0
  56. data/lib/amaterasu/game_boy/cpu/instructions/stop.rb +19 -0
  57. data/lib/amaterasu/game_boy/cpu/instructions/sub.rb +63 -0
  58. data/lib/amaterasu/game_boy/cpu/instructions/xor.rb +60 -0
  59. data/lib/amaterasu/game_boy/cpu/instructions.rb +600 -0
  60. data/lib/amaterasu/game_boy/cpu/registers.rb +264 -0
  61. data/lib/amaterasu/game_boy/cpu.rb +232 -0
  62. data/lib/amaterasu/game_boy/dma.rb +114 -0
  63. data/lib/amaterasu/game_boy/interrupts.rb +108 -0
  64. data/lib/amaterasu/game_boy/joypad.rb +127 -0
  65. data/lib/amaterasu/game_boy/oam/sprite.rb +106 -0
  66. data/lib/amaterasu/game_boy/oam.rb +29 -0
  67. data/lib/amaterasu/game_boy/ppu/modes/disabled.rb +29 -0
  68. data/lib/amaterasu/game_boy/ppu/modes/h_blank.rb +45 -0
  69. data/lib/amaterasu/game_boy/ppu/modes/oam_scan.rb +93 -0
  70. data/lib/amaterasu/game_boy/ppu/modes/rendering/bg_win_fetcher.rb +204 -0
  71. data/lib/amaterasu/game_boy/ppu/modes/rendering/pixel_emitter.rb +83 -0
  72. data/lib/amaterasu/game_boy/ppu/modes/rendering/pixel_fifo.rb +70 -0
  73. data/lib/amaterasu/game_boy/ppu/modes/rendering/sprite_fetcher.rb +140 -0
  74. data/lib/amaterasu/game_boy/ppu/modes/rendering.rb +108 -0
  75. data/lib/amaterasu/game_boy/ppu/modes/v_blank.rb +43 -0
  76. data/lib/amaterasu/game_boy/ppu/modes.rb +22 -0
  77. data/lib/amaterasu/game_boy/ppu/registers/lcd_control.rb +57 -0
  78. data/lib/amaterasu/game_boy/ppu/registers/lcd_status.rb +88 -0
  79. data/lib/amaterasu/game_boy/ppu/registers.rb +131 -0
  80. data/lib/amaterasu/game_boy/ppu.rb +207 -0
  81. data/lib/amaterasu/game_boy/ram.rb +70 -0
  82. data/lib/amaterasu/game_boy/serial.rb +91 -0
  83. data/lib/amaterasu/game_boy/timer.rb +230 -0
  84. data/lib/amaterasu/game_boy/vram/tile.rb +68 -0
  85. data/lib/amaterasu/game_boy/vram/tile_data.rb +52 -0
  86. data/lib/amaterasu/game_boy/vram/tile_map.rb +71 -0
  87. data/lib/amaterasu/game_boy/vram.rb +51 -0
  88. data/lib/amaterasu/hal/console.rb +23 -0
  89. data/lib/amaterasu/hal/sdl2/bindings.rb +59 -0
  90. data/lib/amaterasu/hal/sdl2.rb +127 -0
  91. data/lib/amaterasu/utils/bit_ops.rb +22 -0
  92. data/lib/amaterasu.rb +13 -0
  93. data/sig/akane/cartridge/rom.rbs +29 -0
  94. data/sig/akane/cartridge.rbs +12 -0
  95. data/sig/akane/cli.rbs +16 -0
  96. data/sig/akane/emulator.rbs +19 -0
  97. data/sig/akane/game_boy/apu.rbs +7 -0
  98. data/sig/akane/game_boy/bus.rbs +25 -0
  99. data/sig/akane/game_boy/cpu/instructions/adc.rbs +18 -0
  100. data/sig/akane/game_boy/cpu/instructions/add16.rbs +19 -0
  101. data/sig/akane/game_boy/cpu/instructions/add8.rbs +18 -0
  102. data/sig/akane/game_boy/cpu/instructions/and.rbs +18 -0
  103. data/sig/akane/game_boy/cpu/instructions/base.rbs +20 -0
  104. data/sig/akane/game_boy/cpu/instructions/call.rbs +18 -0
  105. data/sig/akane/game_boy/cpu/instructions/cb_bit.rbs +20 -0
  106. data/sig/akane/game_boy/cpu/instructions/cb_res.rbs +19 -0
  107. data/sig/akane/game_boy/cpu/instructions/cb_rl.rbs +21 -0
  108. data/sig/akane/game_boy/cpu/instructions/cb_rlc.rbs +21 -0
  109. data/sig/akane/game_boy/cpu/instructions/cb_rr.rbs +21 -0
  110. data/sig/akane/game_boy/cpu/instructions/cb_rrc.rbs +21 -0
  111. data/sig/akane/game_boy/cpu/instructions/cb_set.rbs +20 -0
  112. data/sig/akane/game_boy/cpu/instructions/cb_sla.rbs +21 -0
  113. data/sig/akane/game_boy/cpu/instructions/cb_sra.rbs +21 -0
  114. data/sig/akane/game_boy/cpu/instructions/cb_srl.rbs +21 -0
  115. data/sig/akane/game_boy/cpu/instructions/cb_swap.rbs +21 -0
  116. data/sig/akane/game_boy/cpu/instructions/cp.rbs +18 -0
  117. data/sig/akane/game_boy/cpu/instructions/daa.rbs +17 -0
  118. data/sig/akane/game_boy/cpu/instructions/dec.rbs +19 -0
  119. data/sig/akane/game_boy/cpu/instructions/di.rbs +13 -0
  120. data/sig/akane/game_boy/cpu/instructions/ei.rbs +13 -0
  121. data/sig/akane/game_boy/cpu/instructions/halt.rbs +13 -0
  122. data/sig/akane/game_boy/cpu/instructions/inc.rbs +19 -0
  123. data/sig/akane/game_boy/cpu/instructions/jp.rbs +18 -0
  124. data/sig/akane/game_boy/cpu/instructions/jr.rbs +18 -0
  125. data/sig/akane/game_boy/cpu/instructions/ld16.rbs +18 -0
  126. data/sig/akane/game_boy/cpu/instructions/ld8.rbs +31 -0
  127. data/sig/akane/game_boy/cpu/instructions/ldh.rbs +23 -0
  128. data/sig/akane/game_boy/cpu/instructions/misc.rbs +20 -0
  129. data/sig/akane/game_boy/cpu/instructions/nop.rbs +13 -0
  130. data/sig/akane/game_boy/cpu/instructions/or.rbs +18 -0
  131. data/sig/akane/game_boy/cpu/instructions/pop.rbs +17 -0
  132. data/sig/akane/game_boy/cpu/instructions/push.rbs +18 -0
  133. data/sig/akane/game_boy/cpu/instructions/ret.rbs +20 -0
  134. data/sig/akane/game_boy/cpu/instructions/rotate.rbs +23 -0
  135. data/sig/akane/game_boy/cpu/instructions/rst.rbs +17 -0
  136. data/sig/akane/game_boy/cpu/instructions/sbc.rbs +19 -0
  137. data/sig/akane/game_boy/cpu/instructions/stop.rbs +13 -0
  138. data/sig/akane/game_boy/cpu/instructions/sub.rbs +18 -0
  139. data/sig/akane/game_boy/cpu/instructions/xor.rbs +19 -0
  140. data/sig/akane/game_boy/cpu/instructions.rbs +12 -0
  141. data/sig/akane/game_boy/cpu/registers.rbs +56 -0
  142. data/sig/akane/game_boy/cpu.rbs +39 -0
  143. data/sig/akane/game_boy/interrupts.rbs +28 -0
  144. data/sig/akane/game_boy/joypad.rbs +25 -0
  145. data/sig/akane/game_boy/oam/sprite.rbs +30 -0
  146. data/sig/akane/game_boy/ppu/modes/disabled.rbs +17 -0
  147. data/sig/akane/game_boy/ppu/modes/h_blank.rbs +20 -0
  148. data/sig/akane/game_boy/ppu/modes/oam_scan.rbs +28 -0
  149. data/sig/akane/game_boy/ppu/modes/rendering.rbs +26 -0
  150. data/sig/akane/game_boy/ppu/modes/v_blank.rbs +20 -0
  151. data/sig/akane/game_boy/ppu/modes.rbs +13 -0
  152. data/sig/akane/game_boy/ppu.rbs +59 -0
  153. data/sig/akane/game_boy/ram.rbs +16 -0
  154. data/sig/akane/game_boy/serial.rbs +21 -0
  155. data/sig/akane/game_boy/timer.rbs +30 -0
  156. data/sig/akane/utils/bit_ops.rbs +11 -0
  157. data/sig/akane.rbs +3 -0
  158. metadata +226 -0
@@ -0,0 +1,120 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Amaterasu
4
+ module GameBoy
5
+ class Cpu
6
+ module Instructions
7
+ # Holds the logic of all the Rotate instructions.
8
+ #
9
+ # - RLCA: Rotate Left Circular A.
10
+ # - RRCA: Rotate Right Circular A.
11
+ # - RLA: Rotate Left Through Carry A.
12
+ # - RRA: Rotate Right Through Carry A.
13
+ class Rotate < Base
14
+ include Utils::BitOps
15
+
16
+ def initialize(cpu:, operation:)
17
+ super(cpu:)
18
+
19
+ @mnemonic = format_operand(operation).to_s
20
+ @logic = build_logic(operation)
21
+ end
22
+
23
+ private
24
+
25
+ # Returns a lambda object containing the instruction logic.
26
+ def build_logic(operation)
27
+ case operation
28
+ when :rlca then -> { rlca }
29
+ when :rrca then -> { rrca }
30
+ when :rla then -> { rla }
31
+ when :rra then -> { rra }
32
+ else
33
+ raise ArgumentError, 'Unknown Rotate operation'
34
+ end
35
+ end
36
+
37
+ # Rotate Left Circular.
38
+ # Rotate all bits from the A register to the left.
39
+ # Bit 7 falls off the left and at the same time:
40
+ # - Either sets or clears the Carry flag depending on it's value.
41
+ # - Wraps around the A register and becomes Bit 0.
42
+ #
43
+ # Before: [C] <- [7][6][5][4][3][2][1][0]
44
+ # After : [C=7] [6][5][4][3][2][1][0][7] <-
45
+ #
46
+ # M-cycle 1: Fetches the opcode and performs the rotate.
47
+ #
48
+ def rlca
49
+ old_bit7 = bit(@registers.a, 7)
50
+
51
+ @registers.clear_flags
52
+ @registers.c_flag = old_bit7 == 1
53
+
54
+ @registers.a = (@registers.a << 1) | old_bit7
55
+ end
56
+
57
+ # Rotate Right Circular.
58
+ # Rotate all bits from the A register to the right.
59
+ # Bit 0 falls off the right side and at the same time:
60
+ # - Either sets or clears the Carry flag depending on it's value.
61
+ # - Wraps around the A register and becomes Bit 7.
62
+ #
63
+ # Before: [7][6][5][4][3][2][1][0] -> [C]
64
+ # After : -> [0][7][6][5][4][3][2][1] [C=0]
65
+ #
66
+ # M-cycle 1: Fetches the opcode and performs the rotate.
67
+ #
68
+ def rrca
69
+ old_bit0 = bit(@registers.a, 0)
70
+
71
+ @registers.clear_flags
72
+ @registers.c_flag = old_bit0 == 1
73
+
74
+ @registers.a = (old_bit0 << 7) | (@registers.a >> 1)
75
+ end
76
+
77
+ # Rotate Left Through Carry.
78
+ # - Rotate all bits from the A register to the left.
79
+ # - Bit 7 falls off the left side and becomes the Carry flag.
80
+ # - The value of Carry flag becomes the new Bit 0.
81
+ #
82
+ # Before: [C] <- [7][6][5][4][3][2][1][0]
83
+ # After : [7] [6][5][4][3][2][1][0][C] <-
84
+ #
85
+ # M-cycle 1: Fetches the opcode and performs the rotate.
86
+ #
87
+ def rla
88
+ carry_in = @registers.c_flag
89
+ old_bit7 = bit(@registers.a, 7)
90
+
91
+ @registers.clear_flags
92
+ @registers.c_flag = old_bit7 == 1
93
+
94
+ @registers.a = (@registers.a << 1) | carry_in
95
+ end
96
+
97
+ # Rotate Right Through Carry.
98
+ # - Rotate all bits from the A register to the right.
99
+ # - Bit 0 falls off the right side and becomes the Carry flag.
100
+ # - The previous value of Carry flag becomes the new Bit 0.
101
+ #
102
+ # Before: [7][6][5][4][3][2][1][0] -> [C]
103
+ # After : -> [C][7][6][5][4][3][2][1]
104
+ #
105
+ # M-cycle 1: Fetches the opcode and performs the rotate.
106
+ #
107
+ def rra
108
+ carry_in = @registers.c_flag
109
+ old_bit0 = bit(@registers.a, 0)
110
+
111
+ @registers.clear_flags
112
+ @registers.c_flag = old_bit0 == 1
113
+
114
+ @registers.a = (carry_in << 7) | (@registers.a >> 1)
115
+ end
116
+ end
117
+ end
118
+ end
119
+ end
120
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Amaterasu
4
+ module GameBoy
5
+ class Cpu
6
+ module Instructions
7
+ # Holds the logic of all the RST instructions.
8
+ class Rst < Base
9
+ # Creates a Call instruction object with a mnemonic and logic to be executed.
10
+ def initialize(cpu:, vector:)
11
+ super(cpu:)
12
+
13
+ @mnemonic = "RST $#{format('%02X', vector)}"
14
+ @logic = -> { rst(vector) }
15
+ end
16
+
17
+ private
18
+
19
+ # This is essentially a CALL, but jumps to a fixed address vector.
20
+ #
21
+ # M-cycle 1: Fetches opcode.
22
+ # M-cycle 2: Stores the msb of the PC into the Stack.
23
+ # M-cycle 3: Stores the lsb of the PC into the Stack.
24
+ # M-cycle 4: Jumps to a fixed address vector.
25
+ def rst(vector)
26
+ @cpu.stack_push(value: @registers.pc)
27
+ @cpu.jump_to(address: vector)
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Amaterasu
4
+ module GameBoy
5
+ class Cpu
6
+ module Instructions
7
+ # Handles the logic related to all possible SBC instructions
8
+ #
9
+ # - SBC A, r8
10
+ # - SBC A, [HL]
11
+ # - SBC A, n8
12
+ class Sbc < Base
13
+ # @param cpu [Cpu] Holds a direct reference to the main Cpu object.
14
+ # @param source [Symbol] Operator, can be a register, :mem_hl, :imm8.
15
+ def initialize(cpu:, source:)
16
+ super(cpu:)
17
+
18
+ @mnemonic = "SBC A, #{source}"
19
+ @logic = build_logic(source)
20
+ end
21
+
22
+ private
23
+
24
+ # Builds the logic for all SBC instructions.
25
+ # Returns a lambda object to be called by the CPU.
26
+ def build_logic(source)
27
+ case source
28
+ when :a then -> { sbc_a(@registers.a) }
29
+ when :b then -> { sbc_a(@registers.b) }
30
+ when :c then -> { sbc_a(@registers.c) }
31
+ when :d then -> { sbc_a(@registers.d) }
32
+ when :e then -> { sbc_a(@registers.e) }
33
+ when :h then -> { sbc_a(@registers.h) }
34
+ when :l then -> { sbc_a(@registers.l) }
35
+ when :mem_hl then -> { sbc_a(@cpu.bus_read(address: @registers.hl)) }
36
+ when :imm8 then -> { sbc_a(@cpu.fetch_next_byte) }
37
+ else
38
+ raise ArgumentError, 'Unknown Sbc source'
39
+ end
40
+ end
41
+
42
+ # Subtracts a given value + Carry flag from register A and stores it back into A.
43
+ #
44
+ # - Sets the Zero flag if the result is zero, otherwise clears it.
45
+ # - Always sets the SBCtraction flag.
46
+ # - Sets the Half Carry flag if it needed to borrow from Bit 4.
47
+ # - Sets the Carry flag if it needed to borrow (acc < value).
48
+ def sbc_a(value)
49
+ acc = @registers.a
50
+ carry_in = @registers.c_flag
51
+ result = @registers.a - (value + carry_in)
52
+
53
+ @registers.z_flag = result.nobits?(0xFF)
54
+ @registers.n_flag = true
55
+ @registers.h_flag = (acc & 0x0F) < ((value & 0x0F) + carry_in)
56
+ @registers.c_flag = acc < (value + carry_in)
57
+
58
+ @registers.a = result
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Amaterasu
4
+ module GameBoy
5
+ class Cpu
6
+ module Instructions
7
+ # Holds the logic for STOP instruction.
8
+ class Stop < Base
9
+ def initialize(cpu:)
10
+ super
11
+
12
+ @mnemonic = 'STOP'
13
+ @logic = -> { @cpu.fetch_next_byte }
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Amaterasu
4
+ module GameBoy
5
+ class Cpu
6
+ module Instructions
7
+ # Handles the logic related to all possible SUB instructions
8
+ #
9
+ # - SUB A, r8
10
+ # - SUB A, [HL]
11
+ # - SUB A, n8
12
+ class Sub < Base
13
+ # @param cpu [Cpu] Holds a direct reference to the main Cpu object.
14
+ # @param source [Symbol] Operator, can be a register, :mem_hl, :imm8.
15
+ def initialize(cpu:, source:)
16
+ super(cpu:)
17
+
18
+ @mnemonic = "SUB A, #{format_operand(source)}"
19
+ @logic = build_logic(source)
20
+ end
21
+
22
+ private
23
+
24
+ # Builds the logic for all SUB instructions.
25
+ # Returns a lambda object to be called by the CPU.
26
+ def build_logic(source)
27
+ case source
28
+ when :a then -> { sub_a(@registers.a) }
29
+ when :b then -> { sub_a(@registers.b) }
30
+ when :c then -> { sub_a(@registers.c) }
31
+ when :d then -> { sub_a(@registers.d) }
32
+ when :e then -> { sub_a(@registers.e) }
33
+ when :h then -> { sub_a(@registers.h) }
34
+ when :l then -> { sub_a(@registers.l) }
35
+ when :mem_hl then -> { sub_a(@cpu.bus_read(address: @registers.hl)) }
36
+ when :imm8 then -> { sub_a(@cpu.fetch_next_byte) }
37
+ else
38
+ -> { raise 'Not implemented Sub operation' }
39
+ end
40
+ end
41
+
42
+ # Subtracts a given value from register A and stores it back into A.
43
+ #
44
+ # - Sets the Zero flag if the result is zero, otherwise clears it.
45
+ # - Always sets the Subtraction flag.
46
+ # - Sets the Half Carry flag if it needed to borrow from Bit 4.
47
+ # - Sets the Carry flag if it needed to borrow (acc < value).
48
+ def sub_a(value)
49
+ acc = @registers.a
50
+ result = @registers.a - value
51
+
52
+ @registers.z_flag = result.nobits?(0xFF)
53
+ @registers.n_flag = true
54
+ @registers.h_flag = (acc & 0x0F) < (value & 0x0F)
55
+ @registers.c_flag = acc < value
56
+
57
+ @registers.a = result
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Amaterasu
4
+ module GameBoy
5
+ class Cpu
6
+ module Instructions
7
+ # Handles the logic related to all possible XOR instructions
8
+ #
9
+ # - XOR A, r8
10
+ # - XOR A, [HL]
11
+ # - XOR A, n8
12
+ class Xor < Base
13
+ # @param cpu [Cpu] Holds a direct reference to the main Cpu object.
14
+ # @param source [Symbol] Operator, can be a register, :mem_hl, :imm8.
15
+ def initialize(cpu:, source:)
16
+ super(cpu:)
17
+
18
+ @mnemonic = define_mnemonic(source)
19
+ @logic = build_logic(source)
20
+ end
21
+
22
+ private
23
+
24
+ def define_mnemonic(source)
25
+ case source
26
+ when :mem_hl then 'XOR A, [HL]'
27
+ when :imm8 then 'XOR A, n8'
28
+ else "XOR A, #{source.upcase}"
29
+ end
30
+ end
31
+
32
+ def build_logic(source)
33
+ case source
34
+ when :a then -> { xor_a(@registers.a) }
35
+ when :b then -> { xor_a(@registers.b) }
36
+ when :c then -> { xor_a(@registers.c) }
37
+ when :d then -> { xor_a(@registers.d) }
38
+ when :e then -> { xor_a(@registers.e) }
39
+ when :h then -> { xor_a(@registers.h) }
40
+ when :l then -> { xor_a(@registers.l) }
41
+ when :mem_hl then -> { xor_a(@cpu.bus_read(address: @registers.hl)) }
42
+ when :imm8 then -> { xor_a(@cpu.fetch_next_byte) }
43
+ else
44
+ raise ArgumentError, 'Unknown Xor source'
45
+ end
46
+ end
47
+
48
+ def xor_a(value)
49
+ result = @registers.a ^ value
50
+
51
+ @registers.clear_flags
52
+ @registers.z_flag = result.nobits?(0xFF)
53
+
54
+ @registers.a = result
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end