mirror of
https://github.com/mattrberry/crab.git
synced 2025-02-06 08:45:53 +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
|
else nil
|
||||||
end
|
end
|
||||||
@frame_sequencer_stage = 0 if (@frame_sequencer_stage += 1) > 7
|
@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
|
end
|
||||||
|
|
||||||
def get_sample : Nil
|
def get_sample : Nil
|
||||||
|
@ -122,7 +122,7 @@ module GB
|
||||||
@buffer_pos = 0
|
@buffer_pos = 0
|
||||||
end
|
end
|
||||||
|
|
||||||
@gb.scheduler.schedule SAMPLE_PERIOD, Scheduler::EventType::APU, ->get_sample
|
@gb.scheduler.schedule_gb SAMPLE_PERIOD, ->get_sample, Scheduler::EventType::APU
|
||||||
end
|
end
|
||||||
|
|
||||||
# read from apu memory
|
# read from apu memory
|
||||||
|
|
|
@ -41,7 +41,7 @@ module GB
|
||||||
end
|
end
|
||||||
|
|
||||||
def schedule_reload(frequency_timer : UInt32) : Nil
|
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
|
end
|
||||||
|
|
||||||
def sweep_step : Nil
|
def sweep_step : Nil
|
||||||
|
|
|
@ -31,7 +31,7 @@ module GB
|
||||||
end
|
end
|
||||||
|
|
||||||
def schedule_reload(frequency_timer : UInt32) : Nil
|
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
|
end
|
||||||
|
|
||||||
def get_amplitude : Float32
|
def get_amplitude : Float32
|
||||||
|
|
|
@ -32,7 +32,7 @@ module GB
|
||||||
end
|
end
|
||||||
|
|
||||||
def schedule_reload(frequency_timer : UInt32) : Nil
|
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
|
end
|
||||||
|
|
||||||
def get_amplitude : Float32
|
def get_amplitude : Float32
|
||||||
|
|
|
@ -31,7 +31,7 @@ module GB
|
||||||
end
|
end
|
||||||
|
|
||||||
def schedule_reload(frequency_timer : UInt32) : Nil
|
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
|
end
|
||||||
|
|
||||||
def get_amplitude : Float32
|
def get_amplitude : Float32
|
||||||
|
|
|
@ -11,7 +11,6 @@ require "./opcodes"
|
||||||
require "./ppu"
|
require "./ppu"
|
||||||
require "./scanline_ppu"
|
require "./scanline_ppu"
|
||||||
require "./fifo_ppu"
|
require "./fifo_ppu"
|
||||||
require "./scheduler"
|
|
||||||
require "./timer"
|
require "./timer"
|
||||||
|
|
||||||
DISPLAY_SCALE = {% unless flag? :graphics_test %} 4 {% else %} 1 {% end %}
|
DISPLAY_SCALE = {% unless flag? :graphics_test %} 4 {% else %} 1 {% end %}
|
||||||
|
@ -71,7 +70,7 @@ module GB
|
||||||
else nil
|
else nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
scheduler.schedule 70224, Scheduler::EventType::HandleInput, ->handle_events
|
scheduler.schedule_gb 70224, ->handle_events, Scheduler::EventType::HandleInput
|
||||||
end
|
end
|
||||||
|
|
||||||
def run : Nil
|
def run : Nil
|
||||||
|
|
|
@ -2123,7 +2123,7 @@ module GB
|
||||||
# 0xFB EI
|
# 0xFB EI
|
||||||
->(cpu : CPU) {
|
->(cpu : CPU) {
|
||||||
cpu.inc_pc
|
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
|
return 4
|
||||||
},
|
},
|
||||||
# 0xFC UNUSED
|
# 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"
|
require "json"
|
||||||
|
|
||||||
OPCODE_JSON_URL = "https://raw.githubusercontent.com/izik1/gbops/master/dmgops.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 GB
|
||||||
module DmgOps
|
module DmgOps
|
||||||
|
@ -306,7 +306,7 @@ module GB
|
||||||
when "DI"
|
when "DI"
|
||||||
["cpu.ime = false"]
|
["cpu.ime = false"]
|
||||||
when "EI"
|
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"
|
when "HALT"
|
||||||
["cpu.halt"]
|
["cpu.halt"]
|
||||||
when "INC"
|
when "INC"
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
require "./types"
|
require "./types"
|
||||||
require "./reg"
|
require "./reg"
|
||||||
require "./scheduler"
|
|
||||||
require "./cartridge"
|
require "./cartridge"
|
||||||
require "./storage"
|
require "./storage"
|
||||||
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