mirror of
https://github.com/mattrberry/crab.git
synced 2025-01-29 20:35:13 +01:00
abstract scheduler between gb and gba
This commit is contained in:
parent
26d747801d
commit
fceb5ee988
12 changed files with 107 additions and 175 deletions
97
src/crab/common/scheduler.cr
Normal file
97
src/crab/common/scheduler.cr
Normal 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
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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"
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
require "./types"
|
||||
require "./reg"
|
||||
require "./scheduler"
|
||||
require "./cartridge"
|
||||
require "./storage"
|
||||
require "./storage/*"
|
||||
|
|
|
@ -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
|
Loading…
Add table
Reference in a new issue