Class: Amaterasu::GameBoy::Cpu

Inherits:
Object
  • Object
show all
Defined in:
lib/amaterasu/game_boy/cpu.rb,
lib/amaterasu/game_boy/cpu/registers.rb,
lib/amaterasu/game_boy/cpu/instructions.rb,
lib/amaterasu/game_boy/cpu/instructions/cp.rb,
lib/amaterasu/game_boy/cpu/instructions/di.rb,
lib/amaterasu/game_boy/cpu/instructions/ei.rb,
lib/amaterasu/game_boy/cpu/instructions/jp.rb,
lib/amaterasu/game_boy/cpu/instructions/jr.rb,
lib/amaterasu/game_boy/cpu/instructions/or.rb,
lib/amaterasu/game_boy/cpu/instructions/adc.rb,
lib/amaterasu/game_boy/cpu/instructions/and.rb,
lib/amaterasu/game_boy/cpu/instructions/daa.rb,
lib/amaterasu/game_boy/cpu/instructions/dec.rb,
lib/amaterasu/game_boy/cpu/instructions/inc.rb,
lib/amaterasu/game_boy/cpu/instructions/ld8.rb,
lib/amaterasu/game_boy/cpu/instructions/ldh.rb,
lib/amaterasu/game_boy/cpu/instructions/nop.rb,
lib/amaterasu/game_boy/cpu/instructions/pop.rb,
lib/amaterasu/game_boy/cpu/instructions/ret.rb,
lib/amaterasu/game_boy/cpu/instructions/rst.rb,
lib/amaterasu/game_boy/cpu/instructions/sbc.rb,
lib/amaterasu/game_boy/cpu/instructions/sub.rb,
lib/amaterasu/game_boy/cpu/instructions/xor.rb,
lib/amaterasu/game_boy/cpu/instructions/add8.rb,
lib/amaterasu/game_boy/cpu/instructions/base.rb,
lib/amaterasu/game_boy/cpu/instructions/call.rb,
lib/amaterasu/game_boy/cpu/instructions/halt.rb,
lib/amaterasu/game_boy/cpu/instructions/ld16.rb,
lib/amaterasu/game_boy/cpu/instructions/misc.rb,
lib/amaterasu/game_boy/cpu/instructions/push.rb,
lib/amaterasu/game_boy/cpu/instructions/stop.rb,
lib/amaterasu/game_boy/cpu/instructions/add16.rb,
lib/amaterasu/game_boy/cpu/instructions/cb_rl.rb,
lib/amaterasu/game_boy/cpu/instructions/cb_rr.rb,
lib/amaterasu/game_boy/cpu/instructions/cb_bit.rb,
lib/amaterasu/game_boy/cpu/instructions/cb_res.rb,
lib/amaterasu/game_boy/cpu/instructions/cb_rlc.rb,
lib/amaterasu/game_boy/cpu/instructions/cb_rrc.rb,
lib/amaterasu/game_boy/cpu/instructions/cb_set.rb,
lib/amaterasu/game_boy/cpu/instructions/cb_sla.rb,
lib/amaterasu/game_boy/cpu/instructions/cb_sra.rb,
lib/amaterasu/game_boy/cpu/instructions/cb_srl.rb,
lib/amaterasu/game_boy/cpu/instructions/rotate.rb,
lib/amaterasu/game_boy/cpu/instructions/cb_swap.rb

Overview

Models the CPU behavior from the Game Boy.

Defined Under Namespace

Modules: Instructions Classes: Registers

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(bus, hram, interrupts, advance_cycle, trace_cpu: false) ⇒ Cpu

Returns a new instance of Cpu.



9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# File 'lib/amaterasu/game_boy/cpu.rb', line 9

def initialize(bus, hram, interrupts, advance_cycle, trace_cpu: false)
  @bus = bus
  @hram = hram
  @interrupts = interrupts
  @advance_cycle = advance_cycle
  @trace_cpu = trace_cpu

  @registers = Registers.new
  @ime = false
  @ime_scheduled = false
  @halted = false
  @opcode = nil
  @instruction = nil

  @m_cycles = 0

  @instructions = Instructions.load_base_instructions(cpu: self)
  @cb_instructions = Instructions.load_cb_instructions(cpu: self)
end

Instance Attribute Details

#m_cyclesObject (readonly)

Returns the value of attribute m_cycles.



7
8
9
# File 'lib/amaterasu/game_boy/cpu.rb', line 7

def m_cycles
  @m_cycles
end

#registersObject (readonly)

Returns the value of attribute registers.



7
8
9
# File 'lib/amaterasu/game_boy/cpu.rb', line 7

def registers
  @registers
end

Instance Method Details

#add16(value1, value2) ⇒ Object

Performs an addition envolving a 16-bit value, CPU consumes an additional cycle to handle 16-bit values.



135
136
137
138
139
140
# File 'lib/amaterasu/game_boy/cpu.rb', line 135

def add16(value1, value2)
  result = value1 + value2
  internal_processing

  result
end

#bus_read(address:) ⇒ Object

Reads a byte from the Bus at a given address.



64
65
66
67
68
69
# File 'lib/amaterasu/game_boy/cpu.rb', line 64

def bus_read(address:)
  byte = @bus.read_byte(address:, caller: self)
  @m_cycles = @advance_cycle.call

  byte
end

#bus_write(address:, value:) ⇒ Object

Requests a Bus write at a given address with a given value.



72
73
74
75
# File 'lib/amaterasu/game_boy/cpu.rb', line 72

def bus_write(address:, value:)
  @bus.write_byte(address:, value:, caller: self)
  @m_cycles = @advance_cycle.call
end

#disable_interruptsObject

Used by the DI instruction.



152
153
154
# File 'lib/amaterasu/game_boy/cpu.rb', line 152

def disable_interrupts
  @ime = false
end

#enable_interruptsObject

Used by the EI instruction.



157
158
159
# File 'lib/amaterasu/game_boy/cpu.rb', line 157

def enable_interrupts
  @ime_scheduled = true
end

#fetch_next_byteObject

Fetches the next immediate byte from memory pointed to by the Program Counter. Every time a byte is fetched, the PC is incremented by 1.



79
80
81
82
83
84
# File 'lib/amaterasu/game_boy/cpu.rb', line 79

def fetch_next_byte
  byte = bus_read(address: @registers.pc)
  @registers.pc += 1

  byte
end

#fetch_next_wordObject

Fetches the next 2 immediate bytes from memory.

  • The Game Boy uses little endian format.

  • This means that the first byte fetched is the least significant one.

  • So if the memory has these next 2 bytes: $50 $01, the word is: $0150



91
92
93
94
95
96
# File 'lib/amaterasu/game_boy/cpu.rb', line 91

def fetch_next_word
  lsb = fetch_next_byte
  msb = fetch_next_byte

  (msb << 8) | lsb
end

#haltObject



161
162
163
# File 'lib/amaterasu/game_boy/cpu.rb', line 161

def halt
  @halted = true
end

#internal_processingObject

Emulates CPU internal processing which advance cycles without Bus access.



166
167
168
# File 'lib/amaterasu/game_boy/cpu.rb', line 166

def internal_processing
  @m_cycles = @advance_cycle.call
end

#jump_to(address:) ⇒ Object

Jumps execution to a given address by setting the address value into the PC.

Parameters:

  • address (Integer)

    16-bit memory address value.



121
122
123
124
# File 'lib/amaterasu/game_boy/cpu.rb', line 121

def jump_to(address:)
  @registers.pc = address
  internal_processing
end

#sign_value(byte) ⇒ Object

Converts an unsigned byte into a value between -128 to 127 to use as an offset.

Parameters:

  • byte (Integer)

    8-bit unsigned value.



129
130
131
# File 'lib/amaterasu/game_boy/cpu.rb', line 129

def sign_value(byte)
  byte >= 128 ? (byte - 256) : byte
end

#stack_popObject

Pops a 16-bit value from the Stack.



109
110
111
112
113
114
115
116
# File 'lib/amaterasu/game_boy/cpu.rb', line 109

def stack_pop
  lsb = bus_read(address: @registers.sp)
  @registers.sp += 1
  msb = bus_read(address: @registers.sp)
  @registers.sp += 1

  (msb << 8) | lsb
end

#stack_push(value:) ⇒ Object

Pushes a 16-bit value into the Stack.

Parameters:

  • value (Integer)

    16-bit value to be stored in the Stack.



101
102
103
104
105
106
# File 'lib/amaterasu/game_boy/cpu.rb', line 101

def stack_push(value:)
  @registers.sp -= 1
  bus_write(address: @registers.sp, value: (value >> 8) & 0xFF)
  @registers.sp -= 1
  bus_write(address: @registers.sp, value: value & 0xFF)
end

#stepObject

Core CPU loop:

  • Checks IME and any interrupts pending to be serviced.

  • Fetches the current Opcode at the Program Counter.

  • Decodes which instruction based on the Opcode fetched.

  • Executes the instruction.



35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
# File 'lib/amaterasu/game_boy/cpu.rb', line 35

def step
  if @interrupts.any_pending?
    @halted = false if @halted

    if @ime
      handle_interrupts
      return
    end
  end

  if @ime_scheduled
    @ime = true
    @ime_scheduled = false
  end

  if @halted
    @m_cycles = @advance_cycle.call
    return
  end

  old_pc = @registers.pc
  old_cycles = @m_cycles
  @opcode = fetch_next_byte
  decode_instruction
  execute_instruction
  log_state(old_pc, old_cycles, @instruction)
end

#sub16(value1, value2) ⇒ Object

Performs a subtraction envolving a 16-bit value, CPU consumes an additional cycle to handle 16-bit values.



144
145
146
147
148
149
# File 'lib/amaterasu/game_boy/cpu.rb', line 144

def sub16(value1, value2)
  result = value1 - value2
  internal_processing

  result
end