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,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 ADD instructions
8
+ #
9
+ # - ADD A, r8
10
+ # - ADD A, [HL]
11
+ # - ADD A, n8
12
+ class Add8 < 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 = "ADD A, #{source}"
19
+ @logic = build_logic(source)
20
+ end
21
+
22
+ private
23
+
24
+ # Builds the logic for all ADD instructions.
25
+ # Returns a lambda object to be called by the CPU.
26
+ def build_logic(source)
27
+ case source
28
+ when :a then -> { add_a(@registers.a) }
29
+ when :b then -> { add_a(@registers.b) }
30
+ when :c then -> { add_a(@registers.c) }
31
+ when :d then -> { add_a(@registers.d) }
32
+ when :e then -> { add_a(@registers.e) }
33
+ when :h then -> { add_a(@registers.h) }
34
+ when :l then -> { add_a(@registers.l) }
35
+ when :mem_hl then -> { add_a(@cpu.bus_read(address: @registers.hl)) }
36
+ when :imm8 then -> { add_a(@cpu.fetch_next_byte) }
37
+ else
38
+ raise ArgumentError, 'Unknown Add8 source'
39
+ end
40
+ end
41
+
42
+ # Adds a given value into register A.
43
+ #
44
+ # - Sets the Zero flag if the result is zero, otherwise clears it.
45
+ # - Sets the Subtraction flag to zero.
46
+ # - Sets the Half Carry flag if there was overflow from Bit 3, otherwise clears it.
47
+ # - Sets the Carry flag if there was overflow from Bit 7, otherwise clears it.
48
+ def add_a(value)
49
+ acc = @registers.a
50
+ result = @registers.a + value
51
+
52
+ @registers.z_flag = result.nobits?(0xFF)
53
+ @registers.n_flag = false
54
+ @registers.h_flag = (acc & 0x0F) + (value & 0x0F) > 0x0F
55
+ @registers.c_flag = result > 0xFF
56
+
57
+ @registers.a = result
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,62 @@
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 AND instructions
8
+ #
9
+ # - AND A, r8
10
+ # - AND A, [HL]
11
+ # - AND A, n8
12
+ class And < 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 = "AND A, #{source}"
19
+ @logic = build_logic(source)
20
+ end
21
+
22
+ private
23
+
24
+ # Builds the logic for all AND instructions.
25
+ # Returns a lambda object to be called by the CPU.
26
+ def build_logic(source)
27
+ case source
28
+ when :a then -> { and_a(@registers.a) }
29
+ when :b then -> { and_a(@registers.b) }
30
+ when :c then -> { and_a(@registers.c) }
31
+ when :d then -> { and_a(@registers.d) }
32
+ when :e then -> { and_a(@registers.e) }
33
+ when :h then -> { and_a(@registers.h) }
34
+ when :l then -> { and_a(@registers.l) }
35
+ when :mem_hl then -> { and_a(@cpu.bus_read(address: @registers.hl)) }
36
+ when :imm8 then -> { and_a(@cpu.fetch_next_byte) }
37
+ else
38
+ raise ArgumentError, 'Unknown And source'
39
+ end
40
+ end
41
+
42
+ # Performs a bitwise AND between a given value and the register A.
43
+ #
44
+ # - Sets the Zero flag if the result is zero, otherwise clears it.
45
+ # - Always clears the Subtraction flag.
46
+ # - Always sets the Half Carry flag.
47
+ # - Always clears the Carry flag.
48
+ def and_a(value)
49
+ result = @registers.a & value
50
+
51
+ @registers.z_flag = result.nobits?(0xFF)
52
+ @registers.n_flag = false
53
+ @registers.h_flag = true
54
+ @registers.c_flag = false
55
+
56
+ @registers.a = result
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Amaterasu
4
+ module GameBoy
5
+ class Cpu
6
+ module Instructions
7
+ # Defines the mnemonic, bytes and logic for the NOP instruction.
8
+ class Base
9
+ attr_reader :mnemonic
10
+
11
+ def initialize(cpu:)
12
+ @cpu = cpu
13
+ @registers = cpu.registers
14
+ end
15
+
16
+ def execute
17
+ @logic.call
18
+ end
19
+
20
+ private
21
+
22
+ def format_operand(operand)
23
+ return '' if operand.nil?
24
+ return 'n8' if operand == :imm8
25
+ return 'n16' if operand == :imm16
26
+ return '[HL]' if operand == :mem_hl
27
+ return '[HL+]' if operand == :mem_hli
28
+ return '[HL-]' if operand == :mem_hld
29
+ return '[C]' if operand == :mem_c
30
+ return '[a8]' if operand == :mem_unsig8
31
+
32
+ operand.to_s.upcase
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,48 @@
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 CALL instructions.
8
+ class Call < Base
9
+ # Creates a Call instruction object with a mnemonic and logic to be executed.
10
+ def initialize(cpu:, condition: nil)
11
+ super(cpu:)
12
+
13
+ @mnemonic = "CALL #{format_operand(condition)}, imm16"
14
+ @logic = build_logic(condition)
15
+ end
16
+
17
+ private
18
+
19
+ # Returns a Proc object to be executed by the CPU at runtime.
20
+ def build_logic(condition)
21
+ case condition
22
+ when :nz then -> { call(@registers.z_flag.zero?) }
23
+ when :z then -> { call(@registers.z_flag == 1) }
24
+ when :nc then -> { call(@registers.c_flag.zero?) }
25
+ when :c then -> { call(@registers.c_flag == 1) }
26
+ else -> { call(true) }
27
+ end
28
+ end
29
+
30
+ # M-cycle 1: Fetches opcode.
31
+ # M-cycle 2: Fetches next immediate byte (lsb).
32
+ # M-cycle 3: Fetches next immediate byte (msb).
33
+ # ---------- Return early if condition not met.
34
+ # M-cycle 4: Pushes the msb of the PC onto the stack.
35
+ # M-cycle 5: Pushes the lsb of the PC onto the stack.
36
+ # M-cycle 6: Jumps to the call address.
37
+ def call(condition)
38
+ call_address = @cpu.fetch_next_word
39
+ return unless condition
40
+
41
+ @cpu.stack_push(value: @registers.pc)
42
+ @cpu.jump_to(address: call_address)
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,52 @@
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 BIT instructions.
8
+ class CbBit < Base
9
+ include Utils::BitOps
10
+
11
+ def initialize(cpu:, bit_pos:, target:)
12
+ super(cpu:)
13
+
14
+ @mnemonic = "BIT #{bit_pos}, #{format_operand(target)}"
15
+ @logic = build_logic(bit_pos, target)
16
+ end
17
+
18
+ private
19
+
20
+ def build_logic(bit_pos, target)
21
+ case target
22
+ when :b then -> { bit_test(bit_pos, @registers.b) }
23
+ when :c then -> { bit_test(bit_pos, @registers.c) }
24
+ when :d then -> { bit_test(bit_pos, @registers.d) }
25
+ when :e then -> { bit_test(bit_pos, @registers.e) }
26
+ when :h then -> { bit_test(bit_pos, @registers.h) }
27
+ when :l then -> { bit_test(bit_pos, @registers.l) }
28
+ when :mem_hl then -> { bit_test(bit_pos, @cpu.bus_read(address: @registers.hl)) }
29
+ when :a then -> { bit_test(bit_pos, @registers.a) }
30
+ else
31
+ raise ArgumentError, 'Unknown CbBit target'
32
+ end
33
+ end
34
+
35
+ # Checks the bit value from a given target at a given position [0-7].
36
+ #
37
+ # - Sets the zero flag if bit tested is 0, otherwise clears the flag.
38
+ # - Subtraction flag is always cleared.
39
+ # - Half Carry flag is always set.
40
+ # - Carry flag is untouched.
41
+ def bit_test(bit_pos, target_value)
42
+ bit_value = bit(target_value, bit_pos)
43
+
44
+ @registers.z_flag = bit_value.zero?
45
+ @registers.n_flag = false
46
+ @registers.h_flag = true
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,49 @@
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 RES instructions.
8
+ class CbRes < Base
9
+ include Utils::BitOps
10
+
11
+ # Creates a new CbRes object containing the mnemonic (String) and logic (Proc).
12
+ def initialize(cpu:, bit_pos:, target:)
13
+ super(cpu:)
14
+
15
+ @mnemonic = "RES #{bit_pos}, #{format_operand(target)}"
16
+ @logic = build_logic(bit_pos, target)
17
+ end
18
+
19
+ private
20
+
21
+ # @param bit_pos [Integer] Integer between 0 and 7.
22
+ # @param target [Symbol] Either a 8-bit register (:a, :b, :c, ...) or :mem_hl.
23
+ #
24
+ # @return [Proc] Logic to be executed by the Cpu.
25
+ #
26
+ def build_logic(bit_pos, target)
27
+ case target
28
+ when :a then -> { @registers.a = clear_bit(@registers.a, bit_pos) }
29
+ when :b then -> { @registers.b = clear_bit(@registers.b, bit_pos) }
30
+ when :c then -> { @registers.c = clear_bit(@registers.c, bit_pos) }
31
+ when :d then -> { @registers.d = clear_bit(@registers.d, bit_pos) }
32
+ when :e then -> { @registers.e = clear_bit(@registers.e, bit_pos) }
33
+ when :h then -> { @registers.h = clear_bit(@registers.h, bit_pos) }
34
+ when :l then -> { @registers.l = clear_bit(@registers.l, bit_pos) }
35
+ when :mem_hl
36
+ lambda do
37
+ value_at_mem_hl = @cpu.bus_read(address: @registers.hl)
38
+ result = clear_bit(value_at_mem_hl, bit_pos)
39
+ @cpu.bus_write(address: @registers.hl, value: result)
40
+ end
41
+ else
42
+ raise ArgumentError, 'Unknown CbRes target'
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,70 @@
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 RL (Rotate Left Through Carry) instructions.
8
+ #
9
+ # - RL r8
10
+ # - RL [HL]
11
+ class CbRl < Base
12
+ include Utils::BitOps
13
+
14
+ def initialize(cpu:, target:)
15
+ super(cpu:)
16
+
17
+ @mnemonic = "RL #{format_operand(target)}"
18
+ @logic = build_logic(target)
19
+ end
20
+
21
+ private
22
+
23
+ def build_logic(target)
24
+ case target
25
+ when :b then -> { @registers.b = rl_reg8(@registers.b) }
26
+ when :c then -> { @registers.c = rl_reg8(@registers.c) }
27
+ when :d then -> { @registers.d = rl_reg8(@registers.d) }
28
+ when :e then -> { @registers.e = rl_reg8(@registers.e) }
29
+ when :h then -> { @registers.h = rl_reg8(@registers.h) }
30
+ when :l then -> { @registers.l = rl_reg8(@registers.l) }
31
+ when :mem_hl then -> { rl_mem_hl }
32
+ when :a then -> { @registers.a = rl_reg8(@registers.a) }
33
+ else
34
+ raise ArgumentError, 'Unknown CbRl target'
35
+ end
36
+ end
37
+
38
+ # [C] <- [7][6][5][4][3][2][1][0] <- [C]
39
+ # [7] [6][5][4][3][2][1][0][C]
40
+ #
41
+ def rl_reg8(reg8_value)
42
+ carry_in = @registers.c_flag
43
+ old_bit7 = bit(reg8_value, 7)
44
+ result = (reg8_value << 1) | carry_in
45
+
46
+ @registers.clear_flags
47
+ @registers.z_flag = result.nobits?(0xFF)
48
+ @registers.c_flag = old_bit7 == 1
49
+
50
+ result
51
+ end
52
+
53
+ # Takes 2 extra cycles due to the Bus read and write operations.
54
+ def rl_mem_hl
55
+ value_at_mem_hl = @cpu.bus_read(address: @registers.hl)
56
+ carry_in = @registers.c_flag
57
+ old_bit7 = bit(value_at_mem_hl, 7)
58
+ result = (value_at_mem_hl << 1) | carry_in
59
+
60
+ @registers.clear_flags
61
+ @registers.z_flag = result.nobits?(0xFF)
62
+ @registers.c_flag = old_bit7 == 1
63
+
64
+ @cpu.bus_write(address: @registers.hl, value: result)
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,68 @@
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 RLC (Rotate Left Circular) instructions.
8
+ #
9
+ # - RLC r8
10
+ # - RLC [HL]
11
+ class CbRlc < Base
12
+ include Utils::BitOps
13
+
14
+ def initialize(cpu:, target:)
15
+ super(cpu:)
16
+
17
+ @mnemonic = "RLC #{format_operand(target)}"
18
+ @logic = build_logic(target)
19
+ end
20
+
21
+ private
22
+
23
+ def build_logic(target)
24
+ case target
25
+ when :b then -> { @registers.b = rlc_reg8(@registers.b) }
26
+ when :c then -> { @registers.c = rlc_reg8(@registers.c) }
27
+ when :d then -> { @registers.d = rlc_reg8(@registers.d) }
28
+ when :e then -> { @registers.e = rlc_reg8(@registers.e) }
29
+ when :h then -> { @registers.h = rlc_reg8(@registers.h) }
30
+ when :l then -> { @registers.l = rlc_reg8(@registers.l) }
31
+ when :mem_hl then -> { rlc_mem_hl }
32
+ when :a then -> { @registers.a = rlc_reg8(@registers.a) }
33
+ else
34
+ raise ArgumentError, 'Unknown CbRlc target'
35
+ end
36
+ end
37
+
38
+ # [C] <- [7][6][5][4][3][2][1][0] <- [7]
39
+ # [C=7] [6][5][4][3][2][1][0][7]
40
+ #
41
+ def rlc_reg8(reg8_value)
42
+ old_bit7 = bit(reg8_value, 7)
43
+ result = (reg8_value << 1) | old_bit7
44
+
45
+ @registers.clear_flags
46
+ @registers.z_flag = result.nobits?(0xFF)
47
+ @registers.c_flag = old_bit7 == 1
48
+
49
+ result
50
+ end
51
+
52
+ # Takes 2 extra cycles due to the Bus read and write operations.
53
+ def rlc_mem_hl
54
+ value_at_mem_hl = @cpu.bus_read(address: @registers.hl)
55
+ old_bit7 = bit(value_at_mem_hl, 7)
56
+ result = (value_at_mem_hl << 1) | old_bit7
57
+
58
+ @registers.clear_flags
59
+ @registers.z_flag = result.nobits?(0xFF)
60
+ @registers.c_flag = old_bit7 == 1
61
+
62
+ @cpu.bus_write(address: @registers.hl, value: result)
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,70 @@
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 RR (Rotate Right Through Carry) instructions.
8
+ #
9
+ # - RR r8
10
+ # - RR [HL]
11
+ class CbRr < Base
12
+ include Utils::BitOps
13
+
14
+ def initialize(cpu:, target:)
15
+ super(cpu:)
16
+
17
+ @mnemonic = "RR #{format_operand(target)}"
18
+ @logic = build_logic(target)
19
+ end
20
+
21
+ private
22
+
23
+ def build_logic(target)
24
+ case target
25
+ when :b then -> { @registers.b = rr_reg8(@registers.b) }
26
+ when :c then -> { @registers.c = rr_reg8(@registers.c) }
27
+ when :d then -> { @registers.d = rr_reg8(@registers.d) }
28
+ when :e then -> { @registers.e = rr_reg8(@registers.e) }
29
+ when :h then -> { @registers.h = rr_reg8(@registers.h) }
30
+ when :l then -> { @registers.l = rr_reg8(@registers.l) }
31
+ when :mem_hl then -> { rr_mem_hl }
32
+ when :a then -> { @registers.a = rr_reg8(@registers.a) }
33
+ else
34
+ raise ArgumentError, 'Unknown CbRr target'
35
+ end
36
+ end
37
+
38
+ # [C] -> [7][6][5][4][3][2][1][0] -> [C]
39
+ # [C][7][6][5][4][3][2][1] -> [0]
40
+ #
41
+ def rr_reg8(reg8_value)
42
+ carry_in = @registers.c_flag
43
+ old_bit0 = bit(reg8_value, 0)
44
+ result = (carry_in << 7) | (reg8_value >> 1)
45
+
46
+ @registers.clear_flags
47
+ @registers.z_flag = result.nobits?(0xFF)
48
+ @registers.c_flag = old_bit0 == 1
49
+
50
+ result
51
+ end
52
+
53
+ # Takes 2 extra cycles due to the Bus read and write operations.
54
+ def rr_mem_hl
55
+ value_at_mem_hl = @cpu.bus_read(address: @registers.hl)
56
+ carry_in = @registers.c_flag
57
+ old_bit0 = bit(value_at_mem_hl, 0)
58
+ result = (carry_in << 7) | (value_at_mem_hl >> 1)
59
+
60
+ @registers.clear_flags
61
+ @registers.z_flag = result.nobits?(0xFF)
62
+ @registers.c_flag = old_bit0 == 1
63
+
64
+ @cpu.bus_write(address: @registers.hl, value: result)
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,68 @@
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 RRC (Rotate Right Circular) instructions.
8
+ #
9
+ # - RRC r8
10
+ # - RRC [HL]
11
+ class CbRrc < Base
12
+ include Utils::BitOps
13
+
14
+ def initialize(cpu:, target:)
15
+ super(cpu:)
16
+
17
+ @mnemonic = "RRC #{format_operand(target)}"
18
+ @logic = build_logic(target)
19
+ end
20
+
21
+ private
22
+
23
+ def build_logic(target)
24
+ case target
25
+ when :b then -> { @registers.b = rrc_reg8(@registers.b) }
26
+ when :c then -> { @registers.c = rrc_reg8(@registers.c) }
27
+ when :d then -> { @registers.d = rrc_reg8(@registers.d) }
28
+ when :e then -> { @registers.e = rrc_reg8(@registers.e) }
29
+ when :h then -> { @registers.h = rrc_reg8(@registers.h) }
30
+ when :l then -> { @registers.l = rrc_reg8(@registers.l) }
31
+ when :mem_hl then -> { rrc_mem_hl }
32
+ when :a then -> { @registers.a = rrc_reg8(@registers.a) }
33
+ else
34
+ raise ArgumentError, 'Unknown CbRrc target'
35
+ end
36
+ end
37
+
38
+ # [0] -> [7][6][5][4][3][2][1][0] -> [C]
39
+ # [0][7][6][5][4][3][2][1] -> [C=0]
40
+ #
41
+ def rrc_reg8(reg8_value)
42
+ old_bit0 = bit(reg8_value, 0)
43
+ result = (old_bit0 << 7) | (reg8_value >> 1)
44
+
45
+ @registers.clear_flags
46
+ @registers.z_flag = result.nobits?(0xFF)
47
+ @registers.c_flag = old_bit0 == 1
48
+
49
+ result
50
+ end
51
+
52
+ # Takes 2 extra cycles due to the Bus read and write operations.
53
+ def rrc_mem_hl
54
+ value_at_mem_hl = @cpu.bus_read(address: @registers.hl)
55
+ old_bit0 = bit(value_at_mem_hl, 0)
56
+ result = (old_bit0 << 7) | (value_at_mem_hl >> 1)
57
+
58
+ @registers.clear_flags
59
+ @registers.z_flag = result.nobits?(0xFF)
60
+ @registers.c_flag = old_bit0 == 1
61
+
62
+ @cpu.bus_write(address: @registers.hl, value: result)
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,51 @@
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 SET instructions.
8
+ #
9
+ # - SET u3, r8
10
+ # - SET u3, [HL]
11
+ class CbSet < Base
12
+ include Utils::BitOps
13
+
14
+ # Creates a new CbSet object containing the mnemonic (String) and logic (Proc).
15
+ def initialize(cpu:, bit_pos:, target:)
16
+ super(cpu:)
17
+
18
+ @mnemonic = "SET #{bit_pos}, #{format_operand(target)}"
19
+ @logic = build_logic(bit_pos, target)
20
+ end
21
+
22
+ private
23
+
24
+ # @param bit_pos [Integer] Integer between 0 and 7.
25
+ # @param target [Symbol] Either a 8-bit register (:a, :b, :c, ...) or :mem_hl.
26
+ #
27
+ # @return [Proc] Setups what the instruction should execute at runtime.
28
+ #
29
+ def build_logic(bit_pos, target)
30
+ case target
31
+ when :a then -> { @registers.a = set_bit(@registers.a, bit_pos) }
32
+ when :b then -> { @registers.b = set_bit(@registers.b, bit_pos) }
33
+ when :c then -> { @registers.c = set_bit(@registers.c, bit_pos) }
34
+ when :d then -> { @registers.d = set_bit(@registers.d, bit_pos) }
35
+ when :e then -> { @registers.e = set_bit(@registers.e, bit_pos) }
36
+ when :h then -> { @registers.h = set_bit(@registers.h, bit_pos) }
37
+ when :l then -> { @registers.l = set_bit(@registers.l, bit_pos) }
38
+ when :mem_hl
39
+ lambda do
40
+ result = set_bit(@cpu.bus_read(address: @registers.hl), bit_pos)
41
+ @cpu.bus_write(address: @registers.hl, value: result)
42
+ end
43
+ else
44
+ raise ArgumentError, 'Unknown CbSet target'
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end