Refactor Config to allow temporary command-line overrides

This commit is contained in:
Matthew Berry 2022-01-11 17:45:32 -08:00
parent 5405b3978b
commit 4001dae0b8
11 changed files with 64 additions and 24 deletions

View file

@ -16,32 +16,41 @@ module Crab
extend self
def run
def run : NoReturn
config = Config.new
rom = nil
bios = nil
fifo = false
headless = false
OptionParser.parse do |parser|
parser.banner = "#{"crab".colorize.bold} - An accurate and readable Game Boy (Color) (Advance) emulator"
parser.separator
parser.separator("Usage: bin/crab [BIOS] ROM")
parser.separator("Usage: bin/crab [BIOS] [ROM]")
parser.separator
parser.separator("All command-line arguments serve as optional overrides to the config.")
parser.on("-h", "--help", "Show the help message") do
puts parser
exit
end
parser.on("--fifo", "Enable FIFO rendering") { fifo = true }
parser.on("--headless", "Don't open window or play audio") { headless = true }
parser.on("--run-bios", "Run the bios on startup") { config.args.run_bios = true }
parser.on("--skip-bios", "Skip the bios on startup") { config.args.run_bios = false }
parser.on("--fifo", "Enable per-pixel rendering for GBC") { config.args.fifo = true }
parser.on("--scanline", "Enable scanline rendering for GBC") { config.args.fifo = false }
parser.on("--headless", "Don't open window or play audio") { config.args.headless = true }
parser.unknown_args do |args|
case args.size
when 0 # launch the emulator without a rom selected
when 1 then rom = args[0]
when 2 then bios, rom = args[0], args[1]
else abort "Unknown args #{args[2..]}. Use '--help' for help"
end
end
parser.invalid_option { |flag| abort "Unknown flag '#{flag}'. Use '--help' for help" }
parser.missing_option { |flag| abort "Option '#{flag}' is missing a value. Use '--help' for help" }
end
frontend = Frontend.new(bios, rom, headless)
frontend = Frontend.new(config, bios, rom)
frontend.run
end
end

View file

@ -23,9 +23,19 @@ class Config
LibSDL::Keycode::R => Input::R,
}
property recents : Array(String) = [] of String
property run_bios : Bool = false
property gba : GBA = GBA.new
property gbc : GBC = GBC.new
private class Args
property headless : Bool = false
property run_bios : Bool?
property fifo : Bool?
end
@[YAML::Field(ignore: true)]
getter args = Args.new
def self.new : Config
Config.from_yaml(File.read(CONFIG_FILE))
end
@ -34,6 +44,14 @@ class Config
File.write(CONFIG_FILE, to_yaml)
end
def run_bios : Bool
unless (run_bios = args.run_bios).nil?
run_bios
else
@run_bios
end
end
class GBA
include YAML::Serializable
property bios : String = "bios.bin"
@ -45,6 +63,7 @@ class Config
class GBC
include YAML::Serializable
property bios : String?
property fifo : Bool = false
def initialize
end

View file

@ -1,7 +1,6 @@
abstract class Frontend
def self.new(bios : String?, rom : String?, headless = false)
config = Config.new
if headless
def self.new(config : Config, bios : String?, rom : String?)
if config.args.headless
HeadlessFrontend.new(config, bios, rom)
else
SDLOpenGLImGuiFrontend.new(config, bios, rom)
@ -10,7 +9,7 @@ abstract class Frontend
abstract def run : NoReturn
private def init_controller(bios : String?, rom : String?) : Controller
private def init_controller(bios : String?, rom : String?, skip_bios : Bool) : Controller
return StubbedController.new unless rom
extension = rom.rpartition('.')[2]
if GBController.extensions.includes? extension

View file

@ -1,5 +1,4 @@
class GBController < Controller
@config : Config
getter emu : GB::GB
class_getter extensions : Array(String) = ["gb", "gbc"]
class_getter vertex_shader : String = "identity.vert"
@ -8,8 +7,13 @@ class GBController < Controller
getter width : Int32 = 160
getter height : Int32 = 144
def initialize(@config : Config, bios : String?, rom : String)
@emu = GB::GB.new(bios || @config.gbc.bios, rom, true, false)
def initialize(config : Config, bios : String?, rom : String)
fifo = if config.args.fifo.nil?
config.gbc.fifo
else
config.args.fifo.not_nil!
end
@emu = GB::GB.new(bios || config.gbc.bios, rom, fifo, config.args.headless, config.run_bios)
@emu.post_init
end

View file

@ -1,5 +1,4 @@
class GBAController < Controller
@config : Config
getter emu : GBA::GBA
class_getter extensions : Array(String) = ["gba"]
class_getter vertex_shader : String = "identity.vert"
@ -11,8 +10,8 @@ class GBAController < Controller
@debug_window = false
@scheduler_window = false
def initialize(@config : Config, bios : String?, rom : String)
@emu = GBA::GBA.new(bios || @config.gba.bios, rom)
def initialize(config : Config, bios : String?, rom : String)
@emu = GBA::GBA.new(bios || config.gba.bios, rom, config.run_bios)
@emu.post_init
end

View file

@ -3,7 +3,7 @@ class HeadlessFrontend < Frontend
@controller : Controller
def initialize(@config : Config, bios : String?, rom : String?)
@controller = init_controller(bios, rom.not_nil!)
@controller = init_controller(bios, rom.not_nil!, @config.run_bios)
end
def run : NoReturn

View file

@ -44,7 +44,7 @@ class SDLOpenGLImGuiFrontend < Frontend
LibSDL.joystick_open 0
at_exit { SDL.quit }
@controller = init_controller(bios, rom)
@controller = init_controller(bios, rom, @config.run_bios)
@window = SDL::Window.new(
window_title(59.7),
@controller.window_width * @scale,
@ -118,7 +118,7 @@ class SDLOpenGLImGuiFrontend < Frontend
private def load_new_rom(rom : String?) : Nil
LibSDL.close_audio(1)
@controller = init_controller(nil, rom)
@controller = init_controller(nil, rom, @config.run_bios)
@file_explorer.clear_selection
@config.recents.delete(rom)

View file

@ -15,7 +15,8 @@ require "./timer"
module GB
class GB < Emu
getter bootrom : String?
getter cgb_ptr : Pointer(Bool) { pointerof(@cgb_enabled) }
@cgb_enabled : Bool
getter cgb_ptr : Pointer(Bool)
getter cartridge : Cartridge
getter! apu : APU
@ -27,9 +28,10 @@ module GB
getter! scheduler : Scheduler
getter! timer : Timer
def initialize(@bootrom : String?, rom_path : String, @fifo : Bool, @headless : Bool)
def initialize(@bootrom : String?, rom_path : String, @fifo : Bool, @headless : Bool, @run_bios : Bool)
@cartridge = Cartridge.new rom_path
@cgb_enabled = !(bootrom.nil? && @cartridge.cgb == Cartridge::CGB::NONE)
@cgb_enabled = (!bootrom.nil? && @run_bios) || @cartridge.cgb != Cartridge::CGB::NONE
@cgb_ptr = pointerof(@cgb_enabled)
end
def post_init : Nil
@ -41,7 +43,8 @@ module GB
@timer = Timer.new self
@memory = Memory.new self
@cpu = CPU.new self
skip_boot if @bootrom.nil?
skip_boot if @bootrom.nil? || !@run_bios
end
private def skip_boot : Nil

View file

@ -107,6 +107,7 @@ module GB
end
def skip_boot : Nil
@bootrom = Bytes.new 0
write_byte 0xFF10, 0x80_u8 # NR10
write_byte 0xFF11, 0xBF_u8 # NR11
write_byte 0xFF12, 0xF3_u8 # NR12

View file

@ -53,6 +53,10 @@ module GBA
property halted = false
def initialize(@gba : GBA)
clear_pipeline
end
def skip_bios : Nil
@reg_banks[Mode::USR.bank][5] = @r[13] = 0x03007F00
@reg_banks[Mode::IRQ.bank][5] = 0x03007FA0
@reg_banks[Mode::SVC.bank][5] = 0x03007FE0

View file

@ -30,7 +30,7 @@ module GBA
getter! dma : DMA
getter! debugger : Debugger
def initialize(@bios_path : String, @rom_path : String)
def initialize(@bios_path : String, @rom_path : String, @run_bios : Bool)
@scheduler = Scheduler.new
@cartridge = Cartridge.new @rom_path
scheduler.schedule 280896, ->handle_saves
@ -48,6 +48,8 @@ module GBA
@apu = APU.new self
@dma = DMA.new self
@debugger = Debugger.new self
cpu.skip_bios unless @run_bios
end
def run_until_frame : Nil