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,79 @@
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 16-bit Load instructions.
8
+ class Ld16 < Base
9
+ def initialize(cpu:, target:, source:)
10
+ super(cpu:)
11
+
12
+ @mnemonic = "LD #{format_operand(target)}, #{format_operand(source)}"
13
+ @logic = build_logic(target, source)
14
+ end
15
+
16
+ private
17
+
18
+ def build_logic(target, source)
19
+ return -> { ld_mem_imm16_sp } if target == :mem_imm16
20
+
21
+ case target
22
+ when :bc then -> { @registers.bc = @cpu.fetch_next_word }
23
+ when :de then -> { @registers.de = @cpu.fetch_next_word }
24
+ when :hl
25
+ case source
26
+ when :imm16 then -> { @registers.hl = @cpu.fetch_next_word }
27
+ when :sp_plus_sig8
28
+ lambda do
29
+ unsigned_byte = @cpu.fetch_next_byte
30
+ signed_value = @cpu.sign_value(unsigned_byte)
31
+ sp = @registers.sp
32
+ result = @cpu.add16(@registers.sp, signed_value)
33
+
34
+ @registers.clear_flags
35
+ @registers.h_flag = (sp & 0x0F) + (unsigned_byte & 0x0F) > 0x0F
36
+ @registers.c_flag = (sp & 0xFF) + (unsigned_byte & 0xFF) > 0xFF
37
+
38
+ @registers.hl = result
39
+ end
40
+ else
41
+ raise ArgumentError, 'Unknown Ld16 source for LD HL'
42
+ end
43
+ when :sp
44
+ case source
45
+ when :imm16 then -> { @registers.sp = @cpu.fetch_next_word }
46
+ when :hl
47
+ lambda do
48
+ @cpu.internal_processing
49
+ @registers.sp = @registers.hl
50
+ end
51
+ else
52
+ raise ArgumentError, 'Unknown Ld16 source for LD SP'
53
+ end
54
+ else
55
+ raise ArgumentError, 'Unknown Ld16 target'
56
+ end
57
+ end
58
+
59
+ # Loads the LSB from SP to the imm16 address.
60
+ # Loads the MSB from SP to the imm16 address + 1.
61
+ #
62
+ # M-cycle 1: Fetches the opcode.
63
+ # M-cycle 2: Fetches the LSB of the address to be used.
64
+ # M-cycle 3: Fetches the MSB of the address to be used.
65
+ # M-cycle 4: Writes the LSB of SP into the address.
66
+ # M-cycle 5: Writes the MSB of SP into the address + 1.
67
+ #
68
+ def ld_mem_imm16_sp
69
+ imm16 = @cpu.fetch_next_word
70
+ sp = @registers.sp
71
+
72
+ @cpu.bus_write(address: imm16, value: sp & 0xFF)
73
+ @cpu.bus_write(address: imm16 + 1, value: (sp >> 8) & 0xFF)
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,210 @@
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 8-bit Load instructions.
8
+ class Ld8 < Base
9
+ def initialize(cpu:, target:, source:)
10
+ super(cpu:)
11
+
12
+ @mnemonic = "LD #{format_operand(target)}, #{format_operand(source)}"
13
+ @logic = build_logic(target, source)
14
+ end
15
+
16
+ private
17
+
18
+ def build_logic(target, source)
19
+ return -> { ld_a_mem_imm16 } if source == :mem_imm16
20
+ return -> { ld_a_mem_bc } if source == :mem_bc
21
+ return -> { ld_a_mem_de } if source == :mem_de
22
+
23
+ case target
24
+ when :a then load_a(source)
25
+ when :b then load_b(source)
26
+ when :c then load_c(source)
27
+ when :d then load_d(source)
28
+ when :e then load_e(source)
29
+ when :h then load_h(source)
30
+ when :l then load_l(source)
31
+ when :mem_hl then load_mem_hl(source)
32
+ when :mem_hli then -> { load_mem_hli }
33
+ when :mem_hld then -> { load_mem_hld }
34
+ when :mem_bc then -> { @cpu.bus_write(address: @registers.bc, value: @registers.a) }
35
+ when :mem_de then -> { @cpu.bus_write(address: @registers.de, value: @registers.a) }
36
+ when :mem_imm16 then -> { ld_mem_imm16_a }
37
+ else
38
+ raise ArgumentError, 'Unknown target for :build_logic in Ld8'
39
+ end
40
+ end
41
+
42
+ def load_a(source)
43
+ case source
44
+ when :a then -> {}
45
+ when :b then -> { @registers.a = @registers.b }
46
+ when :c then -> { @registers.a = @registers.c }
47
+ when :d then -> { @registers.a = @registers.d }
48
+ when :e then -> { @registers.a = @registers.e }
49
+ when :h then -> { @registers.a = @registers.h }
50
+ when :l then -> { @registers.a = @registers.l }
51
+ when :imm8 then -> { @registers.a = @cpu.fetch_next_byte }
52
+ when :mem_hl then -> { @registers.a = @cpu.bus_read(address: @registers.hl) }
53
+ when :mem_hli
54
+ lambda do
55
+ @registers.a = @cpu.bus_read(address: @registers.hl)
56
+ @registers.hl += 1
57
+ end
58
+ when :mem_hld
59
+ lambda do
60
+ @registers.a = @cpu.bus_read(address: @registers.hl)
61
+ @registers.hl -= 1
62
+ end
63
+ else
64
+ raise ArgumentError, 'Unknown source for :load_a in Ld8'
65
+ end
66
+ end
67
+
68
+ def load_b(source)
69
+ case source
70
+ when :a then -> { @registers.b = @registers.a }
71
+ when :b then -> {}
72
+ when :c then -> { @registers.b = @registers.c }
73
+ when :d then -> { @registers.b = @registers.d }
74
+ when :e then -> { @registers.b = @registers.e }
75
+ when :h then -> { @registers.b = @registers.h }
76
+ when :l then -> { @registers.b = @registers.l }
77
+ when :imm8 then -> { @registers.b = @cpu.fetch_next_byte }
78
+ when :mem_hl then -> { @registers.b = @cpu.bus_read(address: @registers.hl) }
79
+ else
80
+ raise ArgumentError, 'Unknown source for :load_b in Ld8'
81
+ end
82
+ end
83
+
84
+ def load_c(source)
85
+ case source
86
+ when :a then -> { @registers.c = @registers.a }
87
+ when :b then -> { @registers.c = @registers.b }
88
+ when :c then -> {}
89
+ when :d then -> { @registers.c = @registers.d }
90
+ when :e then -> { @registers.c = @registers.e }
91
+ when :h then -> { @registers.c = @registers.h }
92
+ when :l then -> { @registers.c = @registers.l }
93
+ when :imm8 then -> { @registers.c = @cpu.fetch_next_byte }
94
+ when :mem_hl then -> { @registers.c = @cpu.bus_read(address: @registers.hl) }
95
+ else
96
+ raise ArgumentError, 'Unknown source for :load_c in Ld8'
97
+ end
98
+ end
99
+
100
+ def load_d(source)
101
+ case source
102
+ when :a then -> { @registers.d = @registers.a }
103
+ when :b then -> { @registers.d = @registers.b }
104
+ when :c then -> { @registers.d = @registers.c }
105
+ when :d then -> {}
106
+ when :e then -> { @registers.d = @registers.e }
107
+ when :h then -> { @registers.d = @registers.h }
108
+ when :l then -> { @registers.d = @registers.l }
109
+ when :imm8 then -> { @registers.d = @cpu.fetch_next_byte }
110
+ when :mem_hl then -> { @registers.d = @cpu.bus_read(address: @registers.hl) }
111
+ else
112
+ raise ArgumentError, 'Unknown source for :load_d in Ld8'
113
+ end
114
+ end
115
+
116
+ def load_e(source)
117
+ case source
118
+ when :a then -> { @registers.e = @registers.a }
119
+ when :b then -> { @registers.e = @registers.b }
120
+ when :c then -> { @registers.e = @registers.c }
121
+ when :d then -> { @registers.e = @registers.d }
122
+ when :e then -> {}
123
+ when :h then -> { @registers.e = @registers.h }
124
+ when :l then -> { @registers.e = @registers.l }
125
+ when :imm8 then -> { @registers.e = @cpu.fetch_next_byte }
126
+ when :mem_hl then -> { @registers.e = @cpu.bus_read(address: @registers.hl) }
127
+ else
128
+ raise ArgumentError, 'Unknown source for :load_e in Ld8'
129
+ end
130
+ end
131
+
132
+ def load_h(source)
133
+ case source
134
+ when :a then -> { @registers.h = @registers.a }
135
+ when :b then -> { @registers.h = @registers.b }
136
+ when :c then -> { @registers.h = @registers.c }
137
+ when :d then -> { @registers.h = @registers.d }
138
+ when :e then -> { @registers.h = @registers.e }
139
+ when :h then -> {}
140
+ when :l then -> { @registers.h = @registers.l }
141
+ when :imm8 then -> { @registers.h = @cpu.fetch_next_byte }
142
+ when :mem_hl then -> { @registers.h = @cpu.bus_read(address: @registers.hl) }
143
+ else
144
+ raise ArgumentError, 'Unknown source for :load_h in Ld8'
145
+ end
146
+ end
147
+
148
+ def load_l(source)
149
+ case source
150
+ when :a then -> { @registers.l = @registers.a }
151
+ when :b then -> { @registers.l = @registers.b }
152
+ when :c then -> { @registers.l = @registers.c }
153
+ when :d then -> { @registers.l = @registers.d }
154
+ when :e then -> { @registers.l = @registers.e }
155
+ when :h then -> { @registers.l = @registers.h }
156
+ when :l then -> {}
157
+ when :imm8 then -> { @registers.l = @cpu.fetch_next_byte }
158
+ when :mem_hl then -> { @registers.l = @cpu.bus_read(address: @registers.hl) }
159
+ else
160
+ raise ArgumentError, 'Unknown source for :load_l in Ld8'
161
+ end
162
+ end
163
+
164
+ def load_mem_hl(source)
165
+ case source
166
+ when :a then -> { @cpu.bus_write(address: @registers.hl, value: @registers.a) }
167
+ when :b then -> { @cpu.bus_write(address: @registers.hl, value: @registers.b) }
168
+ when :c then -> { @cpu.bus_write(address: @registers.hl, value: @registers.c) }
169
+ when :d then -> { @cpu.bus_write(address: @registers.hl, value: @registers.d) }
170
+ when :e then -> { @cpu.bus_write(address: @registers.hl, value: @registers.e) }
171
+ when :h then -> { @cpu.bus_write(address: @registers.hl, value: @registers.h) }
172
+ when :l then -> { @cpu.bus_write(address: @registers.hl, value: @registers.l) }
173
+ when :imm8 then -> { @cpu.bus_write(address: @registers.hl, value: @cpu.fetch_next_byte) }
174
+ else
175
+ raise ArgumentError, 'Unknown source for :load_mem_hl in Ld8'
176
+ end
177
+ end
178
+
179
+ def load_mem_hli
180
+ @cpu.bus_write(address: @registers.hl, value: @registers.a)
181
+ @registers.hl += 1
182
+ end
183
+
184
+ def load_mem_hld
185
+ @cpu.bus_write(address: @registers.hl, value: @registers.a)
186
+ @registers.hl -= 1
187
+ end
188
+
189
+ def ld_mem_imm16_a
190
+ imm16_address = @cpu.fetch_next_word
191
+ @cpu.bus_write(address: imm16_address, value: @registers.a)
192
+ end
193
+
194
+ def ld_a_mem_imm16
195
+ imm16_address = @cpu.fetch_next_word
196
+ @registers.a = @cpu.bus_read(address: imm16_address)
197
+ end
198
+
199
+ def ld_a_mem_bc
200
+ @registers.a = @cpu.bus_read(address: @registers.bc)
201
+ end
202
+
203
+ def ld_a_mem_de
204
+ @registers.a = @cpu.bus_read(address: @registers.de)
205
+ end
206
+ end
207
+ end
208
+ end
209
+ end
210
+ end
@@ -0,0 +1,61 @@
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 LDH (Load High RAM) instructions.
8
+ class Ldh < Base
9
+ IO_BASE_ADDRESS = 0xFF00
10
+
11
+ def initialize(cpu:, target:, source:)
12
+ super(cpu:)
13
+
14
+ @mnemonic = "LDH #{format_operand(target)}, #{format_operand(source)}"
15
+ @bytes = 2
16
+ @m_cycles = 2
17
+ @logic = build_logic(target, source)
18
+ end
19
+
20
+ private
21
+
22
+ def build_logic(target, source)
23
+ case target
24
+ when :a
25
+ case source
26
+ when :mem_unsig8 then -> { ldh_a_mem_unsig8 }
27
+ when :mem_c then -> { ldh_a_mem_c }
28
+ else
29
+ raise ArgumentError, 'Unknown Ldh source for :a target'
30
+ end
31
+ when :mem_unsig8 then -> { ldh_mem_unsig8_a }
32
+ when :mem_c then -> { ldh_mem_c_a }
33
+ else
34
+ raise ArgumentError, 'Unknown Ldh target'
35
+ end
36
+ end
37
+
38
+ def ldh_mem_unsig8_a
39
+ unsigned_byte = @cpu.fetch_next_byte
40
+
41
+ @cpu.bus_write(address: IO_BASE_ADDRESS + unsigned_byte, value: @registers.a)
42
+ end
43
+
44
+ def ldh_a_mem_unsig8
45
+ unsigned_byte = @cpu.fetch_next_byte
46
+
47
+ @registers.a = @cpu.bus_read(address: IO_BASE_ADDRESS + unsigned_byte)
48
+ end
49
+
50
+ def ldh_mem_c_a
51
+ @cpu.bus_write(address: IO_BASE_ADDRESS + @registers.c, value: @registers.a)
52
+ end
53
+
54
+ def ldh_a_mem_c
55
+ @registers.a = @cpu.bus_read(address: IO_BASE_ADDRESS + @registers.c)
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Amaterasu
4
+ module GameBoy
5
+ class Cpu
6
+ module Instructions
7
+ # Holds the logic for CPL, SCF and CCF instructions.
8
+ class Misc < Base
9
+ def initialize(cpu:, operation:)
10
+ super(cpu:)
11
+
12
+ @mnemonic = format_operand(operation)
13
+ @logic = build_logic(operation)
14
+ end
15
+
16
+ private
17
+
18
+ def build_logic(operation)
19
+ case operation
20
+ when :cpl then -> { cpl }
21
+ when :scf then -> { scf }
22
+ when :ccf then -> { ccf }
23
+ else
24
+ raise ArgumentError, 'Unknown Misc operation'
25
+ end
26
+ end
27
+
28
+ # Set Carry flag.
29
+ def scf
30
+ @registers.n_flag = false
31
+ @registers.h_flag = false
32
+ @registers.c_flag = true
33
+ end
34
+
35
+ # Complement the Carry flag.
36
+ def ccf
37
+ @registers.n_flag = false
38
+ @registers.h_flag = false
39
+ @registers.c_flag = @registers.c_flag.zero?
40
+ end
41
+
42
+ # Complement the value from the A register.
43
+ def cpl
44
+ @registers.a = ~@registers.a
45
+
46
+ @registers.n_flag = true
47
+ @registers.h_flag = true
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,19 @@
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 Nop < Base
9
+ def initialize(cpu:)
10
+ super
11
+
12
+ @mnemonic = 'NOP'
13
+ @logic = -> {}
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,56 @@
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 OR instructions
8
+ #
9
+ # - OR A, r8
10
+ # - OR A, [HL]
11
+ # - OR A, n8
12
+ class Or < 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 = "OR A, #{source}"
19
+ @logic = build_logic(source)
20
+ end
21
+
22
+ private
23
+
24
+ # Builds the logic for all OR instructions.
25
+ # Returns a lambda object to be called by the CPU.
26
+ def build_logic(source)
27
+ case source
28
+ when :a then -> { or_a(@registers.a) }
29
+ when :b then -> { or_a(@registers.b) }
30
+ when :c then -> { or_a(@registers.c) }
31
+ when :d then -> { or_a(@registers.d) }
32
+ when :e then -> { or_a(@registers.e) }
33
+ when :h then -> { or_a(@registers.h) }
34
+ when :l then -> { or_a(@registers.l) }
35
+ when :mem_hl then -> { or_a(@cpu.bus_read(address: @registers.hl)) }
36
+ when :imm8 then -> { or_a(@cpu.fetch_next_byte) }
37
+ else
38
+ raise ArgumentError, 'Unknown Or source'
39
+ end
40
+ end
41
+
42
+ # Performs a Bitwise OR between a given value and the A register.
43
+ #
44
+ def or_a(value)
45
+ result = @registers.a | value
46
+
47
+ @registers.clear_flags
48
+ @registers.z_flag = result.nobits?(0xFF)
49
+
50
+ @registers.a = result
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,39 @@
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 POP instructions.
8
+ class Pop < Base
9
+ # Creates a Pop instruction object with a mnemonic and logic to be executed.
10
+ def initialize(cpu:, reg16:)
11
+ super(cpu:)
12
+
13
+ @mnemonic = "POP #{format_operand(reg16)}"
14
+ @logic = build_logic(reg16)
15
+ end
16
+
17
+ private
18
+
19
+ # Returns a Proc object to be executed by the CPU at runtime.
20
+ #
21
+ # M-cycle 1: Fetches opcode.
22
+ # M-cycle 2: Gets the reg16 lsb from the stack.
23
+ # M-cycle 3: Gets the reg16 msb from the stack.
24
+ # Assigns the 16-bit value to a given reg16 (same cycle).
25
+ def build_logic(reg16)
26
+ case reg16
27
+ when :bc then -> { @registers.bc = @cpu.stack_pop }
28
+ when :de then -> { @registers.de = @cpu.stack_pop }
29
+ when :hl then -> { @registers.hl = @cpu.stack_pop }
30
+ when :af then -> { @registers.af = @cpu.stack_pop }
31
+ else
32
+ raise ArgumentError, 'Unknown Pop reg16'
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,43 @@
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 PUSH instructions.
8
+ class Push < Base
9
+ # Creates a Push instruction object with a mnemonic and logic to be executed.
10
+ def initialize(cpu:, reg16:)
11
+ super(cpu:)
12
+
13
+ @mnemonic = "PUSH #{format_operand(reg16)}"
14
+ @logic = build_logic(reg16)
15
+ end
16
+
17
+ private
18
+
19
+ # Returns a Proc object to be executed by the CPU at runtime.
20
+ def build_logic(reg16)
21
+ case reg16
22
+ when :bc then -> { push(@registers.bc) }
23
+ when :de then -> { push(@registers.de) }
24
+ when :hl then -> { push(@registers.hl) }
25
+ when :af then -> { push(@registers.af) }
26
+ else
27
+ raise ArgumentError, 'Unknown Push reg16'
28
+ end
29
+ end
30
+
31
+ # M-cycle 1: Fetches opcode.
32
+ # M-cycle 2: Internal processing (dead cycle).
33
+ # M-cycle 3: Writes msb from reg16 into the stack.
34
+ # M-cycle 4: Writes lsb from reg16 into the stack.
35
+ def push(reg16)
36
+ @cpu.internal_processing
37
+ @cpu.stack_push(value: reg16)
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ 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 RET + RETI instructions.
8
+ class Ret < Base
9
+ # Creates a Ret instruction object with a mnemonic and logic to be executed.
10
+ def initialize(cpu:, condition: nil, enable_ime: false)
11
+ super(cpu:)
12
+
13
+ @mnemonic = enable_ime ? 'RETI' : "RET #{format_operand(condition)}".strip
14
+ @logic = build_logic(condition, enable_ime)
15
+ end
16
+
17
+ private
18
+
19
+ # Returns a Proc object to be executed by the CPU at runtime.
20
+ def build_logic(condition, enable_ime)
21
+ return -> { reti } if enable_ime
22
+ return -> { ret } if condition.nil?
23
+
24
+ case condition
25
+ when :nz then -> { ret_cc(condition: @registers.z_flag.zero?) }
26
+ when :z then -> { ret_cc(condition: @registers.z_flag == 1) }
27
+ when :nc then -> { ret_cc(condition: @registers.c_flag.zero?) }
28
+ when :c then -> { ret_cc(condition: @registers.c_flag == 1) }
29
+ else
30
+ raise ArgumentError, 'Unknown Ret condition'
31
+ end
32
+ end
33
+
34
+ # M-cycle 1: Fetches opcode.
35
+ # M-cycle 2: Spends 1 cycle to evaluate condition (Only for RET cc).
36
+ # ---------- Returns early if condition not met.
37
+ # M-cycle 3: Pops the LSB from the Stack.
38
+ # M-cycle 4: Pops the MSB from the Stack.
39
+ # M-cycle 5: Jumps to the return address.
40
+ def ret_cc(condition: true)
41
+ @cpu.internal_processing
42
+ return unless condition
43
+
44
+ return_address = @cpu.stack_pop
45
+ @cpu.jump_to(address: return_address)
46
+ end
47
+
48
+ # M-cycle 1: Fetches opcode.
49
+ # M-cycle 2: Pops the LSB from the Stack.
50
+ # M-cycle 3: Pops the MSB from the Stack.
51
+ # M-cycle 4: Jumps to the return address.
52
+ def ret
53
+ return_address = @cpu.stack_pop
54
+ @cpu.jump_to(address: return_address)
55
+ end
56
+
57
+ # M-cycle 1: Fetches opcode.
58
+ # M-cycle 2: Pops the LSB from the Stack.
59
+ # M-cycle 3: Pops the MSB from the Stack.
60
+ # M-cycle 4: Jumps to the return address
61
+ # Enable interrupts (same cycle).
62
+ def reti
63
+ ret
64
+ @cpu.enable_interrupts
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end