Continue working on DMA sound.

Cleanup timer.rs
run cargo fmt
restore debugging continue&frame commands
Fix bug introduced in previous commit causing the bios animation to
hang


Former-commit-id: 188acaa1121503a97f2d3be816f6f57835e17fe1
This commit is contained in:
Michel Heily 2019-12-21 20:19:43 +02:00
parent 70cb99161d
commit fefeddbc40
10 changed files with 185 additions and 159 deletions

View file

@ -57,6 +57,8 @@ pub struct Core {
pub verbose: bool, pub verbose: bool,
pub trace_opcodes: bool, pub trace_opcodes: bool,
pub trace_exceptions: bool,
} }
pub type CpuExecResult = CpuResult<()>; pub type CpuExecResult = CpuResult<()>;

View file

@ -30,7 +30,7 @@ impl Core {
Irq => (CpuMode::Irq, true, false), Irq => (CpuMode::Irq, true, false),
Fiq => (CpuMode::Fiq, true, true), Fiq => (CpuMode::Fiq, true, true),
}; };
if self.verbose { if self.trace_exceptions {
println!( println!(
"{}: {:?}, pc: {:#x}, new_mode: {:?} old_mode: {:?}", "{}: {:?}, pc: {:#x}, new_mode: {:?} old_mode: {:?}",
"Exception".cyan(), "Exception".cyan(),

View file

@ -1,6 +1,7 @@
use std::collections::VecDeque; use std::collections::VecDeque;
use super::arm7tdmi::{Addr, Bus}; use super::arm7tdmi::{Addr, Bus};
use super::iodev::consts::{REG_FIFO_A, REG_FIFO_B};
use super::sysbus::SysBus; use super::sysbus::SysBus;
use super::{Interrupt, IrqBitmask}; use super::{Interrupt, IrqBitmask};
@ -99,7 +100,10 @@ impl DmaChannel {
self.internal.src_addr = self.src; self.internal.src_addr = self.src;
self.internal.dst_addr = self.dst; self.internal.dst_addr = self.dst;
self.internal.count = self.wc; self.internal.count = self.wc;
self.fifo_mode = timing == 3 && (self.id == 0 || self.id == 1); self.fifo_mode = timing == 3
&& ctrl.repeat()
&& (self.id == 1 || self.id == 2)
&& (self.dst == REG_FIFO_A || self.dst == REG_FIFO_B);
} }
self.ctrl = ctrl; self.ctrl = ctrl;
return start_immediately; return start_immediately;
@ -133,11 +137,10 @@ impl DmaChannel {
let fifo_mode = self.fifo_mode; let fifo_mode = self.fifo_mode;
if fifo_mode { if fifo_mode {
println!("FIFO Tranfer"); for _ in 0..4 {
for _ in 0..count { let v = sb.read_32(self.internal.src_addr);
let v = sb.read_16(self.internal.src_addr); sb.write_32(self.internal.dst_addr, v);
sb.write_16(self.internal.dst_addr, v); self.internal.src_addr += 4;
self.internal.src_addr += 2;
} }
} else if word_size == 4 { } else if word_size == 4 {
for _ in 0..count { for _ in 0..count {

View file

@ -8,9 +8,6 @@ use super::gpu::*;
use super::interrupt::*; use super::interrupt::*;
use super::iodev::*; use super::iodev::*;
use super::sysbus::SysBus; use super::sysbus::SysBus;
use super::timer::TimerEvent;
use super::SyncedIoDevice;
use super::super::{AudioInterface, InputInterface, VideoInterface}; use super::super::{AudioInterface, InputInterface, VideoInterface};
@ -51,8 +48,13 @@ where
} }
} }
pub fn frame(&mut self) { #[inline]
pub fn key_poll(&mut self) {
self.sysbus.io.keyinput = self.input_device.borrow_mut().poll(); self.sysbus.io.keyinput = self.input_device.borrow_mut().poll();
}
pub fn frame(&mut self) {
self.key_poll();
while self.sysbus.io.gpu.state != GpuState::VBlank { while self.sysbus.io.gpu.state != GpuState::VBlank {
self.step(); self.step();
} }
@ -114,7 +116,7 @@ where
0 0
}; };
io.timers.tick(cycles, &mut self.sysbus, &mut irqs); io.timers.step(cycles, &mut self.sysbus, &mut irqs);
if let Some(new_gpu_state) = io.gpu.step(cycles, &mut self.sysbus, &mut irqs) { if let Some(new_gpu_state) = io.gpu.step(cycles, &mut self.sysbus, &mut irqs) {
match new_gpu_state { match new_gpu_state {

View file

@ -28,7 +28,6 @@ pub struct IoDevices {
pub post_boot_flag: bool, pub post_boot_flag: bool,
pub waitcnt: WaitControl, // TODO also implement 4000800 pub waitcnt: WaitControl, // TODO also implement 4000800
pub haltcnt: HaltState, pub haltcnt: HaltState,
pub sound_bias: u16,
mem: BoxedMemory, mem: BoxedMemory,
} }
@ -46,7 +45,6 @@ impl IoDevices {
haltcnt: HaltState::Running, haltcnt: HaltState::Running,
keyinput: keypad::KEYINPUT_ALL_RELEASED, keyinput: keypad::KEYINPUT_ALL_RELEASED,
waitcnt: WaitControl(0), waitcnt: WaitControl(0),
sound_bias: 0x200,
} }
} }
} }
@ -87,30 +85,20 @@ impl Bus for IoDevices {
REG_IE => io.intc.interrupt_enable.0 as u16, REG_IE => io.intc.interrupt_enable.0 as u16,
REG_IF => io.intc.interrupt_flags.0 as u16, REG_IF => io.intc.interrupt_flags.0 as u16,
REG_TM0CNT_L => io.timers[0].timer_data, REG_TM0CNT_L..=REG_TM3CNT_H => io.timers.handle_read(io_addr),
REG_TM0CNT_H => io.timers[0].timer_ctl.0,
REG_TM1CNT_L => io.timers[1].timer_data,
REG_TM1CNT_H => io.timers[1].timer_ctl.0,
REG_TM2CNT_L => io.timers[2].timer_data,
REG_TM2CNT_H => io.timers[2].timer_ctl.0,
REG_TM3CNT_L => io.timers[3].timer_data,
REG_TM3CNT_H => io.timers[3].timer_ctl.0,
REG_SOUND1CNT_L..DMA_BASE => io.sound.handle_read(io_addr),
REG_DMA0CNT_H => io.dmac.channels[0].ctrl.0, REG_DMA0CNT_H => io.dmac.channels[0].ctrl.0,
REG_DMA1CNT_H => io.dmac.channels[1].ctrl.0, REG_DMA1CNT_H => io.dmac.channels[1].ctrl.0,
REG_DMA2CNT_H => io.dmac.channels[2].ctrl.0, REG_DMA2CNT_H => io.dmac.channels[2].ctrl.0,
REG_DMA3CNT_H => io.dmac.channels[3].ctrl.0, REG_DMA3CNT_H => io.dmac.channels[3].ctrl.0,
REG_SOUNDBIAS => io.sound_bias,
REG_WAITCNT => io.waitcnt.0, REG_WAITCNT => io.waitcnt.0,
REG_POSTFLG => io.post_boot_flag as u16, REG_POSTFLG => io.post_boot_flag as u16,
REG_HALTCNT => 0, REG_HALTCNT => 0,
REG_KEYINPUT => io.keyinput as u16, REG_KEYINPUT => io.keyinput as u16,
REG_SOUND1CNT_L..=DMA_BASE => io.sound.handle_read(io_addr),
_ => { _ => {
// println!( // println!(
// "Unimplemented read from {:x} {}", // "Unimplemented read from {:x} {}",
@ -216,29 +204,11 @@ impl Bus for IoDevices {
REG_IE => io.intc.interrupt_enable.0 = value, REG_IE => io.intc.interrupt_enable.0 = value,
REG_IF => io.intc.interrupt_flags.0 &= !value, REG_IF => io.intc.interrupt_flags.0 &= !value,
REG_TM0CNT_L => { REG_TM0CNT_L..=REG_TM3CNT_H => io.timers.handle_write(io_addr, value),
io.timers[0].timer_data = value;
io.timers[0].initial_data = value;
}
REG_TM0CNT_H => io.timers.write_timer_ctl(0, value),
REG_TM1CNT_L => { REG_SOUND1CNT_L..DMA_BASE => {
io.timers[1].timer_data = value; io.sound.handle_write(io_addr, value);
io.timers[1].initial_data = value;
} }
REG_TM1CNT_H => io.timers.write_timer_ctl(1, value),
REG_TM2CNT_L => {
io.timers[2].timer_data = value;
io.timers[2].initial_data = value;
}
REG_TM2CNT_H => io.timers.write_timer_ctl(2, value),
REG_TM3CNT_L => {
io.timers[3].timer_data = value;
io.timers[3].initial_data = value;
}
REG_TM3CNT_H => io.timers.write_timer_ctl(3, value),
DMA_BASE..=REG_DMA3CNT_H => { DMA_BASE..=REG_DMA3CNT_H => {
let ofs = io_addr - DMA_BASE; let ofs = io_addr - DMA_BASE;
@ -246,8 +216,6 @@ impl Bus for IoDevices {
io.dmac.write_16(channel_id, ofs % 12, value) io.dmac.write_16(channel_id, ofs % 12, value)
} }
REG_SOUNDBIAS => io.sound_bias = value & 0xc3fe,
REG_WAITCNT => io.waitcnt.0 = value, REG_WAITCNT => io.waitcnt.0 = value,
REG_POSTFLG => io.post_boot_flag = value != 0, REG_POSTFLG => io.post_boot_flag = value != 0,
@ -260,9 +228,6 @@ impl Bus for IoDevices {
} }
} }
REG_SOUND1CNT_L..=DMA_BASE => {
io.sound.handle_write(io_addr, value);
}
_ => { _ => {
// println!( // println!(
// "Unimplemented write to {:x} {}", // "Unimplemented write to {:x} {}",

View file

@ -36,6 +36,12 @@ impl Default for DmaSound {
} }
} }
const REG_FIFO_A_L: u32 = REG_FIFO_A;
const REG_FIFO_A_H: u32 = REG_FIFO_A + 2;
const REG_FIFO_B_L: u32 = REG_FIFO_B;
const REG_FIFO_B_H: u32 = REG_FIFO_B + 2;
#[derive(Debug)] #[derive(Debug)]
pub struct SoundController { pub struct SoundController {
sample_rate_to_cpu_freq: usize, // how many "cycles" are a sample? sample_rate_to_cpu_freq: usize, // how many "cycles" are a sample?
@ -66,6 +72,8 @@ pub struct SoundController {
sqr1_initial_vol: usize, sqr1_initial_vol: usize,
sqr1_cur_vol: usize, sqr1_cur_vol: usize,
sound_bias: u16,
sound_a: DmaSound, sound_a: DmaSound,
sound_b: DmaSound, sound_b: DmaSound,
} }
@ -95,6 +103,7 @@ impl SoundController {
sqr1_step_increase: false, sqr1_step_increase: false,
sqr1_initial_vol: 0, sqr1_initial_vol: 0,
sqr1_cur_vol: 0, sqr1_cur_vol: 0,
sound_bias: 0x200,
sound_a: Default::default(), sound_a: Default::default(),
sound_b: Default::default(), sound_b: Default::default(),
} }
@ -137,6 +146,8 @@ impl SoundController {
| cbit(14, self.sound_b.timer_select != 0) | cbit(14, self.sound_b.timer_select != 0)
} }
REG_SOUNDBIAS => self.sound_bias,
_ => { _ => {
println!( println!(
"Unimplemented read from {:x} {}", "Unimplemented read from {:x} {}",
@ -174,7 +185,7 @@ impl SoundController {
} }
if !self.mse { if !self.mse {
println!("MSE disabled, refusing to write"); // println!("MSE disabled, refusing to write");
return; return;
} }
@ -227,16 +238,18 @@ impl SoundController {
} }
} }
REG_FIFO_A => { REG_FIFO_A_L | REG_FIFO_A_H => {
self.sound_a.fifo.write((value & 0xff00 >> 8) as i8); self.sound_a.fifo.write(((value >> 8) & 0xff) as i8);
self.sound_a.fifo.write((value & 0xff) as i8); self.sound_a.fifo.write((value & 0xff) as i8);
} }
REG_FIFO_B => { REG_FIFO_B_L | REG_FIFO_B_H => {
self.sound_b.fifo.write((value & 0xff00 >> 8) as i8); self.sound_b.fifo.write(((value >> 8) & 0xff) as i8);
self.sound_b.fifo.write((value & 0xff) as i8); self.sound_b.fifo.write((value & 0xff) as i8);
} }
REG_SOUNDBIAS => self.sound_bias = value & 0xc3fe,
_ => { _ => {
println!( println!(
"Unimplemented write to {:x} {}", "Unimplemented write to {:x} {}",

View file

@ -1,29 +1,23 @@
use super::interrupt::{Interrupt, IrqBitmask}; use super::interrupt::{Interrupt, IrqBitmask};
use super::iodev::consts::*;
use super::sysbus::SysBus; use super::sysbus::SysBus;
use super::SyncedIoDevice;
use num::FromPrimitive; use num::FromPrimitive;
#[derive(Debug, Default)] #[derive(Debug)]
pub struct Timer { pub struct Timer {
// registers // registers
pub timer_ctl: TimerCtl, pub ctl: TimerCtl,
pub timer_data: u16, pub data: u16,
irq: Interrupt,
timer_id: usize, timer_id: usize,
reg: u32,
target: u32,
fired: bool,
pub initial_data: u16, pub initial_data: u16,
pub cycles: usize, pub cycles: usize,
} }
pub enum TimerEvent {
Overflow(usize, usize),
Increment(usize),
}
impl Timer { impl Timer {
pub fn new(timer_id: usize) -> Timer { pub fn new(timer_id: usize) -> Timer {
if timer_id > 3 { if timer_id > 3 {
@ -31,16 +25,16 @@ impl Timer {
} }
Timer { Timer {
timer_id: timer_id, timer_id: timer_id,
..Timer::default() irq: Interrupt::from_usize(timer_id + 8).unwrap(),
data: 0,
ctl: TimerCtl(0),
initial_data: 0,
cycles: 0,
} }
} }
fn get_irq(&self) -> Interrupt {
Interrupt::from_usize(self.timer_id + 8).unwrap()
}
fn frequency(&self) -> usize { fn frequency(&self) -> usize {
match self.timer_ctl.prescalar() { match self.ctl.prescalar() {
0 => 1, 0 => 1,
1 => 64, 1 => 64,
2 => 256, 2 => 256,
@ -48,29 +42,6 @@ impl Timer {
_ => unreachable!(), _ => unreachable!(),
} }
} }
pub fn add_cycles(&mut self, cycles: usize, irqs: &mut IrqBitmask) -> TimerEvent {
let mut num_overflows = 0;
self.cycles += cycles;
let frequency = self.frequency();
while self.cycles >= frequency {
self.cycles -= frequency;
self.timer_data = self.timer_data.wrapping_add(1);
if self.timer_data == 0 {
if self.timer_ctl.irq_enabled() {
irqs.add_irq(self.get_irq());
}
self.timer_data = self.initial_data;
num_overflows += 1;
}
}
if num_overflows > 0 {
return TimerEvent::Overflow(self.timer_id, num_overflows);
} else {
return TimerEvent::Increment(self.timer_id);
}
}
} }
#[derive(Debug)] #[derive(Debug)]
@ -96,14 +67,14 @@ impl Timers {
pub fn new() -> Timers { pub fn new() -> Timers {
Timers { Timers {
timers: [Timer::new(0), Timer::new(1), Timer::new(2), Timer::new(3)], timers: [Timer::new(0), Timer::new(1), Timer::new(2), Timer::new(3)],
trace: false, trace: true,
} }
} }
pub fn write_timer_ctl(&mut self, id: usize, value: u16) { pub fn write_timer_ctl(&mut self, id: usize, value: u16) {
let old_enabled = self[id].timer_ctl.enabled(); let old_enabled = self[id].ctl.enabled();
self[id].timer_ctl.0 = value; self[id].ctl.0 = value;
let new_enabled = self[id].timer_ctl.enabled(); let new_enabled = self[id].ctl.enabled();
if self.trace && old_enabled != new_enabled { if self.trace && old_enabled != new_enabled {
println!( println!(
"TMR{} {}", "TMR{} {}",
@ -113,36 +84,88 @@ impl Timers {
} }
} }
pub fn tick( pub fn handle_read(&self, io_addr: u32) -> u16 {
&mut self, match io_addr {
cycles: usize, REG_TM0CNT_L => self.timers[0].data,
sb: &mut SysBus, REG_TM0CNT_H => self.timers[0].ctl.0,
irqs: &mut IrqBitmask, REG_TM1CNT_L => self.timers[1].data,
) -> Option<TimerEvent> { REG_TM1CNT_H => self.timers[1].ctl.0,
for i in 0..4 { REG_TM2CNT_L => self.timers[2].data,
if self[i].timer_ctl.enabled() && !self[i].timer_ctl.cascade() { REG_TM2CNT_H => self.timers[2].ctl.0,
let event = self[i].add_cycles(cycles, irqs); REG_TM3CNT_L => self.timers[3].data,
match event { REG_TM3CNT_H => self.timers[3].ctl.0,
TimerEvent::Overflow(_, num_overflows) => { _ => unreachable!(),
if self.trace { }
println!("TMR{} overflown!", i); }
}
if i != 3 { pub fn handle_write(&mut self, io_addr: u32, value: u16) {
let next_i = i + 1; match io_addr {
if self[next_i].timer_ctl.cascade() { REG_TM0CNT_L => {
self[next_i].add_cycles(num_overflows, irqs); self.timers[0].data = value;
} self.timers[0].initial_data = value;
} }
if i == 0 || i == 1 { REG_TM0CNT_H => self.write_timer_ctl(0, value),
sb.io.sound.handle_timer_overflow(&mut sb.io.dmac, i);
} REG_TM1CNT_L => {
} self.timers[1].data = value;
_ => {} self.timers[1].initial_data = value;
}
REG_TM1CNT_H => self.write_timer_ctl(1, value),
REG_TM2CNT_L => {
self.timers[2].data = value;
self.timers[2].initial_data = value;
}
REG_TM2CNT_H => self.write_timer_ctl(2, value),
REG_TM3CNT_L => {
self.timers[3].data = value;
self.timers[3].initial_data = value;
}
REG_TM3CNT_H => self.write_timer_ctl(3, value),
_ => unreachable!(),
}
}
fn update_timer(&mut self, id: usize, cycles: usize, sb: &mut SysBus, irqs: &mut IrqBitmask) {
let timer = &mut self.timers[id];
timer.cycles += cycles;
let mut num_overflows = 0;
let freq = timer.frequency();
while timer.cycles >= freq {
timer.cycles -= freq;
timer.data = timer.data.wrapping_add(1);
if timer.data == 0 {
if self.trace {
println!("TMR{} overflown!", id);
} }
return Some(event); if timer.ctl.irq_enabled() {
irqs.add_irq(timer.irq);
}
timer.data = timer.initial_data;
num_overflows += 1;
}
}
if num_overflows > 0 {
if id != 3 {
let next_timer = &mut self.timers[id + 1];
if next_timer.ctl.cascade() {
self.update_timer(id + 1, num_overflows, sb, irqs);
}
}
if id == 0 || id == 1 {
sb.io.sound.handle_timer_overflow(&mut sb.io.dmac, id);
}
}
}
pub fn step(&mut self, cycles: usize, sb: &mut SysBus, irqs: &mut IrqBitmask) {
for i in 0..4 {
if self.timers[i].ctl.enabled() && !self.timers[i].ctl.cascade() {
self.update_timer(i, cycles, sb, irqs);
} }
} }
None
} }
} }

View file

@ -38,6 +38,7 @@ bitflags! {
const TRACE_OPCODE = 0b00000010; const TRACE_OPCODE = 0b00000010;
const TRACE_DMA = 0b00000100; const TRACE_DMA = 0b00000100;
const TRACE_TIMERS = 0b000001000; const TRACE_TIMERS = 0b000001000;
const TRACE_EXCEPTIONS = 0b000001000;
} }
} }
@ -108,31 +109,30 @@ where
} }
println!("{}\n", self.gba.cpu); println!("{}\n", self.gba.cpu);
} }
// Continue => { Continue => {
// self.ctrlc_flag.store(true, Ordering::SeqCst); self.ctrlc_flag.store(true, Ordering::SeqCst);
// while self.ctrlc_flag.load(Ordering::SeqCst) { while self.ctrlc_flag.load(Ordering::SeqCst) {
// let start_time = time::Instant::now(); self.gba.key_poll();
// self.gba.update_key_state(); match self.gba.check_breakpoint() {
// match self.gba.check_breakpoint() { Some(addr) => {
// Some(addr) => { println!("Breakpoint reached! @{:x}", addr);
// println!("Breakpoint reached! @{:x}", addr); break;
// break; }
// } _ => {
// _ => { self.gba.step();
// self.gba.step(); }
// } }
// } }
// } }
// } Frame(count) => {
// Frame(count) => { use super::time::PreciseTime;
// use super::time::PreciseTime; let start = PreciseTime::now();
// let start = PreciseTime::now(); for _ in 0..count {
// for _ in 0..count { self.gba.frame();
// self.gba.frame(); }
// } let end = PreciseTime::now();
// let end = PreciseTime::now(); println!("that took {} seconds", start.to(end));
// println!("that took {} seconds", start.to(end)); }
// }
HexDump(addr, nbytes) => { HexDump(addr, nbytes) => {
let bytes = self.gba.sysbus.get_bytes(addr..addr + nbytes); let bytes = self.gba.sysbus.get_bytes(addr..addr + nbytes);
hexdump::hexdump(&bytes); hexdump::hexdump(&bytes);
@ -205,6 +205,17 @@ where
} }
) )
} }
if flags.contains(TraceFlags::TRACE_EXCEPTIONS) {
self.gba.cpu.trace_exceptions = !self.gba.cpu.trace_exceptions;
println!(
"[*] exception tracing {}",
if self.gba.cpu.trace_exceptions {
"on"
} else {
"off"
}
)
}
if flags.contains(TraceFlags::TRACE_DMA) { if flags.contains(TraceFlags::TRACE_DMA) {
println!("[*] dma tracing not implemented"); println!("[*] dma tracing not implemented");
} }
@ -420,7 +431,7 @@ where
"r" | "reset" => Ok(Command::Reset), "r" | "reset" => Ok(Command::Reset),
"trace" => { "trace" => {
let usage = DebuggerError::InvalidCommandFormat(String::from( let usage = DebuggerError::InvalidCommandFormat(String::from(
"trace [sysbus|opcode|dma|all]", "trace [sysbus|opcode|dma|all|exceptions]",
)); ));
if args.len() != 1 { if args.len() != 1 {
Err(usage) Err(usage)
@ -429,6 +440,7 @@ where
let flags = match flag_str.as_ref() { let flags = match flag_str.as_ref() {
"sysbus" => TraceFlags::TRACE_SYSBUS, "sysbus" => TraceFlags::TRACE_SYSBUS,
"opcode" => TraceFlags::TRACE_OPCODE, "opcode" => TraceFlags::TRACE_OPCODE,
"exceptions" => TraceFlags::TRACE_EXCEPTIONS,
"dma" => TraceFlags::TRACE_DMA, "dma" => TraceFlags::TRACE_DMA,
"timers" => TraceFlags::TRACE_TIMERS, "timers" => TraceFlags::TRACE_TIMERS,
"all" => TraceFlags::all(), "all" => TraceFlags::all(),

View file

@ -1,5 +1,6 @@
#![feature(asm)] #![feature(asm)]
#![feature(core_intrinsics)] #![feature(core_intrinsics)]
#![feature(exclusive_range_pattern)]
#[macro_use] #[macro_use]
extern crate enum_primitive_derive; extern crate enum_primitive_derive;

View file

@ -11,9 +11,9 @@ extern crate clap;
#[macro_use] #[macro_use]
extern crate rustboyadvance_ng; extern crate rustboyadvance_ng;
use rustboyadvance_ng::core::keypad;
use rustboyadvance_ng::prelude::*; use rustboyadvance_ng::prelude::*;
use rustboyadvance_ng::util::FpsCounter; use rustboyadvance_ng::util::FpsCounter;
use rustboyadvance_ng::core::keypad;
extern crate bit; extern crate bit;
use bit::BitIndex; use bit::BitIndex;
@ -80,7 +80,6 @@ impl AudioInterface for MiniFb {
} }
} }
fn main() { fn main() {
let yaml = load_yaml!("cli.yml"); let yaml = load_yaml!("cli.yml");
let matches = clap::App::from_yaml(yaml).get_matches(); let matches = clap::App::from_yaml(yaml).get_matches();
@ -118,8 +117,14 @@ fn main() {
})); }));
let mut fps_counter = FpsCounter::default(); let mut fps_counter = FpsCounter::default();
let mut gba: GameBoyAdvance<MiniFb, MiniFb, MiniFb> = let mut gba: GameBoyAdvance<MiniFb, MiniFb, MiniFb> = GameBoyAdvance::new(
GameBoyAdvance::new(cpu, bios_bin, cart, minifb.clone(), minifb.clone(), minifb.clone()); cpu,
bios_bin,
cart,
minifb.clone(),
minifb.clone(),
minifb.clone(),
);
if debug { if debug {
gba.cpu.set_verbose(true); gba.cpu.set_verbose(true);