From fceb5ee988a4d17199c9f660f66ebd338b7f9d89 Mon Sep 17 00:00:00 2001 From: Matthew Berry Date: Fri, 7 May 2021 02:28:19 -0700 Subject: [PATCH] abstract scheduler between gb and gba --- src/crab/common/scheduler.cr | 97 +++++++++++++++++++++++++++++++++++ src/crab/gb/apu.cr | 4 +- src/crab/gb/audio/channel1.cr | 2 +- src/crab/gb/audio/channel2.cr | 2 +- src/crab/gb/audio/channel3.cr | 2 +- src/crab/gb/audio/channel4.cr | 2 +- src/crab/gb/gb.cr | 3 +- src/crab/gb/opcodes.cr | 2 +- src/crab/gb/scheduler.cr | 90 -------------------------------- src/crab/gb/update_opcodes.cr | 4 +- src/crab/gba/gba.cr | 1 - src/crab/gba/scheduler.cr | 73 -------------------------- 12 files changed, 107 insertions(+), 175 deletions(-) create mode 100644 src/crab/common/scheduler.cr delete mode 100644 src/crab/gb/scheduler.cr delete mode 100644 src/crab/gba/scheduler.cr diff --git a/src/crab/common/scheduler.cr b/src/crab/common/scheduler.cr new file mode 100644 index 0000000..b7f2821 --- /dev/null +++ b/src/crab/common/scheduler.cr @@ -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 diff --git a/src/crab/gb/apu.cr b/src/crab/gb/apu.cr index f493b28..22d06d2 100644 --- a/src/crab/gb/apu.cr +++ b/src/crab/gb/apu.cr @@ -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 diff --git a/src/crab/gb/audio/channel1.cr b/src/crab/gb/audio/channel1.cr index 26c2761..936e6d6 100644 --- a/src/crab/gb/audio/channel1.cr +++ b/src/crab/gb/audio/channel1.cr @@ -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 diff --git a/src/crab/gb/audio/channel2.cr b/src/crab/gb/audio/channel2.cr index 9cd4e82..f7fe480 100644 --- a/src/crab/gb/audio/channel2.cr +++ b/src/crab/gb/audio/channel2.cr @@ -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 diff --git a/src/crab/gb/audio/channel3.cr b/src/crab/gb/audio/channel3.cr index e7ec883..456b0e7 100644 --- a/src/crab/gb/audio/channel3.cr +++ b/src/crab/gb/audio/channel3.cr @@ -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 diff --git a/src/crab/gb/audio/channel4.cr b/src/crab/gb/audio/channel4.cr index 6d2e193..a27cf69 100644 --- a/src/crab/gb/audio/channel4.cr +++ b/src/crab/gb/audio/channel4.cr @@ -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 diff --git a/src/crab/gb/gb.cr b/src/crab/gb/gb.cr index 99add86..72640b5 100644 --- a/src/crab/gb/gb.cr +++ b/src/crab/gb/gb.cr @@ -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 diff --git a/src/crab/gb/opcodes.cr b/src/crab/gb/opcodes.cr index 8edba0d..5512ba8 100644 --- a/src/crab/gb/opcodes.cr +++ b/src/crab/gb/opcodes.cr @@ -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 diff --git a/src/crab/gb/scheduler.cr b/src/crab/gb/scheduler.cr deleted file mode 100644 index 0ec4f51..0000000 --- a/src/crab/gb/scheduler.cr +++ /dev/null @@ -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 diff --git a/src/crab/gb/update_opcodes.cr b/src/crab/gb/update_opcodes.cr index f1af171..960d348 100644 --- a/src/crab/gb/update_opcodes.cr +++ b/src/crab/gb/update_opcodes.cr @@ -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" diff --git a/src/crab/gba/gba.cr b/src/crab/gba/gba.cr index e13a801..133b1e1 100644 --- a/src/crab/gba/gba.cr +++ b/src/crab/gba/gba.cr @@ -1,6 +1,5 @@ require "./types" require "./reg" -require "./scheduler" require "./cartridge" require "./storage" require "./storage/*" diff --git a/src/crab/gba/scheduler.cr b/src/crab/gba/scheduler.cr deleted file mode 100644 index ae22f48..0000000 --- a/src/crab/gba/scheduler.cr +++ /dev/null @@ -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