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,69 @@
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 SLA instructions.
8
+ #
9
+ # - SLA r8
10
+ # - SLA [HL]
11
+ class CbSla < Base
12
+ include Utils::BitOps
13
+
14
+ def initialize(cpu:, target:)
15
+ super(cpu:)
16
+
17
+ @mnemonic = "SLA #{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 = sla_reg8(@registers.b) }
26
+ when :c then -> { @registers.c = sla_reg8(@registers.c) }
27
+ when :d then -> { @registers.d = sla_reg8(@registers.d) }
28
+ when :e then -> { @registers.e = sla_reg8(@registers.e) }
29
+ when :h then -> { @registers.h = sla_reg8(@registers.h) }
30
+ when :l then -> { @registers.l = sla_reg8(@registers.l) }
31
+ when :mem_hl then -> { sla_mem_hl }
32
+ when :a then -> { @registers.a = sla_reg8(@registers.a) }
33
+ else
34
+ raise ArgumentError, 'Unknown CbSla target'
35
+ end
36
+ end
37
+
38
+ # Shift Left Arithmetically.
39
+ #
40
+ # C <- [7][6][5][4][3][2][1][0] <- 0
41
+ #
42
+ def sla_reg8(reg8_value)
43
+ old_bit7 = bit(reg8_value, 7)
44
+ result = reg8_value << 1
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 sla_mem_hl
55
+ byte = @cpu.bus_read(address: @registers.hl)
56
+ old_bit7 = bit(byte, 7)
57
+ result = byte << 1
58
+
59
+ @registers.clear_flags
60
+ @registers.z_flag = result.nobits?(0xFF)
61
+ @registers.c_flag = old_bit7 == 1
62
+
63
+ @cpu.bus_write(address: @registers.hl, value: result)
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,71 @@
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 SRA instructions.
8
+ #
9
+ # - SRA r8
10
+ # - SRA [HL]
11
+ class CbSra < Base
12
+ include Utils::BitOps
13
+
14
+ def initialize(cpu:, target:)
15
+ super(cpu:)
16
+
17
+ @mnemonic = "SRA #{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 = sra_reg8(@registers.b) }
26
+ when :c then -> { @registers.c = sra_reg8(@registers.c) }
27
+ when :d then -> { @registers.d = sra_reg8(@registers.d) }
28
+ when :e then -> { @registers.e = sra_reg8(@registers.e) }
29
+ when :h then -> { @registers.h = sra_reg8(@registers.h) }
30
+ when :l then -> { @registers.l = sra_reg8(@registers.l) }
31
+ when :mem_hl then -> { sra_mem_hl }
32
+ when :a then -> { @registers.a = sra_reg8(@registers.a) }
33
+ else
34
+ raise ArgumentError, 'Unknown CbSra target'
35
+ end
36
+ end
37
+
38
+ # Shift Right Arithmetically.
39
+ #
40
+ # [7][6][5][4][3][2][1][0] -> C
41
+ #
42
+ def sra_reg8(reg8)
43
+ old_bit0 = bit(reg8, 0)
44
+ old_bit7 = bit(reg8, 7)
45
+ result = (old_bit7 << 7) | (reg8 >> 1)
46
+
47
+ @registers.clear_flags
48
+ @registers.z_flag = result.nobits?(0xFF)
49
+ @registers.c_flag = old_bit0 == 1
50
+
51
+ result
52
+ end
53
+
54
+ # Takes 2 extra cycles due to the Bus read and write operations.
55
+ def sra_mem_hl
56
+ byte = @cpu.bus_read(address: @registers.hl)
57
+ old_bit0 = bit(byte, 0)
58
+ old_bit7 = bit(byte, 7)
59
+ result = (old_bit7 << 7) | (byte >> 1)
60
+
61
+ @registers.clear_flags
62
+ @registers.z_flag = result.nobits?(0xFF)
63
+ @registers.c_flag = old_bit0 == 1
64
+
65
+ @cpu.bus_write(address: @registers.hl, value: result)
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,69 @@
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 SRL instructions.
8
+ #
9
+ # - SRL r8
10
+ # - SRL [HL]
11
+ class CbSrl < Base
12
+ include Utils::BitOps
13
+
14
+ def initialize(cpu:, target:)
15
+ super(cpu:)
16
+
17
+ @mnemonic = "SRL #{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 = srl_reg8(@registers.b) }
26
+ when :c then -> { @registers.c = srl_reg8(@registers.c) }
27
+ when :d then -> { @registers.d = srl_reg8(@registers.d) }
28
+ when :e then -> { @registers.e = srl_reg8(@registers.e) }
29
+ when :h then -> { @registers.h = srl_reg8(@registers.h) }
30
+ when :l then -> { @registers.l = srl_reg8(@registers.l) }
31
+ when :mem_hl then -> { srl_mem_hl }
32
+ when :a then -> { @registers.a = srl_reg8(@registers.a) }
33
+ else
34
+ raise ArgumentError, 'Unknown CbSrl target'
35
+ end
36
+ end
37
+
38
+ # Shift Right Logically.
39
+ #
40
+ # 0 -> [7][6][5][4][3][2][1][0] -> C
41
+ #
42
+ def srl_reg8(reg8)
43
+ old_bit0 = bit(reg8, 0)
44
+ result = reg8 >> 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 srl_mem_hl
55
+ byte = @cpu.bus_read(address: @registers.hl)
56
+ old_bit0 = bit(byte, 0)
57
+ result = byte >> 1
58
+
59
+ @registers.clear_flags
60
+ @registers.z_flag = result.nobits?(0xFF)
61
+ @registers.c_flag = old_bit0 == 1
62
+
63
+ @cpu.bus_write(address: @registers.hl, value: result)
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,67 @@
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 SWAP instructions.
8
+ #
9
+ # - SWAP r8
10
+ # - SWAP [HL]
11
+ class CbSwap < Base
12
+ include Utils::BitOps
13
+
14
+ def initialize(cpu:, target:)
15
+ super(cpu:)
16
+
17
+ @mnemonic = "SWAP #{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 = swap(@registers.b) }
26
+ when :c then -> { @registers.c = swap(@registers.c) }
27
+ when :d then -> { @registers.d = swap(@registers.d) }
28
+ when :e then -> { @registers.e = swap(@registers.e) }
29
+ when :h then -> { @registers.h = swap(@registers.h) }
30
+ when :l then -> { @registers.l = swap(@registers.l) }
31
+ when :mem_hl then -> { swap_mem_hl }
32
+ when :a then -> { @registers.a = swap(@registers.a) }
33
+ else
34
+ raise ArgumentError, 'Unknown CbSwap target'
35
+ end
36
+ end
37
+
38
+ # Swaps the positions of the Upper and Lower 4 bits.
39
+ #
40
+ # 11110000 -> 00001111
41
+ #
42
+ def swap(target)
43
+ upper4 = (target >> 4) & 0x0F
44
+ result = (target << 4) | upper4
45
+
46
+ @registers.clear_flags
47
+ @registers.z_flag = result.nobits?(0xFF)
48
+
49
+ result
50
+ end
51
+
52
+ # Takes 2 extra cycles due to the Bus read and write operations.
53
+ def swap_mem_hl
54
+ byte = @cpu.bus_read(address: @registers.hl)
55
+ upper4 = (byte >> 4) & 0x0F
56
+ result = (byte << 4) | upper4
57
+
58
+ @registers.clear_flags
59
+ @registers.z_flag = result.nobits?(0xFF)
60
+
61
+ @cpu.bus_write(address: @registers.hl, value: result)
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,61 @@
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 CP instructions
8
+ #
9
+ # - CP A, r8
10
+ # - CP A, [HL]
11
+ # - CP A, n8
12
+ class Cp < 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 = "CP A, #{source}"
19
+ @logic = build_logic(source)
20
+ end
21
+
22
+ private
23
+
24
+ # Builds the logic for all CP instructions.
25
+ # Returns a lambda object to be called by the CPU.
26
+ def build_logic(source)
27
+ case source
28
+ when :a then -> { cp_a(@registers.a) }
29
+ when :b then -> { cp_a(@registers.b) }
30
+ when :c then -> { cp_a(@registers.c) }
31
+ when :d then -> { cp_a(@registers.d) }
32
+ when :e then -> { cp_a(@registers.e) }
33
+ when :h then -> { cp_a(@registers.h) }
34
+ when :l then -> { cp_a(@registers.l) }
35
+ when :mem_hl then -> { cp_a(@cpu.bus_read(address: @registers.hl)) }
36
+ when :imm8 then -> { cp_a(@cpu.fetch_next_byte) }
37
+ else
38
+ raise ArgumentError, 'Unknown Cp source'
39
+ end
40
+ end
41
+
42
+ # Subtracts a given value from register A and discard the result.
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 cp_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
+ end
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Amaterasu
4
+ module GameBoy
5
+ class Cpu
6
+ module Instructions
7
+ # Holds the logic for DAA instruction.
8
+ class Daa < Base
9
+ def initialize(cpu:)
10
+ super
11
+
12
+ @mnemonic = 'DAA'
13
+ @logic = -> { daa }
14
+ end
15
+
16
+ private
17
+
18
+ # Decimal Adjust Accumulator.
19
+ #
20
+ # Is used after performing a given arithmetic operation (ADD, SUB, ADC, SBC)
21
+ # whose inputs were in Binary-Coded Decimal (BCD).
22
+ # So after running this instruction the numbers would be adjusted to BCD format.
23
+ #
24
+ # This instruction never clears the Carry flag (1 => 0).
25
+ # It either preserves the value (0 => 0), (1 => 1) or sets it (0 => 1).
26
+ #
27
+ def daa
28
+ acc = @registers.a
29
+ carry_in = @registers.c_flag
30
+ new_carry = carry_in == 1
31
+ bcd_correction = 0x00
32
+
33
+ if @registers.n_flag.zero?
34
+ bcd_correction |= 0x06 if @registers.h_flag == 1 || (acc & 0x0F) > 0x09
35
+
36
+ if @registers.c_flag == 1 || (acc & 0xFF) > 0x99
37
+ bcd_correction |= 0x60
38
+ new_carry = true
39
+ end
40
+
41
+ bcd_adjusted = acc + bcd_correction
42
+ else
43
+ bcd_correction |= 0x06 if @registers.h_flag == 1
44
+ bcd_correction |= 0x60 if @registers.c_flag == 1
45
+
46
+ bcd_adjusted = acc - bcd_correction
47
+ end
48
+
49
+ @registers.z_flag = bcd_adjusted.nobits?(0xFF)
50
+ @registers.h_flag = false
51
+ @registers.c_flag = new_carry
52
+
53
+ @registers.a = bcd_adjusted
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,64 @@
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 Decrement instructions.
8
+ class Dec < Base
9
+ def initialize(cpu:, operand:)
10
+ super(cpu:)
11
+
12
+ @mnemonic = "DEC #{format_operand(operand)}"
13
+ @logic = build_logic(operand)
14
+ end
15
+
16
+ private
17
+
18
+ def build_logic(operand)
19
+ case operand
20
+ when :a then -> { @registers.a = dec(@registers.a) }
21
+ when :b then -> { @registers.b = dec(@registers.b) }
22
+ when :c then -> { @registers.c = dec(@registers.c) }
23
+ when :d then -> { @registers.d = dec(@registers.d) }
24
+ when :e then -> { @registers.e = dec(@registers.e) }
25
+ when :h then -> { @registers.h = dec(@registers.h) }
26
+ when :l then -> { @registers.l = dec(@registers.l) }
27
+ when :bc then -> { @registers.bc = dec16(@registers.bc) }
28
+ when :de then -> { @registers.de = dec16(@registers.de) }
29
+ when :hl then -> { @registers.hl = dec16(@registers.hl) }
30
+ when :sp then -> { @registers.sp = dec16(@registers.sp) }
31
+ when :mem_hl
32
+ lambda {
33
+ value_at_mem_hl = @cpu.bus_read(address: @registers.hl)
34
+ @cpu.bus_write(address: @registers.hl, value: dec(value_at_mem_hl))
35
+ }
36
+ else
37
+ raise ArgumentError, 'Unknown Dec operand'
38
+ end
39
+ end
40
+
41
+ # M-cycle 1: Decrements a 8-bit value, sets the flags.
42
+ #
43
+ def dec(value)
44
+ result = value - 1
45
+
46
+ @registers.z_flag = result.nobits?(0xFF)
47
+ @registers.n_flag = true
48
+ @registers.h_flag = value.nobits?(0x0F)
49
+
50
+ result
51
+ end
52
+
53
+ # M-cycle 1: Decrements a 16-bit register value.
54
+ # M-cycle 2: Internal processing due to the 16-bit subtraction.
55
+ # --------- Flags are untouched in the dec16.
56
+ #
57
+ def dec16(reg16)
58
+ @cpu.sub16(reg16, 1)
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Amaterasu
4
+ module GameBoy
5
+ class Cpu
6
+ module Instructions
7
+ # Holds the logic of the DI (Disable Interrupts) instruction.
8
+ class Di < Base
9
+ def initialize(cpu:)
10
+ super
11
+
12
+ @mnemonic = 'DI'
13
+ @bytes = 1
14
+ @m_cycles = 1
15
+ @logic = -> { @cpu.disable_interrupts }
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ 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 of the DI (Disable Interrupts) instruction.
8
+ class Ei < Base
9
+ def initialize(cpu:)
10
+ super
11
+
12
+ @mnemonic = 'EI'
13
+ @logic = -> { @cpu.enable_interrupts }
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Amaterasu
4
+ module GameBoy
5
+ class Cpu
6
+ module Instructions
7
+ # Handles the logic for the HALT instruction.
8
+ class Halt < Base
9
+ def initialize(cpu:)
10
+ super
11
+
12
+ @mnemonic = 'HALT'
13
+ @logic = -> { @cpu.halt }
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,64 @@
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 INC (Increment) instructions.
8
+ class Inc < Base
9
+ def initialize(cpu:, operand:)
10
+ super(cpu:)
11
+
12
+ @mnemonic = "INC #{format_operand(operand)}"
13
+ @logic = build_logic(operand)
14
+ end
15
+
16
+ private
17
+
18
+ def build_logic(operand)
19
+ case operand
20
+ when :a then -> { @registers.a = inc(@registers.a) }
21
+ when :b then -> { @registers.b = inc(@registers.b) }
22
+ when :c then -> { @registers.c = inc(@registers.c) }
23
+ when :d then -> { @registers.d = inc(@registers.d) }
24
+ when :e then -> { @registers.e = inc(@registers.e) }
25
+ when :h then -> { @registers.h = inc(@registers.h) }
26
+ when :l then -> { @registers.l = inc(@registers.l) }
27
+ when :bc then -> { @registers.bc = inc16(@registers.bc) }
28
+ when :de then -> { @registers.de = inc16(@registers.de) }
29
+ when :hl then -> { @registers.hl = inc16(@registers.hl) }
30
+ when :sp then -> { @registers.sp = inc16(@registers.sp) }
31
+ when :mem_hl
32
+ lambda do
33
+ value_at_mem_hl = @cpu.bus_read(address: @registers.hl)
34
+ @cpu.bus_write(address: @registers.hl, value: inc(value_at_mem_hl))
35
+ end
36
+ else
37
+ raise ArgumentError, 'Unknown Inc operand'
38
+ end
39
+ end
40
+
41
+ # M-cycle 1: Increments a 8-bit value, sets the flags.
42
+ #
43
+ def inc(value)
44
+ result = value + 1
45
+
46
+ @registers.z_flag = result.nobits?(0xFF)
47
+ @registers.n_flag = false
48
+ @registers.h_flag = value.allbits?(0x0F)
49
+
50
+ result
51
+ end
52
+
53
+ # M-cycle 1: Increments a 16-bit register value.
54
+ # M-cycle 2: Internal processing due to the 16-bit addition.
55
+ # --------- Flags are untouched in the inc16.
56
+ #
57
+ def inc16(reg16)
58
+ @cpu.add16(reg16, 1)
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,54 @@
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 load instructions.
8
+ class Jp < Base
9
+ def initialize(cpu:, location:, condition: nil)
10
+ super(cpu:)
11
+
12
+ @mnemonic = "JP #{format_operand(condition)}, #{format_operand(location)}"
13
+ @logic = build_logic(location, condition)
14
+ end
15
+
16
+ private
17
+
18
+ def build_logic(location, condition)
19
+ if condition.nil?
20
+ case location
21
+ when :imm16 then -> { @cpu.jump_to(address: @cpu.fetch_next_word) }
22
+ when :hl then -> { @registers.pc = @registers.hl } # No extra cycle
23
+ else
24
+ raise ArgumentError, 'Unknown Jp location'
25
+ end
26
+ else
27
+ case condition
28
+ when :nz then -> { jp(condition: @registers.z_flag.zero?) }
29
+ when :nc then -> { jp(condition: @registers.c_flag.zero?) }
30
+ when :z then -> { jp(condition: @registers.z_flag == 1) }
31
+ when :c then -> { jp(condition: @registers.c_flag == 1) }
32
+ else
33
+ raise ArgumentError, 'Unknown Jp condition'
34
+ end
35
+ end
36
+ end
37
+
38
+ # M-cycle 1: Fetches the opcode.
39
+ # M-cycle 2: Fetches next immediate byte (lsb).
40
+ # M-cycle 3: Fetches next immediate byte (msb).
41
+ # --------- Returns early if condition is not met.
42
+ # M-cycle 4: Jumps to the address (takes 1 internal cycle).
43
+ #
44
+ def jp(condition:)
45
+ address = @cpu.fetch_next_word
46
+ return unless condition
47
+
48
+ @cpu.jump_to(address:)
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,45 @@
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 JR (Jump Relative) instructions.
8
+ class Jr < Base
9
+ def initialize(cpu:, condition: nil)
10
+ super(cpu:)
11
+
12
+ @mnemonic = "JR #{format_operand(condition)}, e8"
13
+ @logic = build_logic(condition)
14
+ end
15
+
16
+ private
17
+
18
+ def build_logic(condition)
19
+ case condition
20
+ when :nz then -> { jr(@registers.z_flag.zero?) }
21
+ when :z then -> { jr(@registers.z_flag == 1) }
22
+ when :nc then -> { jr(@registers.c_flag.zero?) }
23
+ when :c then -> { jr(@registers.c_flag == 1) }
24
+ else -> { jr(true) }
25
+ end
26
+ end
27
+
28
+ # Jumps relative to a signed offset.
29
+ #
30
+ # - CPU always fetches the unsigned byte.
31
+ # - Calculates the offset by signing the value between -128 and +127.
32
+ # - If it's a conditional jump, check the condition and return early if it's false.
33
+ # - If the condition is true or there is no condition, jump.
34
+ def jr(cc)
35
+ unsigned_byte = @cpu.fetch_next_byte
36
+ offset = @cpu.sign_value(unsigned_byte)
37
+ return unless cc
38
+
39
+ @cpu.jump_to(address: @registers.pc + offset)
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end