mirror of
https://github.com/mattrberry/crab.git
synced 2025-01-29 20:35:13 +01:00
scheduler, ppu vcount/dispstat on scheduler, ppu io fix read/write order
This commit is contained in:
parent
71235e4b2a
commit
4a2f0a6789
4 changed files with 94 additions and 30 deletions
|
@ -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
|
||||
|
|
|
@ -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}"
|
||||
|
|
|
@ -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
43
src/crab/scheduler.cr
Normal 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
|
Loading…
Add table
Reference in a new issue