Class: Amaterasu::GameBoy::Cpu
- Inherits:
-
Object
- Object
- Amaterasu::GameBoy::Cpu
- 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
-
#m_cycles ⇒ Object
readonly
Returns the value of attribute m_cycles.
-
#registers ⇒ Object
readonly
Returns the value of attribute registers.
Instance Method Summary collapse
-
#add16(value1, value2) ⇒ Object
Performs an addition envolving a 16-bit value, CPU consumes an additional cycle to handle 16-bit values.
-
#bus_read(address:) ⇒ Object
Reads a byte from the Bus at a given address.
-
#bus_write(address:, value:) ⇒ Object
Requests a Bus write at a given address with a given value.
-
#disable_interrupts ⇒ Object
Used by the DI instruction.
-
#enable_interrupts ⇒ Object
Used by the EI instruction.
-
#fetch_next_byte ⇒ Object
Fetches the next immediate byte from memory pointed to by the Program Counter.
-
#fetch_next_word ⇒ Object
Fetches the next 2 immediate bytes from memory.
- #halt ⇒ Object
-
#initialize(bus, hram, interrupts, advance_cycle, trace_cpu: false) ⇒ Cpu
constructor
A new instance of Cpu.
-
#internal_processing ⇒ Object
Emulates CPU internal processing which advance cycles without Bus access.
-
#jump_to(address:) ⇒ Object
Jumps execution to a given address by setting the address value into the PC.
-
#sign_value(byte) ⇒ Object
Converts an unsigned byte into a value between -128 to 127 to use as an offset.
-
#stack_pop ⇒ Object
Pops a 16-bit value from the Stack.
-
#stack_push(value:) ⇒ Object
Pushes a 16-bit value into the Stack.
-
#step ⇒ Object
Core CPU loop:.
-
#sub16(value1, value2) ⇒ Object
Performs a subtraction envolving a 16-bit value, CPU consumes an additional cycle to handle 16-bit values.
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_cycles ⇒ Object (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 |
#registers ⇒ Object (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_interrupts ⇒ Object
Used by the DI instruction.
152 153 154 |
# File 'lib/amaterasu/game_boy/cpu.rb', line 152 def disable_interrupts @ime = false end |
#enable_interrupts ⇒ Object
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_byte ⇒ Object
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_word ⇒ Object
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 |
#halt ⇒ Object
161 162 163 |
# File 'lib/amaterasu/game_boy/cpu.rb', line 161 def halt @halted = true end |
#internal_processing ⇒ Object
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.
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.
129 130 131 |
# File 'lib/amaterasu/game_boy/cpu.rb', line 129 def sign_value(byte) byte >= 128 ? (byte - 256) : byte end |
#stack_pop ⇒ Object
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.
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 |
#step ⇒ Object
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 |