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,120 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Amaterasu
|
|
4
|
+
module GameBoy
|
|
5
|
+
class Cpu
|
|
6
|
+
module Instructions
|
|
7
|
+
# Holds the logic of all the Rotate instructions.
|
|
8
|
+
#
|
|
9
|
+
# - RLCA: Rotate Left Circular A.
|
|
10
|
+
# - RRCA: Rotate Right Circular A.
|
|
11
|
+
# - RLA: Rotate Left Through Carry A.
|
|
12
|
+
# - RRA: Rotate Right Through Carry A.
|
|
13
|
+
class Rotate < Base
|
|
14
|
+
include Utils::BitOps
|
|
15
|
+
|
|
16
|
+
def initialize(cpu:, operation:)
|
|
17
|
+
super(cpu:)
|
|
18
|
+
|
|
19
|
+
@mnemonic = format_operand(operation).to_s
|
|
20
|
+
@logic = build_logic(operation)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
|
|
25
|
+
# Returns a lambda object containing the instruction logic.
|
|
26
|
+
def build_logic(operation)
|
|
27
|
+
case operation
|
|
28
|
+
when :rlca then -> { rlca }
|
|
29
|
+
when :rrca then -> { rrca }
|
|
30
|
+
when :rla then -> { rla }
|
|
31
|
+
when :rra then -> { rra }
|
|
32
|
+
else
|
|
33
|
+
raise ArgumentError, 'Unknown Rotate operation'
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Rotate Left Circular.
|
|
38
|
+
# Rotate all bits from the A register to the left.
|
|
39
|
+
# Bit 7 falls off the left and at the same time:
|
|
40
|
+
# - Either sets or clears the Carry flag depending on it's value.
|
|
41
|
+
# - Wraps around the A register and becomes Bit 0.
|
|
42
|
+
#
|
|
43
|
+
# Before: [C] <- [7][6][5][4][3][2][1][0]
|
|
44
|
+
# After : [C=7] [6][5][4][3][2][1][0][7] <-
|
|
45
|
+
#
|
|
46
|
+
# M-cycle 1: Fetches the opcode and performs the rotate.
|
|
47
|
+
#
|
|
48
|
+
def rlca
|
|
49
|
+
old_bit7 = bit(@registers.a, 7)
|
|
50
|
+
|
|
51
|
+
@registers.clear_flags
|
|
52
|
+
@registers.c_flag = old_bit7 == 1
|
|
53
|
+
|
|
54
|
+
@registers.a = (@registers.a << 1) | old_bit7
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Rotate Right Circular.
|
|
58
|
+
# Rotate all bits from the A register to the right.
|
|
59
|
+
# Bit 0 falls off the right side and at the same time:
|
|
60
|
+
# - Either sets or clears the Carry flag depending on it's value.
|
|
61
|
+
# - Wraps around the A register and becomes Bit 7.
|
|
62
|
+
#
|
|
63
|
+
# Before: [7][6][5][4][3][2][1][0] -> [C]
|
|
64
|
+
# After : -> [0][7][6][5][4][3][2][1] [C=0]
|
|
65
|
+
#
|
|
66
|
+
# M-cycle 1: Fetches the opcode and performs the rotate.
|
|
67
|
+
#
|
|
68
|
+
def rrca
|
|
69
|
+
old_bit0 = bit(@registers.a, 0)
|
|
70
|
+
|
|
71
|
+
@registers.clear_flags
|
|
72
|
+
@registers.c_flag = old_bit0 == 1
|
|
73
|
+
|
|
74
|
+
@registers.a = (old_bit0 << 7) | (@registers.a >> 1)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Rotate Left Through Carry.
|
|
78
|
+
# - Rotate all bits from the A register to the left.
|
|
79
|
+
# - Bit 7 falls off the left side and becomes the Carry flag.
|
|
80
|
+
# - The value of Carry flag becomes the new Bit 0.
|
|
81
|
+
#
|
|
82
|
+
# Before: [C] <- [7][6][5][4][3][2][1][0]
|
|
83
|
+
# After : [7] [6][5][4][3][2][1][0][C] <-
|
|
84
|
+
#
|
|
85
|
+
# M-cycle 1: Fetches the opcode and performs the rotate.
|
|
86
|
+
#
|
|
87
|
+
def rla
|
|
88
|
+
carry_in = @registers.c_flag
|
|
89
|
+
old_bit7 = bit(@registers.a, 7)
|
|
90
|
+
|
|
91
|
+
@registers.clear_flags
|
|
92
|
+
@registers.c_flag = old_bit7 == 1
|
|
93
|
+
|
|
94
|
+
@registers.a = (@registers.a << 1) | carry_in
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# Rotate Right Through Carry.
|
|
98
|
+
# - Rotate all bits from the A register to the right.
|
|
99
|
+
# - Bit 0 falls off the right side and becomes the Carry flag.
|
|
100
|
+
# - The previous value of Carry flag becomes the new Bit 0.
|
|
101
|
+
#
|
|
102
|
+
# Before: [7][6][5][4][3][2][1][0] -> [C]
|
|
103
|
+
# After : -> [C][7][6][5][4][3][2][1]
|
|
104
|
+
#
|
|
105
|
+
# M-cycle 1: Fetches the opcode and performs the rotate.
|
|
106
|
+
#
|
|
107
|
+
def rra
|
|
108
|
+
carry_in = @registers.c_flag
|
|
109
|
+
old_bit0 = bit(@registers.a, 0)
|
|
110
|
+
|
|
111
|
+
@registers.clear_flags
|
|
112
|
+
@registers.c_flag = old_bit0 == 1
|
|
113
|
+
|
|
114
|
+
@registers.a = (carry_in << 7) | (@registers.a >> 1)
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Amaterasu
|
|
4
|
+
module GameBoy
|
|
5
|
+
class Cpu
|
|
6
|
+
module Instructions
|
|
7
|
+
# Holds the logic of all the RST instructions.
|
|
8
|
+
class Rst < Base
|
|
9
|
+
# Creates a Call instruction object with a mnemonic and logic to be executed.
|
|
10
|
+
def initialize(cpu:, vector:)
|
|
11
|
+
super(cpu:)
|
|
12
|
+
|
|
13
|
+
@mnemonic = "RST $#{format('%02X', vector)}"
|
|
14
|
+
@logic = -> { rst(vector) }
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
private
|
|
18
|
+
|
|
19
|
+
# This is essentially a CALL, but jumps to a fixed address vector.
|
|
20
|
+
#
|
|
21
|
+
# M-cycle 1: Fetches opcode.
|
|
22
|
+
# M-cycle 2: Stores the msb of the PC into the Stack.
|
|
23
|
+
# M-cycle 3: Stores the lsb of the PC into the Stack.
|
|
24
|
+
# M-cycle 4: Jumps to a fixed address vector.
|
|
25
|
+
def rst(vector)
|
|
26
|
+
@cpu.stack_push(value: @registers.pc)
|
|
27
|
+
@cpu.jump_to(address: vector)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Amaterasu
|
|
4
|
+
module GameBoy
|
|
5
|
+
class Cpu
|
|
6
|
+
module Instructions
|
|
7
|
+
# Handles the logic related to all possible SBC instructions
|
|
8
|
+
#
|
|
9
|
+
# - SBC A, r8
|
|
10
|
+
# - SBC A, [HL]
|
|
11
|
+
# - SBC A, n8
|
|
12
|
+
class Sbc < Base
|
|
13
|
+
# @param cpu [Cpu] Holds a direct reference to the main Cpu object.
|
|
14
|
+
# @param source [Symbol] Operator, can be a register, :mem_hl, :imm8.
|
|
15
|
+
def initialize(cpu:, source:)
|
|
16
|
+
super(cpu:)
|
|
17
|
+
|
|
18
|
+
@mnemonic = "SBC A, #{source}"
|
|
19
|
+
@logic = build_logic(source)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
|
|
24
|
+
# Builds the logic for all SBC instructions.
|
|
25
|
+
# Returns a lambda object to be called by the CPU.
|
|
26
|
+
def build_logic(source)
|
|
27
|
+
case source
|
|
28
|
+
when :a then -> { sbc_a(@registers.a) }
|
|
29
|
+
when :b then -> { sbc_a(@registers.b) }
|
|
30
|
+
when :c then -> { sbc_a(@registers.c) }
|
|
31
|
+
when :d then -> { sbc_a(@registers.d) }
|
|
32
|
+
when :e then -> { sbc_a(@registers.e) }
|
|
33
|
+
when :h then -> { sbc_a(@registers.h) }
|
|
34
|
+
when :l then -> { sbc_a(@registers.l) }
|
|
35
|
+
when :mem_hl then -> { sbc_a(@cpu.bus_read(address: @registers.hl)) }
|
|
36
|
+
when :imm8 then -> { sbc_a(@cpu.fetch_next_byte) }
|
|
37
|
+
else
|
|
38
|
+
raise ArgumentError, 'Unknown Sbc source'
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Subtracts a given value + Carry flag from register A and stores it back into A.
|
|
43
|
+
#
|
|
44
|
+
# - Sets the Zero flag if the result is zero, otherwise clears it.
|
|
45
|
+
# - Always sets the SBCtraction flag.
|
|
46
|
+
# - Sets the Half Carry flag if it needed to borrow from Bit 4.
|
|
47
|
+
# - Sets the Carry flag if it needed to borrow (acc < value).
|
|
48
|
+
def sbc_a(value)
|
|
49
|
+
acc = @registers.a
|
|
50
|
+
carry_in = @registers.c_flag
|
|
51
|
+
result = @registers.a - (value + carry_in)
|
|
52
|
+
|
|
53
|
+
@registers.z_flag = result.nobits?(0xFF)
|
|
54
|
+
@registers.n_flag = true
|
|
55
|
+
@registers.h_flag = (acc & 0x0F) < ((value & 0x0F) + carry_in)
|
|
56
|
+
@registers.c_flag = acc < (value + carry_in)
|
|
57
|
+
|
|
58
|
+
@registers.a = result
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Amaterasu
|
|
4
|
+
module GameBoy
|
|
5
|
+
class Cpu
|
|
6
|
+
module Instructions
|
|
7
|
+
# Holds the logic for STOP instruction.
|
|
8
|
+
class Stop < Base
|
|
9
|
+
def initialize(cpu:)
|
|
10
|
+
super
|
|
11
|
+
|
|
12
|
+
@mnemonic = 'STOP'
|
|
13
|
+
@logic = -> { @cpu.fetch_next_byte }
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Amaterasu
|
|
4
|
+
module GameBoy
|
|
5
|
+
class Cpu
|
|
6
|
+
module Instructions
|
|
7
|
+
# Handles the logic related to all possible SUB instructions
|
|
8
|
+
#
|
|
9
|
+
# - SUB A, r8
|
|
10
|
+
# - SUB A, [HL]
|
|
11
|
+
# - SUB A, n8
|
|
12
|
+
class Sub < Base
|
|
13
|
+
# @param cpu [Cpu] Holds a direct reference to the main Cpu object.
|
|
14
|
+
# @param source [Symbol] Operator, can be a register, :mem_hl, :imm8.
|
|
15
|
+
def initialize(cpu:, source:)
|
|
16
|
+
super(cpu:)
|
|
17
|
+
|
|
18
|
+
@mnemonic = "SUB A, #{format_operand(source)}"
|
|
19
|
+
@logic = build_logic(source)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
|
|
24
|
+
# Builds the logic for all SUB instructions.
|
|
25
|
+
# Returns a lambda object to be called by the CPU.
|
|
26
|
+
def build_logic(source)
|
|
27
|
+
case source
|
|
28
|
+
when :a then -> { sub_a(@registers.a) }
|
|
29
|
+
when :b then -> { sub_a(@registers.b) }
|
|
30
|
+
when :c then -> { sub_a(@registers.c) }
|
|
31
|
+
when :d then -> { sub_a(@registers.d) }
|
|
32
|
+
when :e then -> { sub_a(@registers.e) }
|
|
33
|
+
when :h then -> { sub_a(@registers.h) }
|
|
34
|
+
when :l then -> { sub_a(@registers.l) }
|
|
35
|
+
when :mem_hl then -> { sub_a(@cpu.bus_read(address: @registers.hl)) }
|
|
36
|
+
when :imm8 then -> { sub_a(@cpu.fetch_next_byte) }
|
|
37
|
+
else
|
|
38
|
+
-> { raise 'Not implemented Sub operation' }
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Subtracts a given value from register A and stores it back into A.
|
|
43
|
+
#
|
|
44
|
+
# - Sets the Zero flag if the result is zero, otherwise clears it.
|
|
45
|
+
# - Always sets the Subtraction flag.
|
|
46
|
+
# - Sets the Half Carry flag if it needed to borrow from Bit 4.
|
|
47
|
+
# - Sets the Carry flag if it needed to borrow (acc < value).
|
|
48
|
+
def sub_a(value)
|
|
49
|
+
acc = @registers.a
|
|
50
|
+
result = @registers.a - value
|
|
51
|
+
|
|
52
|
+
@registers.z_flag = result.nobits?(0xFF)
|
|
53
|
+
@registers.n_flag = true
|
|
54
|
+
@registers.h_flag = (acc & 0x0F) < (value & 0x0F)
|
|
55
|
+
@registers.c_flag = acc < value
|
|
56
|
+
|
|
57
|
+
@registers.a = result
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Amaterasu
|
|
4
|
+
module GameBoy
|
|
5
|
+
class Cpu
|
|
6
|
+
module Instructions
|
|
7
|
+
# Handles the logic related to all possible XOR instructions
|
|
8
|
+
#
|
|
9
|
+
# - XOR A, r8
|
|
10
|
+
# - XOR A, [HL]
|
|
11
|
+
# - XOR A, n8
|
|
12
|
+
class Xor < Base
|
|
13
|
+
# @param cpu [Cpu] Holds a direct reference to the main Cpu object.
|
|
14
|
+
# @param source [Symbol] Operator, can be a register, :mem_hl, :imm8.
|
|
15
|
+
def initialize(cpu:, source:)
|
|
16
|
+
super(cpu:)
|
|
17
|
+
|
|
18
|
+
@mnemonic = define_mnemonic(source)
|
|
19
|
+
@logic = build_logic(source)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
|
|
24
|
+
def define_mnemonic(source)
|
|
25
|
+
case source
|
|
26
|
+
when :mem_hl then 'XOR A, [HL]'
|
|
27
|
+
when :imm8 then 'XOR A, n8'
|
|
28
|
+
else "XOR A, #{source.upcase}"
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def build_logic(source)
|
|
33
|
+
case source
|
|
34
|
+
when :a then -> { xor_a(@registers.a) }
|
|
35
|
+
when :b then -> { xor_a(@registers.b) }
|
|
36
|
+
when :c then -> { xor_a(@registers.c) }
|
|
37
|
+
when :d then -> { xor_a(@registers.d) }
|
|
38
|
+
when :e then -> { xor_a(@registers.e) }
|
|
39
|
+
when :h then -> { xor_a(@registers.h) }
|
|
40
|
+
when :l then -> { xor_a(@registers.l) }
|
|
41
|
+
when :mem_hl then -> { xor_a(@cpu.bus_read(address: @registers.hl)) }
|
|
42
|
+
when :imm8 then -> { xor_a(@cpu.fetch_next_byte) }
|
|
43
|
+
else
|
|
44
|
+
raise ArgumentError, 'Unknown Xor source'
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def xor_a(value)
|
|
49
|
+
result = @registers.a ^ value
|
|
50
|
+
|
|
51
|
+
@registers.clear_flags
|
|
52
|
+
@registers.z_flag = result.nobits?(0xFF)
|
|
53
|
+
|
|
54
|
+
@registers.a = result
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|