mirror of
https://github.com/colby-swandale/waterfoul
synced 2025-01-14 08:01:51 +01:00
some code cleanup and documentation
This commit is contained in:
parent
9a1f2456ae
commit
932159d05f
2 changed files with 48 additions and 47 deletions
|
@ -58,10 +58,7 @@ module Waterfoul
|
||||||
# other cpu flags
|
# other cpu flags
|
||||||
attr_reader :ime, :stop
|
attr_reader :ime, :stop
|
||||||
|
|
||||||
##
|
# init CPU registers to 0
|
||||||
# Set the CPU to its initial state, the boot rom will then initialize
|
|
||||||
# the memory and registers to the approiate values before control is
|
|
||||||
# handed to the game cartridge
|
|
||||||
def initialize(options = {})
|
def initialize(options = {})
|
||||||
@pc = 0x0000
|
@pc = 0x0000
|
||||||
@sp = 0x0000
|
@sp = 0x0000
|
||||||
|
@ -69,49 +66,21 @@ module Waterfoul
|
||||||
@m = 0
|
@m = 0
|
||||||
end
|
end
|
||||||
|
|
||||||
##
|
|
||||||
# This method emulates the CPU cycle process. Each instruction is
|
# This method emulates the CPU cycle process. Each instruction is
|
||||||
# fetched from memory (pointed by the program counter). The value in memory is then
|
# fetched from memory (pointed by the program counter) and executed.
|
||||||
# matched against an instruction from the set of opcodes.
|
# This processes repeats infinitly until the process is closed
|
||||||
# (see instructions/opcode.rb) and executed. This processes repeats infinitly
|
|
||||||
# until the process is closed.
|
|
||||||
def step
|
def step
|
||||||
if halt?
|
reset_tick
|
||||||
|
if halted?
|
||||||
halt_step
|
halt_step
|
||||||
end
|
else
|
||||||
|
|
||||||
if !halt?
|
|
||||||
reset_switches
|
|
||||||
serve_interrupt if @ime
|
serve_interrupt if @ime
|
||||||
instruction_byte = fetch_instruction
|
instruction_byte = fetch_instruction
|
||||||
perform_instruction instruction_byte
|
perform_instruction instruction_byte
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def serve_interrupt
|
def halted?
|
||||||
interrupt = Interrupt.serve_interrupt
|
|
||||||
|
|
||||||
if interrupt > 0
|
|
||||||
@ime = false
|
|
||||||
push_onto_stack @pc
|
|
||||||
@m = 10
|
|
||||||
end
|
|
||||||
|
|
||||||
case interrupt
|
|
||||||
when Interrupt::INTERRUPT_VBLANK
|
|
||||||
@pc = 0x40
|
|
||||||
when Interrupt::INTERRUPT_LCDSTAT
|
|
||||||
@pc = 0x48
|
|
||||||
when Interrupt::INTERRUPT_TIMER
|
|
||||||
@pc = 0x50
|
|
||||||
when Interrupt::INTERRUPT_SERIAL
|
|
||||||
@pc = 0x58
|
|
||||||
when Interrupt::INTERRUPT_JOYPAD
|
|
||||||
@pc = 0x60
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def halt?
|
|
||||||
@halt == true
|
@halt == true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -130,28 +99,60 @@ module Waterfoul
|
||||||
@m = 2
|
@m = 2
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Execute the instruction and
|
||||||
def perform_instruction(instruction)
|
def perform_instruction(instruction)
|
||||||
operation = OPCODE[instruction]
|
operation = OPCODE[instruction]
|
||||||
|
raise 'instruction not found' if operation.nil?
|
||||||
# perform the instruction
|
# perform the instruction
|
||||||
self.public_send operation
|
self.public_send operation
|
||||||
set_instruction_timing instruction
|
@m = instruction_cycle_time instruction
|
||||||
end
|
end
|
||||||
|
|
||||||
def fetch_instruction
|
# fetch the next byte to be executed from memory and increment the program
|
||||||
|
# counter (except under particular circumstances, see interrupts)
|
||||||
|
def fetch_instruction(increment_pc = false)
|
||||||
instruction_byte = $mmu.read_byte @pc
|
instruction_byte = $mmu.read_byte @pc
|
||||||
@pc = (@pc + 1) & 0xFFFF
|
@pc = (@pc + 1) & 0xFFFF unless increment_pc
|
||||||
instruction_byte
|
instruction_byte
|
||||||
end
|
end
|
||||||
|
|
||||||
def set_instruction_timing(instruction)
|
private
|
||||||
|
|
||||||
|
# get the number of cycles a instruction takes to execute. The times
|
||||||
|
# can be found in the instruction opcode table
|
||||||
|
def instruction_cycle_time(instruction)
|
||||||
if @branched
|
if @branched
|
||||||
@m = OPCODE_CONDITIONAL_TIMINGS[instruction]
|
OPCODE_CONDITIONAL_TIMINGS[instruction]
|
||||||
else
|
else
|
||||||
@m = OPCODE_TIMINGS[instruction]
|
OPCODE_TIMINGS[instruction]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def reset_switches
|
def serve_interrupt
|
||||||
|
interrupt = Interrupt.serve_interrupt
|
||||||
|
# skip if there is no interrupt to serve
|
||||||
|
return if interrupt == Interrupt::INTERRUPT_NONE
|
||||||
|
# master disable interrupts
|
||||||
|
@ime = false
|
||||||
|
push_onto_stack @pc
|
||||||
|
@m = 10
|
||||||
|
# point to instruction which handles appropiate interrupt
|
||||||
|
case interrupt
|
||||||
|
when Interrupt::INTERRUPT_VBLANK
|
||||||
|
@pc = 0x40
|
||||||
|
when Interrupt::INTERRUPT_LCDSTAT
|
||||||
|
@pc = 0x48
|
||||||
|
when Interrupt::INTERRUPT_TIMER
|
||||||
|
@pc = 0x50
|
||||||
|
when Interrupt::INTERRUPT_SERIAL
|
||||||
|
@pc = 0x58
|
||||||
|
when Interrupt::INTERRUPT_JOYPAD
|
||||||
|
@pc = 0x60
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# reset variables that are set on every instruction
|
||||||
|
def reset_tick
|
||||||
@branched = false
|
@branched = false
|
||||||
@m = 0
|
@m = 0
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
module Waterfoul
|
module Waterfoul
|
||||||
# set the state of the emulator to the same state it is in after the boot rom is executed.
|
# Set the state of the emulator after it has finished running the bootloader and ready to execute
|
||||||
# This helps us run the game program without needing to execute the boot rom every time
|
# the game program. This lets us run the game program without needing to execute the boot rom every time
|
||||||
# the emulator is started.
|
# the emulator is started.
|
||||||
class SkipBoot
|
class SkipBoot
|
||||||
def self.set_state(cpu)
|
def self.set_state(cpu)
|
||||||
|
|
Loading…
Reference in a new issue