scheduler, ppu vcount/dispstat on scheduler, ppu io fix read/write order

This commit is contained in:
Matthew Berry 2020-10-09 21:01:04 -07:00
parent 71235e4b2a
commit 4a2f0a6789
4 changed files with 94 additions and 30 deletions

View file

@ -1,5 +1,6 @@
require "./types"
require "./util"
require "./scheduler"
require "./cartridge"
require "./mmio"
require "./bus"
@ -9,6 +10,7 @@ require "./display"
require "./ppu"
class GBA
getter scheduler : Scheduler
getter cartridge : Cartridge
getter mmio : MMIO { MMIO.new self }
getter bus : Bus { Bus.new self }
@ -17,10 +19,10 @@ class GBA
getter display : Display { Display.new }
getter ppu : PPU { PPU.new self }
@cycles = 0
def initialize(rom_path : String)
@scheduler = Scheduler.new
@cartridge = Cartridge.new rom_path
handle_events
SDL.init(SDL::Init::VIDEO | SDL::Init::AUDIO | SDL::Init::JOYSTICK)
LibSDL.joystick_open 0
@ -28,6 +30,7 @@ class GBA
end
def handle_events : Nil
@scheduler.schedule PPU::REFRESH, ->handle_events
while event = SDL::Event.poll
case event
when SDL::Event::Quit then exit 0
@ -46,11 +49,6 @@ class GBA
end
def tick(cycles : Int) : Nil
ppu.tick cycles
@cycles += cycles
if @cycles >= PPU::REFRESH
handle_events
@cycles -= PPU::REFRESH
end
@scheduler.tick cycles
end
end

View file

@ -5,10 +5,10 @@ class Interrupts
def read_io(io_addr : Int) : Byte
case io_addr
when 0x200 then (@reg_ie >> 8).to_u8
when 0x201 then @reg_ie.to_u8!
when 0x202 then (@reg_if >> 8).to_u8
when 0x203 then @reg_if.to_u8!
when 0x200 then 0xFF_u8 & @reg_ie
when 0x201 then 0xFF_u8 & @reg_ie >> 8
when 0x202 then 0xFF_u8 & @reg_if
when 0x203 then 0xFF_u8 & @reg_if >> 8
when 0x208 then @reg_ime ? 1_u8 : 0_u8
when 0x209 then @reg_ime ? 1_u8 : 0_u8
else raise "Unimplemented interrupts read ~ addr:#{hex_str io_addr.to_u8}"
@ -17,10 +17,10 @@ class Interrupts
def write_io(io_addr : Int, value : Byte) : Nil
case io_addr
when 0x200 then @reg_ie = (@reg_ie & 0x00FF) | value.to_u16 << 8
when 0x201 then @reg_ie = (@reg_ie & 0xFF00) | value
when 0x202 then @reg_ie = (@reg_if & 0x00FF) | value.to_u16 << 8
when 0x203 then @reg_ie = (@reg_if & 0xFF00) | value
when 0x200 then @reg_ie = (@reg_ie & 0xFF00) | value
when 0x201 then @reg_ie = (@reg_ie & 0x00FF) | value.to_u16 << 8
when 0x202 then @reg_ie = (@reg_if & 0xFF00) | value
when 0x203 then @reg_ie = (@reg_if & 0x00FF) | value.to_u16 << 8
when 0x208 then @reg_ime = bit?(value, 0)
when 0x209 then @reg_ime = bit?(value, 0)
else raise "Unimplemented interrupts write ~ addr:#{hex_str io_addr.to_u8!}, val:#{value}"

View file

@ -41,38 +41,61 @@ class PPU
getter dispcnt : DISPCNT = DISPCNT.new 0
getter dispstat : DISPSTAT = DISPSTAT.new 0
@cycles = 0
getter vcount : UInt16 = 0x0000_u16
def initialize(@gba : GBA)
start_scanline
end
def tick(cycles : Int) : Nil
@cycles += cycles
if @cycles >= REFRESH
def start_scanline : Nil
@gba.scheduler.schedule 960, ->start_hblank
end
def start_hblank : Nil
@gba.scheduler.schedule 272, ->end_hblank
@dispstat.hblank = true
end
def end_hblank : Nil
@dispstat.hblank = false
@vcount += 1
if @vcount == 228
@vcount = 0
@dispstat.vblank = false
@gba.scheduler.schedule 0, ->start_scanline
elsif @vcount == 160
@dispstat.vblank = true
@gba.scheduler.schedule 0, ->start_vblank_line
@gba.display.draw @vram
@cycles -= REFRESH
elsif @vcount >= 160
@gba.scheduler.schedule 0, ->start_vblank_line
else
@gba.scheduler.schedule 0, ->start_scanline
end
end
def start_vblank_line : Nil
@gba.scheduler.schedule 960, ->start_hblank
end
def read_io(io_addr : Int) : Byte
case io_addr
when 0x000 then 0xFF_u8 & @dispcnt.value >> 8
when 0x001 then 0xFF_u8 & @dispcnt.value
when 0x004 then 0xFF_u8 & @dispstat.value >> 8
when 0x005 then 0xFF_u8 & @dispstat.value
when 0x000 then 0xFF_u8 & @dispcnt.value
when 0x001 then 0xFF_u8 & @dispcnt.value >> 8
when 0x004 then 0xFF_u8 & @dispstat.value
when 0x005 then 0xFF_u8 & @dispstat.value >> 8
else raise "Unimplemented PPU read ~ addr:#{hex_str io_addr.to_u8}"
end
end
def write_io(io_addr : Int, value : Byte) : Nil
case io_addr
when 0x000 then @dispcnt.value = (@dispcnt.value & 0x00FF) | value.to_u16 << 8
when 0x001 then @dispcnt.value = (@dispcnt.value & 0xFF00) | value
when 0x000 then @dispcnt.value = (@dispcnt.value & 0xFF00) | value
when 0x001 then @dispcnt.value = (@dispcnt.value & 0x00FF) | value.to_u16 << 8
when 0x002 # undocumented - green swap
when 0x003 # undocumented - green swap
when 0x004 then @dispstat.value = (@dispstat.value & 0x00FF) | value.to_u16 << 8
when 0x005 then @dispstat.value = (@dispstat.value & 0xFF00) | value
when 0x004 then @dispstat.value = (@dispstat.value & 0xFF00) | value
when 0x005 then @dispstat.value = (@dispstat.value & 0x00FF) | value.to_u16 << 8
else raise "Unimplemented PPU write ~ addr:#{hex_str io_addr.to_u8}, val:#{value}"
end
end

43
src/crab/scheduler.cr Normal file
View file

@ -0,0 +1,43 @@
class Scheduler
private record Event, cycles : UInt64, proc : Proc(Void)
@events : Deque(Event) = Deque(Event).new 10
@cycles : UInt64 = 0
def schedule(cycles : Int, proc : Proc(Void)) : Nil
self << Event.new @cycles + cycles, proc
end
def schedule(cycles : Int, &block : ->)
self << Event.new @cycles + cycles, block
end
@[AlwaysInline]
def <<(event : Event) : Nil
idx = @events.bsearch_index { |e| e.cycles > event.cycles }
unless idx.nil?
@events.insert(idx, event)
else
@events << event
end
end
def tick(cycles : Int) : Nil
cycles.times do
@cycles += 1
call_current
end
end
def call_current : Nil
loop do
event = @events.first?
if event && @cycles >= event.cycles
event.proc.call
@events.shift
else
break
end
end
end
end