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