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.
- checksums.yaml +7 -0
- data/.gitignore +14 -0
- data/.rspec +1 -0
- data/.rubocop.yml +54 -0
- data/Gemfile +32 -0
- data/Gemfile.lock +267 -0
- data/LICENSE +21 -0
- data/README.md +115 -0
- data/Steepfile +7 -0
- data/exe/amaterasu +23 -0
- data/lib/amaterasu/cartridge/mbc1.rb +56 -0
- data/lib/amaterasu/cartridge/rom.rb +118 -0
- data/lib/amaterasu/cartridge.rb +68 -0
- data/lib/amaterasu/cli.rb +60 -0
- data/lib/amaterasu/emulator.rb +121 -0
- data/lib/amaterasu/game_boy/apu.rb +12 -0
- data/lib/amaterasu/game_boy/bus.rb +161 -0
- data/lib/amaterasu/game_boy/cpu/instructions/adc.rb +64 -0
- data/lib/amaterasu/game_boy/cpu/instructions/add16.rb +73 -0
- data/lib/amaterasu/game_boy/cpu/instructions/add8.rb +63 -0
- data/lib/amaterasu/game_boy/cpu/instructions/and.rb +62 -0
- data/lib/amaterasu/game_boy/cpu/instructions/base.rb +38 -0
- data/lib/amaterasu/game_boy/cpu/instructions/call.rb +48 -0
- data/lib/amaterasu/game_boy/cpu/instructions/cb_bit.rb +52 -0
- data/lib/amaterasu/game_boy/cpu/instructions/cb_res.rb +49 -0
- data/lib/amaterasu/game_boy/cpu/instructions/cb_rl.rb +70 -0
- data/lib/amaterasu/game_boy/cpu/instructions/cb_rlc.rb +68 -0
- data/lib/amaterasu/game_boy/cpu/instructions/cb_rr.rb +70 -0
- data/lib/amaterasu/game_boy/cpu/instructions/cb_rrc.rb +68 -0
- data/lib/amaterasu/game_boy/cpu/instructions/cb_set.rb +51 -0
- data/lib/amaterasu/game_boy/cpu/instructions/cb_sla.rb +69 -0
- data/lib/amaterasu/game_boy/cpu/instructions/cb_sra.rb +71 -0
- data/lib/amaterasu/game_boy/cpu/instructions/cb_srl.rb +69 -0
- data/lib/amaterasu/game_boy/cpu/instructions/cb_swap.rb +67 -0
- data/lib/amaterasu/game_boy/cpu/instructions/cp.rb +61 -0
- data/lib/amaterasu/game_boy/cpu/instructions/daa.rb +59 -0
- data/lib/amaterasu/game_boy/cpu/instructions/dec.rb +64 -0
- data/lib/amaterasu/game_boy/cpu/instructions/di.rb +21 -0
- data/lib/amaterasu/game_boy/cpu/instructions/ei.rb +19 -0
- data/lib/amaterasu/game_boy/cpu/instructions/halt.rb +19 -0
- data/lib/amaterasu/game_boy/cpu/instructions/inc.rb +64 -0
- data/lib/amaterasu/game_boy/cpu/instructions/jp.rb +54 -0
- data/lib/amaterasu/game_boy/cpu/instructions/jr.rb +45 -0
- data/lib/amaterasu/game_boy/cpu/instructions/ld16.rb +79 -0
- data/lib/amaterasu/game_boy/cpu/instructions/ld8.rb +210 -0
- data/lib/amaterasu/game_boy/cpu/instructions/ldh.rb +61 -0
- data/lib/amaterasu/game_boy/cpu/instructions/misc.rb +53 -0
- data/lib/amaterasu/game_boy/cpu/instructions/nop.rb +19 -0
- data/lib/amaterasu/game_boy/cpu/instructions/or.rb +56 -0
- data/lib/amaterasu/game_boy/cpu/instructions/pop.rb +39 -0
- data/lib/amaterasu/game_boy/cpu/instructions/push.rb +43 -0
- data/lib/amaterasu/game_boy/cpu/instructions/ret.rb +70 -0
- data/lib/amaterasu/game_boy/cpu/instructions/rotate.rb +120 -0
- data/lib/amaterasu/game_boy/cpu/instructions/rst.rb +33 -0
- data/lib/amaterasu/game_boy/cpu/instructions/sbc.rb +64 -0
- data/lib/amaterasu/game_boy/cpu/instructions/stop.rb +19 -0
- data/lib/amaterasu/game_boy/cpu/instructions/sub.rb +63 -0
- data/lib/amaterasu/game_boy/cpu/instructions/xor.rb +60 -0
- data/lib/amaterasu/game_boy/cpu/instructions.rb +600 -0
- data/lib/amaterasu/game_boy/cpu/registers.rb +264 -0
- data/lib/amaterasu/game_boy/cpu.rb +232 -0
- data/lib/amaterasu/game_boy/dma.rb +114 -0
- data/lib/amaterasu/game_boy/interrupts.rb +108 -0
- data/lib/amaterasu/game_boy/joypad.rb +127 -0
- data/lib/amaterasu/game_boy/oam/sprite.rb +106 -0
- data/lib/amaterasu/game_boy/oam.rb +29 -0
- data/lib/amaterasu/game_boy/ppu/modes/disabled.rb +29 -0
- data/lib/amaterasu/game_boy/ppu/modes/h_blank.rb +45 -0
- data/lib/amaterasu/game_boy/ppu/modes/oam_scan.rb +93 -0
- data/lib/amaterasu/game_boy/ppu/modes/rendering/bg_win_fetcher.rb +204 -0
- data/lib/amaterasu/game_boy/ppu/modes/rendering/pixel_emitter.rb +83 -0
- data/lib/amaterasu/game_boy/ppu/modes/rendering/pixel_fifo.rb +70 -0
- data/lib/amaterasu/game_boy/ppu/modes/rendering/sprite_fetcher.rb +140 -0
- data/lib/amaterasu/game_boy/ppu/modes/rendering.rb +108 -0
- data/lib/amaterasu/game_boy/ppu/modes/v_blank.rb +43 -0
- data/lib/amaterasu/game_boy/ppu/modes.rb +22 -0
- data/lib/amaterasu/game_boy/ppu/registers/lcd_control.rb +57 -0
- data/lib/amaterasu/game_boy/ppu/registers/lcd_status.rb +88 -0
- data/lib/amaterasu/game_boy/ppu/registers.rb +131 -0
- data/lib/amaterasu/game_boy/ppu.rb +207 -0
- data/lib/amaterasu/game_boy/ram.rb +70 -0
- data/lib/amaterasu/game_boy/serial.rb +91 -0
- data/lib/amaterasu/game_boy/timer.rb +230 -0
- data/lib/amaterasu/game_boy/vram/tile.rb +68 -0
- data/lib/amaterasu/game_boy/vram/tile_data.rb +52 -0
- data/lib/amaterasu/game_boy/vram/tile_map.rb +71 -0
- data/lib/amaterasu/game_boy/vram.rb +51 -0
- data/lib/amaterasu/hal/console.rb +23 -0
- data/lib/amaterasu/hal/sdl2/bindings.rb +59 -0
- data/lib/amaterasu/hal/sdl2.rb +127 -0
- data/lib/amaterasu/utils/bit_ops.rb +22 -0
- data/lib/amaterasu.rb +13 -0
- data/sig/akane/cartridge/rom.rbs +29 -0
- data/sig/akane/cartridge.rbs +12 -0
- data/sig/akane/cli.rbs +16 -0
- data/sig/akane/emulator.rbs +19 -0
- data/sig/akane/game_boy/apu.rbs +7 -0
- data/sig/akane/game_boy/bus.rbs +25 -0
- data/sig/akane/game_boy/cpu/instructions/adc.rbs +18 -0
- data/sig/akane/game_boy/cpu/instructions/add16.rbs +19 -0
- data/sig/akane/game_boy/cpu/instructions/add8.rbs +18 -0
- data/sig/akane/game_boy/cpu/instructions/and.rbs +18 -0
- data/sig/akane/game_boy/cpu/instructions/base.rbs +20 -0
- data/sig/akane/game_boy/cpu/instructions/call.rbs +18 -0
- data/sig/akane/game_boy/cpu/instructions/cb_bit.rbs +20 -0
- data/sig/akane/game_boy/cpu/instructions/cb_res.rbs +19 -0
- data/sig/akane/game_boy/cpu/instructions/cb_rl.rbs +21 -0
- data/sig/akane/game_boy/cpu/instructions/cb_rlc.rbs +21 -0
- data/sig/akane/game_boy/cpu/instructions/cb_rr.rbs +21 -0
- data/sig/akane/game_boy/cpu/instructions/cb_rrc.rbs +21 -0
- data/sig/akane/game_boy/cpu/instructions/cb_set.rbs +20 -0
- data/sig/akane/game_boy/cpu/instructions/cb_sla.rbs +21 -0
- data/sig/akane/game_boy/cpu/instructions/cb_sra.rbs +21 -0
- data/sig/akane/game_boy/cpu/instructions/cb_srl.rbs +21 -0
- data/sig/akane/game_boy/cpu/instructions/cb_swap.rbs +21 -0
- data/sig/akane/game_boy/cpu/instructions/cp.rbs +18 -0
- data/sig/akane/game_boy/cpu/instructions/daa.rbs +17 -0
- data/sig/akane/game_boy/cpu/instructions/dec.rbs +19 -0
- data/sig/akane/game_boy/cpu/instructions/di.rbs +13 -0
- data/sig/akane/game_boy/cpu/instructions/ei.rbs +13 -0
- data/sig/akane/game_boy/cpu/instructions/halt.rbs +13 -0
- data/sig/akane/game_boy/cpu/instructions/inc.rbs +19 -0
- data/sig/akane/game_boy/cpu/instructions/jp.rbs +18 -0
- data/sig/akane/game_boy/cpu/instructions/jr.rbs +18 -0
- data/sig/akane/game_boy/cpu/instructions/ld16.rbs +18 -0
- data/sig/akane/game_boy/cpu/instructions/ld8.rbs +31 -0
- data/sig/akane/game_boy/cpu/instructions/ldh.rbs +23 -0
- data/sig/akane/game_boy/cpu/instructions/misc.rbs +20 -0
- data/sig/akane/game_boy/cpu/instructions/nop.rbs +13 -0
- data/sig/akane/game_boy/cpu/instructions/or.rbs +18 -0
- data/sig/akane/game_boy/cpu/instructions/pop.rbs +17 -0
- data/sig/akane/game_boy/cpu/instructions/push.rbs +18 -0
- data/sig/akane/game_boy/cpu/instructions/ret.rbs +20 -0
- data/sig/akane/game_boy/cpu/instructions/rotate.rbs +23 -0
- data/sig/akane/game_boy/cpu/instructions/rst.rbs +17 -0
- data/sig/akane/game_boy/cpu/instructions/sbc.rbs +19 -0
- data/sig/akane/game_boy/cpu/instructions/stop.rbs +13 -0
- data/sig/akane/game_boy/cpu/instructions/sub.rbs +18 -0
- data/sig/akane/game_boy/cpu/instructions/xor.rbs +19 -0
- data/sig/akane/game_boy/cpu/instructions.rbs +12 -0
- data/sig/akane/game_boy/cpu/registers.rbs +56 -0
- data/sig/akane/game_boy/cpu.rbs +39 -0
- data/sig/akane/game_boy/interrupts.rbs +28 -0
- data/sig/akane/game_boy/joypad.rbs +25 -0
- data/sig/akane/game_boy/oam/sprite.rbs +30 -0
- data/sig/akane/game_boy/ppu/modes/disabled.rbs +17 -0
- data/sig/akane/game_boy/ppu/modes/h_blank.rbs +20 -0
- data/sig/akane/game_boy/ppu/modes/oam_scan.rbs +28 -0
- data/sig/akane/game_boy/ppu/modes/rendering.rbs +26 -0
- data/sig/akane/game_boy/ppu/modes/v_blank.rbs +20 -0
- data/sig/akane/game_boy/ppu/modes.rbs +13 -0
- data/sig/akane/game_boy/ppu.rbs +59 -0
- data/sig/akane/game_boy/ram.rbs +16 -0
- data/sig/akane/game_boy/serial.rbs +21 -0
- data/sig/akane/game_boy/timer.rbs +30 -0
- data/sig/akane/utils/bit_ops.rbs +11 -0
- data/sig/akane.rbs +3 -0
- 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
|