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,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
|