mirror of
https://github.com/colby-swandale/waterfoul
synced 2024-12-26 21:58:54 +01:00
documentation updats
This commit is contained in:
parent
7ae56c04ed
commit
5ee093760d
7 changed files with 33 additions and 48 deletions
|
@ -51,7 +51,6 @@ module Waterfoul
|
|||
|
||||
|
||||
# Read bootstrap instruction given an index (memory location)
|
||||
# @return Integer - instruction or immediate value
|
||||
def self.[](i)
|
||||
ROM[i]
|
||||
end
|
||||
|
|
|
@ -9,8 +9,8 @@ require 'waterfoul/instructions/shift'
|
|||
require 'waterfoul/instructions/prefix'
|
||||
|
||||
module Waterfoul
|
||||
# These constants represent each state in the F register and are used as a helper to
|
||||
# reference the state when setting/resetting a state bit. Any combination of these
|
||||
# These constants represent status bit in the F register. These are used as a
|
||||
# helper when setting/resetting a state bit. Any combination of these
|
||||
# states can be set at any one time.
|
||||
#
|
||||
# Z_FLAG: Zero Flag
|
||||
|
@ -18,25 +18,20 @@ module Waterfoul
|
|||
# H_FLAG: half carry flag
|
||||
# C_FLAG: Carry Flag
|
||||
# BIT 0-3 Always 0 and not used
|
||||
#
|
||||
Z_FLAG = 0b1000_0000
|
||||
N_FLAG = 0b0100_0000
|
||||
H_FLAG = 0b0010_0000
|
||||
C_FLAG = 0b0001_0000
|
||||
|
||||
# number of cycles a HALT will puase program execution for
|
||||
HALT_CYCLES = 6
|
||||
|
||||
##
|
||||
# The CPU emulates the Sharp LR35902 CPU that is built into the device, similar to the
|
||||
# Intel 8080 and Zilog Z80 processor. Each instruction is categorized
|
||||
# into a subset of instructions by the type of action performed by the instruction.
|
||||
# The CPU emulates the Sharp LR35902 CPU that is built into the device,
|
||||
# similar to the Intel 8080 and Zilog Z80 processor. Each instruction
|
||||
# is categorized into a subset of instructions by the type of action
|
||||
# performed by the instruction.
|
||||
#
|
||||
# See lib/instuctions/ for the implementation for the CPU instruction set.
|
||||
# See lib/instuctions for the implementation for the CPU instruction set.
|
||||
#
|
||||
# I recommend looking at http://www.pastraiser.com/cpu/gameboy/gameboy_opcodes.html for an
|
||||
# easy to understand chart for each instruction.
|
||||
#
|
||||
class CPU
|
||||
include Helper
|
||||
include Instructions::Opcode
|
||||
|
@ -51,7 +46,7 @@ module Waterfoul
|
|||
|
||||
# 8 bit registers
|
||||
attr_reader :a, :b, :c, :d, :e, :f, :h, :l, :f
|
||||
# 8 CPU clock
|
||||
# CPU cycle count
|
||||
attr_reader :m
|
||||
# 16 bit registers
|
||||
attr_reader :sp, :pc
|
||||
|
@ -89,7 +84,7 @@ module Waterfoul
|
|||
end
|
||||
|
||||
def halted?
|
||||
@halt == true
|
||||
@halt
|
||||
end
|
||||
|
||||
# Execute the instruction and
|
||||
|
@ -103,7 +98,7 @@ module Waterfoul
|
|||
|
||||
# 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)
|
||||
def fetch_instruction(no_increment_pc = false)
|
||||
instruction_byte = $mmu.read_byte @pc
|
||||
@pc = (@pc + 1) & 0xFFFF unless increment_pc
|
||||
instruction_byte
|
||||
|
|
|
@ -1,22 +1,18 @@
|
|||
require 'sdl2'
|
||||
|
||||
module Waterfoul
|
||||
# The Emulator is the abstraction of the emulator as a whole, it initializes
|
||||
# each component and performs the tick.
|
||||
class Emulator
|
||||
def initialize(rom_filename, options = {})
|
||||
SDL2.init SDL2::INIT_EVERYTHING
|
||||
# read the rom into host memory
|
||||
rom = read_program(rom_filename).bytes
|
||||
# initialize emulated CPU, GPU & Sound components
|
||||
# read the given file as binary and break it down into an array of bytes
|
||||
rom = File.binread(rom_filename).bytes
|
||||
# initialize emulated CPU, GPU & Scren components
|
||||
cartridge = Cartridge.new rom
|
||||
# initialize emulated memory management unit
|
||||
$mmu = MMU.new
|
||||
$mmu.cartridge = cartridge
|
||||
cpu = CPU.new
|
||||
@cpu = options.has_key?('skip_boot') ? SkipBoot.set_state(cpu) : cpu
|
||||
@cpu = CPU.new
|
||||
@cpu = SkipBoot.set_state(@cpu) if options.has_key?('skip_boot')
|
||||
@gpu = GPU.new
|
||||
# @input = Input.new
|
||||
@screen = Screen.new
|
||||
# @sound = Sound.new
|
||||
end
|
||||
|
||||
def run
|
||||
|
@ -25,14 +21,7 @@ module Waterfoul
|
|||
@gpu.step @cpu.m
|
||||
@screen.render @gpu.framebuffer if @gpu.vblank?
|
||||
# @input.step @cpu.m
|
||||
# @sound.step
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def read_program(rom)
|
||||
File.binread rom
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
module Waterfoul
|
||||
module Helper
|
||||
|
||||
def pop_from_stack(word = true)
|
||||
if word
|
||||
lower = $mmu.read_byte @sp
|
||||
|
|
|
@ -12,6 +12,7 @@ module Waterfoul
|
|||
INTERRUPT_SERIAL = 0x8
|
||||
INTERRUPT_JOYPAD = 0x10
|
||||
|
||||
#
|
||||
def self.request_interrupt(interrupt)
|
||||
if_reg = $mmu.read_byte IF_REG_MEM_LOC
|
||||
$mmu.write_byte IF_REG_MEM_LOC, (if_reg | interrupt)
|
||||
|
|
|
@ -7,12 +7,11 @@ module Waterfoul
|
|||
# does not implement any IO instructions.
|
||||
class MMU
|
||||
MEMORY_SIZE = 65536 # bytes
|
||||
# location in memory that when written to will unmap the boot rom from
|
||||
# memory
|
||||
# unmap boot rom register address
|
||||
UNMAP_BOOT_ROM_MEM_LOC = 0xFF50
|
||||
# location in memory where the boot rom ends
|
||||
BOOT_ROM_END_MEM_LOC = 0xFF
|
||||
# location in memory where DMA transfer is init
|
||||
# DMA register function address
|
||||
DMA_TRANSFER_MEM_LOC = 0xFF46
|
||||
# DIV register memory location
|
||||
DIV_MEM_LOC = 0xFF04
|
||||
|
@ -23,19 +22,19 @@ module Waterfoul
|
|||
# Set the initial state the memory management unit when program starts
|
||||
def initialize
|
||||
@cartridge = []
|
||||
# flag to indicate if the boot rom is mapped to memory
|
||||
# map the boot rom by default
|
||||
@map_boot_rom = true
|
||||
# storage for usable memory (zero filled)
|
||||
@memory = Array.new MEMORY_SIZE, 0
|
||||
end
|
||||
|
||||
# Read 1 byte from memory given address
|
||||
# @param i Integer location in memory to read value
|
||||
def [](i)
|
||||
raise MemoryOutOfBounds if i > MEMORY_SIZE || i < 0
|
||||
|
||||
case i
|
||||
when 0x0000...0x8000 # ROM Bank 0 + n
|
||||
# if the boot rom is enabled and the address is < 0x100
|
||||
if @map_boot_rom && i <= BOOT_ROM_END_MEM_LOC
|
||||
BootROM[i]
|
||||
else
|
||||
|
@ -54,14 +53,12 @@ module Waterfoul
|
|||
end
|
||||
end
|
||||
|
||||
##
|
||||
# Storage 1 byte into memory given address
|
||||
# @param i Integer location in memory to storage value
|
||||
# @param v Integer value to be written into memory
|
||||
# Write 1 byte into memory
|
||||
def []=(i, v, options = {})
|
||||
# raise exception if an attempt is made to read memory that is out of bounds
|
||||
raise MemoryOutOfBounds if i > MEMORY_SIZE || i < 0
|
||||
|
||||
# ignore memory rules if emulated hardware components need to write to
|
||||
# memory
|
||||
unless options[:hardware_operation]
|
||||
case i
|
||||
when UNMAP_BOOT_ROM_MEM_LOC # unmap the boot rom when 0xFF50 is wrtiten to in memory
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
module Waterfoul
|
||||
# Set the state of the emulator after it has finished running the bootloader and ready to execute
|
||||
# the game program. This lets us run the game program without needing to execute the boot rom every time
|
||||
# the emulator is started.
|
||||
# Set the state of the emulator as defined to the same state as it typically
|
||||
# is after it has finished running the bootloader and ready to execute the
|
||||
# game program.
|
||||
|
||||
# This lets us run the game without needing to execute the boot rom
|
||||
# every time the emulator is started.
|
||||
class SkipBoot
|
||||
def self.set_state(cpu)
|
||||
# CPU registers
|
||||
|
|
Loading…
Reference in a new issue