abstract scheduler between gb and gba

This commit is contained in:
Matthew Berry 2021-05-07 02:28:19 -07:00
parent 26d747801d
commit fceb5ee988
12 changed files with 107 additions and 175 deletions

View file

@ -0,0 +1,97 @@
class Scheduler
enum EventType
# Shared
DEFAULT
APU
APUChannel1
APUChannel2
APUChannel3
APUChannel4
HandleInput
# GB
IME
# GBA
Timer0
Timer1
Timer2
Timer3
end
private record Event, cycles : UInt64, proc : Proc(Nil), type : EventType
@events : Deque(Event) = Deque(Event).new 10
getter cycles : UInt64 = 0
@next_event : UInt64 = UInt64::MAX
@current_speed : UInt8 = 0
def schedule(cycles : Int, proc : Proc(Nil), type = EventType::DEFAULT) : Nil
self << Event.new @cycles + cycles, proc, type
end
def schedule_gb(cycles : Int, proc : Proc(Nil), type : EventType) : Nil
cycles = cycles << @current_speed unless type == EventType::IME
schedule(cycles, proc, type)
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
@next_event = @events[0].cycles
end
def clear(type : EventType) : Nil
@events.reject! { |event| event.type == type }
end
def tick(cycles : Int) : Nil
if @cycles + cycles < @next_event
@cycles += cycles
else
cycles.times do
@cycles += 1
call_current
end
end
end
def call_current : Nil
loop do
event = @events.first?
if event && @cycles >= event.cycles
event.proc.call
@events.shift
else
if event
@next_event = event.cycles
else
@next_event = UInt64::MAX
end
return
end
end
end
def fast_forward : Nil
@cycles = @next_event
call_current
end
# Set the CGB current speed to 1x (0) or 2x (1)
def speed_mode=(speed : UInt8) : Nil
@current_speed = speed
@events.each_with_index do |event, idx|
unless event.type == EventType::IME
remaining_cycles = event.cycles - @cycles
# divide by two if entering single speed, else multiply by two
offset = remaining_cycles >> (@current_speed - speed)
@events[idx] = event.copy_with cycles: @cycles + offset
end
end
end
end

View file

@ -92,7 +92,7 @@ module GB
else nil
end
@frame_sequencer_stage = 0 if (@frame_sequencer_stage += 1) > 7
@gb.scheduler.schedule FRAME_SEQUENCER_PERIOD, Scheduler::EventType::APU, ->tick_frame_sequencer
@gb.scheduler.schedule_gb FRAME_SEQUENCER_PERIOD, ->tick_frame_sequencer, Scheduler::EventType::APU
end
def get_sample : Nil
@ -122,7 +122,7 @@ module GB
@buffer_pos = 0
end
@gb.scheduler.schedule SAMPLE_PERIOD, Scheduler::EventType::APU, ->get_sample
@gb.scheduler.schedule_gb SAMPLE_PERIOD, ->get_sample, Scheduler::EventType::APU
end
# read from apu memory

View file

@ -41,7 +41,7 @@ module GB
end
def schedule_reload(frequency_timer : UInt32) : Nil
@gb.scheduler.schedule frequency_timer, Scheduler::EventType::APUChannel1, ->step
@gb.scheduler.schedule_gb frequency_timer, ->step, Scheduler::EventType::APUChannel1
end
def sweep_step : Nil

View file

@ -31,7 +31,7 @@ module GB
end
def schedule_reload(frequency_timer : UInt32) : Nil
@gb.scheduler.schedule frequency_timer, Scheduler::EventType::APUChannel2, ->step
@gb.scheduler.schedule_gb frequency_timer, ->step, Scheduler::EventType::APUChannel2
end
def get_amplitude : Float32

View file

@ -32,7 +32,7 @@ module GB
end
def schedule_reload(frequency_timer : UInt32) : Nil
@gb.scheduler.schedule frequency_timer, Scheduler::EventType::APUChannel3, ->step
@gb.scheduler.schedule_gb frequency_timer, ->step, Scheduler::EventType::APUChannel3
end
def get_amplitude : Float32

View file

@ -31,7 +31,7 @@ module GB
end
def schedule_reload(frequency_timer : UInt32) : Nil
@gb.scheduler.schedule frequency_timer, Scheduler::EventType::APUChannel4, ->step
@gb.scheduler.schedule_gb frequency_timer, ->step, Scheduler::EventType::APUChannel4
end
def get_amplitude : Float32

View file

@ -11,7 +11,6 @@ require "./opcodes"
require "./ppu"
require "./scanline_ppu"
require "./fifo_ppu"
require "./scheduler"
require "./timer"
DISPLAY_SCALE = {% unless flag? :graphics_test %} 4 {% else %} 1 {% end %}
@ -71,7 +70,7 @@ module GB
else nil
end
end
scheduler.schedule 70224, Scheduler::EventType::HandleInput, ->handle_events
scheduler.schedule_gb 70224, ->handle_events, Scheduler::EventType::HandleInput
end
def run : Nil

View file

@ -2123,7 +2123,7 @@ module GB
# 0xFB EI
->(cpu : CPU) {
cpu.inc_pc
cpu.scheduler.schedule(4, Scheduler::EventType::IME) { cpu.ime = true }
cpu.scheduler.schedule_gb(4, Proc(Nil).new { cpu.ime = true }, Scheduler::EventType::IME)
return 4
},
# 0xFC UNUSED

View file

@ -1,90 +0,0 @@
module GB
class Scheduler
enum EventType
APU
APUChannel1
APUChannel2
APUChannel3
APUChannel4
IME
HandleInput
end
private record Event, cycles : UInt64, type : EventType, proc : Proc(Void) do
def to_s(io)
io << "Event(cycles: #{cycles}, type: #{type}, proc: #{proc})"
end
end
@events : Deque(Event) = Deque(Event).new 10
@cycles : UInt64 = 0
@next_event : UInt64 = UInt64::MAX
@current_speed : UInt8 = 0
def schedule(cycles : Int, type : EventType, proc : Proc(Void)) : Nil
cycles = cycles << @current_speed unless type == EventType::IME
self << Event.new @cycles + cycles, type, proc
end
def schedule(cycles : Int, type : EventType, &block : ->)
cycles = cycles << @current_speed unless type == EventType::IME
self << Event.new @cycles + cycles, type, block
end
def clear(type : EventType) : Nil
@events.reject! { |event| event.type == type }
end
# Set the current speed to 1x (0) or 2x (1)
def speed_mode=(speed : UInt8) : Nil
@current_speed = speed
@events.each_with_index do |event, idx|
unless event.type == EventType::IME
remaining_cycles = event.cycles - @cycles
# divide by two if entering single speed, else multiply by two
offset = remaining_cycles >> (@current_speed - speed)
@events[idx] = event.copy_with cycles: @cycles + offset
end
end
end
def <<(event : Event) : Nil
idx = @events.bsearch_index { |e, i| e.cycles > event.cycles }
unless idx.nil?
@events.insert(idx, event)
else
@events << event
end
@next_event = @events[0].cycles
end
def tick(cycles : Int) : Nil
if @cycles + cycles < @next_event
@cycles += cycles
else
cycles.times do
@cycles += 1
call_current
end
end
end
def call_current : Nil
loop do
event = @events.first?
if event && @cycles >= event.cycles
event.proc.call
@events.shift
else
if event
@next_event = event.cycles
else
@next_event = UInt64::MAX
end
return
end
end
end
end
end

View file

@ -4,7 +4,7 @@ require "http/client"
require "json"
OPCODE_JSON_URL = "https://raw.githubusercontent.com/izik1/gbops/master/dmgops.json"
FILE_PATH = "src/cryboy/opcodes.cr"
FILE_PATH = "src/crab/gb/opcodes.cr"
module GB
module DmgOps
@ -306,7 +306,7 @@ module GB
when "DI"
["cpu.ime = false"]
when "EI"
["cpu.scheduler.schedule(4, Scheduler::EventType::IME) { cpu.ime = true }"]
["cpu.scheduler.schedule_gb(4, Proc(Nil).new { cpu.ime = true }, Scheduler::EventType::IME)"]
when "HALT"
["cpu.halt"]
when "INC"

View file

@ -1,6 +1,5 @@
require "./types"
require "./reg"
require "./scheduler"
require "./cartridge"
require "./storage"
require "./storage/*"

View file

@ -1,73 +0,0 @@
module GBA
class Scheduler
enum EventType
DEFAULT
APUChannel1
APUChannel2
APUChannel3
APUChannel4
Timer0
Timer1
Timer2
Timer3
end
private record Event, cycles : UInt64, proc : Proc(Void), type : EventType
@events : Deque(Event) = Deque(Event).new 10
getter cycles : UInt64 = 0
@next_event : UInt64 = UInt64::MAX
def schedule(cycles : Int, proc : Proc(Void), type = EventType::DEFAULT) : Nil
self << Event.new @cycles + cycles, proc, type
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
@next_event = @events[0].cycles
end
def clear(type : EventType) : Nil
@events.reject! { |event| event.type == type }
end
def tick(cycles : Int) : Nil
if @cycles + cycles < @next_event
@cycles += cycles
else
cycles.times do
@cycles += 1
call_current
end
end
end
def call_current : Nil
loop do
event = @events.first?
if event && @cycles >= event.cycles
event.proc.call
@events.shift
else
if event
@next_event = event.cycles
else
@next_event = UInt64::MAX
end
return
end
end
end
def fast_forward : Nil
@cycles = @next_event
call_current
end
end
end