diff --git a/src/core/arm7tdmi/cpu.rs b/src/core/arm7tdmi/cpu.rs index 96749ec..8cfe193 100644 --- a/src/core/arm7tdmi/cpu.rs +++ b/src/core/arm7tdmi/cpu.rs @@ -57,6 +57,8 @@ pub struct Core { pub verbose: bool, pub trace_opcodes: bool, + + pub trace_exceptions: bool, } pub type CpuExecResult = CpuResult<()>; diff --git a/src/core/arm7tdmi/exception.rs b/src/core/arm7tdmi/exception.rs index 9bfa767..175c21b 100644 --- a/src/core/arm7tdmi/exception.rs +++ b/src/core/arm7tdmi/exception.rs @@ -30,7 +30,7 @@ impl Core { Irq => (CpuMode::Irq, true, false), Fiq => (CpuMode::Fiq, true, true), }; - if self.verbose { + if self.trace_exceptions { println!( "{}: {:?}, pc: {:#x}, new_mode: {:?} old_mode: {:?}", "Exception".cyan(), diff --git a/src/core/dma.rs b/src/core/dma.rs index 2a1ce36..581a355 100644 --- a/src/core/dma.rs +++ b/src/core/dma.rs @@ -1,6 +1,7 @@ use std::collections::VecDeque; use super::arm7tdmi::{Addr, Bus}; +use super::iodev::consts::{REG_FIFO_A, REG_FIFO_B}; use super::sysbus::SysBus; use super::{Interrupt, IrqBitmask}; @@ -99,7 +100,10 @@ impl DmaChannel { self.internal.src_addr = self.src; self.internal.dst_addr = self.dst; 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; return start_immediately; @@ -133,11 +137,10 @@ impl DmaChannel { let fifo_mode = self.fifo_mode; if fifo_mode { - println!("FIFO Tranfer"); - for _ in 0..count { - let v = sb.read_16(self.internal.src_addr); - sb.write_16(self.internal.dst_addr, v); - self.internal.src_addr += 2; + for _ in 0..4 { + let v = sb.read_32(self.internal.src_addr); + sb.write_32(self.internal.dst_addr, v); + self.internal.src_addr += 4; } } else if word_size == 4 { for _ in 0..count { diff --git a/src/core/gba.rs b/src/core/gba.rs index 0ff81cc..bae3ce6 100644 --- a/src/core/gba.rs +++ b/src/core/gba.rs @@ -8,9 +8,6 @@ use super::gpu::*; use super::interrupt::*; use super::iodev::*; use super::sysbus::SysBus; -use super::timer::TimerEvent; - -use super::SyncedIoDevice; 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(); + } + + pub fn frame(&mut self) { + self.key_poll(); while self.sysbus.io.gpu.state != GpuState::VBlank { self.step(); } @@ -114,7 +116,7 @@ where 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) { match new_gpu_state { diff --git a/src/core/iodev.rs b/src/core/iodev.rs index ee5fb7a..5d0336b 100644 --- a/src/core/iodev.rs +++ b/src/core/iodev.rs @@ -28,7 +28,6 @@ pub struct IoDevices { pub post_boot_flag: bool, pub waitcnt: WaitControl, // TODO also implement 4000800 pub haltcnt: HaltState, - pub sound_bias: u16, mem: BoxedMemory, } @@ -46,7 +45,6 @@ impl IoDevices { haltcnt: HaltState::Running, keyinput: keypad::KEYINPUT_ALL_RELEASED, waitcnt: WaitControl(0), - sound_bias: 0x200, } } } @@ -87,30 +85,20 @@ impl Bus for IoDevices { REG_IE => io.intc.interrupt_enable.0 as u16, REG_IF => io.intc.interrupt_flags.0 as u16, - REG_TM0CNT_L => io.timers[0].timer_data, - 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_TM0CNT_L..=REG_TM3CNT_H => io.timers.handle_read(io_addr), + REG_SOUND1CNT_L..DMA_BASE => io.sound.handle_read(io_addr), REG_DMA0CNT_H => io.dmac.channels[0].ctrl.0, REG_DMA1CNT_H => io.dmac.channels[1].ctrl.0, REG_DMA2CNT_H => io.dmac.channels[2].ctrl.0, REG_DMA3CNT_H => io.dmac.channels[3].ctrl.0, - REG_SOUNDBIAS => io.sound_bias, - REG_WAITCNT => io.waitcnt.0, REG_POSTFLG => io.post_boot_flag as u16, REG_HALTCNT => 0, REG_KEYINPUT => io.keyinput as u16, - REG_SOUND1CNT_L..=DMA_BASE => io.sound.handle_read(io_addr), - _ => { // println!( // "Unimplemented read from {:x} {}", @@ -216,29 +204,11 @@ impl Bus for IoDevices { REG_IE => io.intc.interrupt_enable.0 = value, REG_IF => io.intc.interrupt_flags.0 &= !value, - REG_TM0CNT_L => { - io.timers[0].timer_data = value; - io.timers[0].initial_data = value; - } - REG_TM0CNT_H => io.timers.write_timer_ctl(0, value), + REG_TM0CNT_L..=REG_TM3CNT_H => io.timers.handle_write(io_addr, value), - REG_TM1CNT_L => { - io.timers[1].timer_data = value; - io.timers[1].initial_data = value; + REG_SOUND1CNT_L..DMA_BASE => { + io.sound.handle_write(io_addr, 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 => { let ofs = io_addr - DMA_BASE; @@ -246,8 +216,6 @@ impl Bus for IoDevices { io.dmac.write_16(channel_id, ofs % 12, value) } - REG_SOUNDBIAS => io.sound_bias = value & 0xc3fe, - REG_WAITCNT => io.waitcnt.0 = value, 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!( // "Unimplemented write to {:x} {}", diff --git a/src/core/sound/mod.rs b/src/core/sound/mod.rs index 552c6ad..6f7c5ca 100644 --- a/src/core/sound/mod.rs +++ b/src/core/sound/mod.rs @@ -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)] pub struct SoundController { sample_rate_to_cpu_freq: usize, // how many "cycles" are a sample? @@ -66,6 +72,8 @@ pub struct SoundController { sqr1_initial_vol: usize, sqr1_cur_vol: usize, + sound_bias: u16, + sound_a: DmaSound, sound_b: DmaSound, } @@ -95,6 +103,7 @@ impl SoundController { sqr1_step_increase: false, sqr1_initial_vol: 0, sqr1_cur_vol: 0, + sound_bias: 0x200, sound_a: Default::default(), sound_b: Default::default(), } @@ -137,6 +146,8 @@ impl SoundController { | cbit(14, self.sound_b.timer_select != 0) } + REG_SOUNDBIAS => self.sound_bias, + _ => { println!( "Unimplemented read from {:x} {}", @@ -174,7 +185,7 @@ impl SoundController { } if !self.mse { - println!("MSE disabled, refusing to write"); + // println!("MSE disabled, refusing to write"); return; } @@ -227,16 +238,18 @@ impl SoundController { } } - REG_FIFO_A => { - self.sound_a.fifo.write((value & 0xff00 >> 8) as i8); + REG_FIFO_A_L | REG_FIFO_A_H => { + self.sound_a.fifo.write(((value >> 8) & 0xff) as i8); self.sound_a.fifo.write((value & 0xff) as i8); } - REG_FIFO_B => { - self.sound_b.fifo.write((value & 0xff00 >> 8) as i8); + REG_FIFO_B_L | REG_FIFO_B_H => { + self.sound_b.fifo.write(((value >> 8) & 0xff) as i8); self.sound_b.fifo.write((value & 0xff) as i8); } + REG_SOUNDBIAS => self.sound_bias = value & 0xc3fe, + _ => { println!( "Unimplemented write to {:x} {}", diff --git a/src/core/timer.rs b/src/core/timer.rs index c398745..494ae06 100644 --- a/src/core/timer.rs +++ b/src/core/timer.rs @@ -1,29 +1,23 @@ use super::interrupt::{Interrupt, IrqBitmask}; +use super::iodev::consts::*; use super::sysbus::SysBus; -use super::SyncedIoDevice; use num::FromPrimitive; -#[derive(Debug, Default)] +#[derive(Debug)] pub struct Timer { // registers - pub timer_ctl: TimerCtl, - pub timer_data: u16, + pub ctl: TimerCtl, + pub data: u16, + + irq: Interrupt, timer_id: usize, - reg: u32, - target: u32, - fired: bool, pub initial_data: u16, pub cycles: usize, } -pub enum TimerEvent { - Overflow(usize, usize), - Increment(usize), -} - impl Timer { pub fn new(timer_id: usize) -> Timer { if timer_id > 3 { @@ -31,16 +25,16 @@ impl Timer { } Timer { 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 { - match self.timer_ctl.prescalar() { + match self.ctl.prescalar() { 0 => 1, 1 => 64, 2 => 256, @@ -48,29 +42,6 @@ impl Timer { _ => 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)] @@ -96,14 +67,14 @@ impl Timers { pub fn new() -> Timers { Timers { 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) { - let old_enabled = self[id].timer_ctl.enabled(); - self[id].timer_ctl.0 = value; - let new_enabled = self[id].timer_ctl.enabled(); + let old_enabled = self[id].ctl.enabled(); + self[id].ctl.0 = value; + let new_enabled = self[id].ctl.enabled(); if self.trace && old_enabled != new_enabled { println!( "TMR{} {}", @@ -113,36 +84,88 @@ impl Timers { } } - pub fn tick( - &mut self, - cycles: usize, - sb: &mut SysBus, - irqs: &mut IrqBitmask, - ) -> Option { - for i in 0..4 { - if self[i].timer_ctl.enabled() && !self[i].timer_ctl.cascade() { - let event = self[i].add_cycles(cycles, irqs); - match event { - TimerEvent::Overflow(_, num_overflows) => { - if self.trace { - println!("TMR{} overflown!", i); - } - if i != 3 { - let next_i = i + 1; - if self[next_i].timer_ctl.cascade() { - self[next_i].add_cycles(num_overflows, irqs); - } - } - if i == 0 || i == 1 { - sb.io.sound.handle_timer_overflow(&mut sb.io.dmac, i); - } - } - _ => {} + pub fn handle_read(&self, io_addr: u32) -> u16 { + match io_addr { + REG_TM0CNT_L => self.timers[0].data, + REG_TM0CNT_H => self.timers[0].ctl.0, + REG_TM1CNT_L => self.timers[1].data, + REG_TM1CNT_H => self.timers[1].ctl.0, + REG_TM2CNT_L => self.timers[2].data, + REG_TM2CNT_H => self.timers[2].ctl.0, + REG_TM3CNT_L => self.timers[3].data, + REG_TM3CNT_H => self.timers[3].ctl.0, + _ => unreachable!(), + } + } + + pub fn handle_write(&mut self, io_addr: u32, value: u16) { + match io_addr { + REG_TM0CNT_L => { + self.timers[0].data = value; + self.timers[0].initial_data = value; + } + REG_TM0CNT_H => self.write_timer_ctl(0, value), + + 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 } } diff --git a/src/debugger/command.rs b/src/debugger/command.rs index 346fe6a..a0aab9e 100644 --- a/src/debugger/command.rs +++ b/src/debugger/command.rs @@ -38,6 +38,7 @@ bitflags! { const TRACE_OPCODE = 0b00000010; const TRACE_DMA = 0b00000100; const TRACE_TIMERS = 0b000001000; + const TRACE_EXCEPTIONS = 0b000001000; } } @@ -108,31 +109,30 @@ where } println!("{}\n", self.gba.cpu); } - // Continue => { - // self.ctrlc_flag.store(true, Ordering::SeqCst); - // while self.ctrlc_flag.load(Ordering::SeqCst) { - // let start_time = time::Instant::now(); - // self.gba.update_key_state(); - // match self.gba.check_breakpoint() { - // Some(addr) => { - // println!("Breakpoint reached! @{:x}", addr); - // break; - // } - // _ => { - // self.gba.step(); - // } - // } - // } - // } - // Frame(count) => { - // use super::time::PreciseTime; - // let start = PreciseTime::now(); - // for _ in 0..count { - // self.gba.frame(); - // } - // let end = PreciseTime::now(); - // println!("that took {} seconds", start.to(end)); - // } + Continue => { + self.ctrlc_flag.store(true, Ordering::SeqCst); + while self.ctrlc_flag.load(Ordering::SeqCst) { + self.gba.key_poll(); + match self.gba.check_breakpoint() { + Some(addr) => { + println!("Breakpoint reached! @{:x}", addr); + break; + } + _ => { + self.gba.step(); + } + } + } + } + Frame(count) => { + use super::time::PreciseTime; + let start = PreciseTime::now(); + for _ in 0..count { + self.gba.frame(); + } + let end = PreciseTime::now(); + println!("that took {} seconds", start.to(end)); + } HexDump(addr, nbytes) => { let bytes = self.gba.sysbus.get_bytes(addr..addr + nbytes); 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) { println!("[*] dma tracing not implemented"); } @@ -420,7 +431,7 @@ where "r" | "reset" => Ok(Command::Reset), "trace" => { let usage = DebuggerError::InvalidCommandFormat(String::from( - "trace [sysbus|opcode|dma|all]", + "trace [sysbus|opcode|dma|all|exceptions]", )); if args.len() != 1 { Err(usage) @@ -429,6 +440,7 @@ where let flags = match flag_str.as_ref() { "sysbus" => TraceFlags::TRACE_SYSBUS, "opcode" => TraceFlags::TRACE_OPCODE, + "exceptions" => TraceFlags::TRACE_EXCEPTIONS, "dma" => TraceFlags::TRACE_DMA, "timers" => TraceFlags::TRACE_TIMERS, "all" => TraceFlags::all(), diff --git a/src/lib.rs b/src/lib.rs index f6bf84e..ff315b5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,6 @@ #![feature(asm)] #![feature(core_intrinsics)] +#![feature(exclusive_range_pattern)] #[macro_use] extern crate enum_primitive_derive; diff --git a/src/plat/minifb/main.rs b/src/plat/minifb/main.rs index 97febe4..d0a6e8e 100644 --- a/src/plat/minifb/main.rs +++ b/src/plat/minifb/main.rs @@ -11,9 +11,9 @@ extern crate clap; #[macro_use] extern crate rustboyadvance_ng; +use rustboyadvance_ng::core::keypad; use rustboyadvance_ng::prelude::*; use rustboyadvance_ng::util::FpsCounter; -use rustboyadvance_ng::core::keypad; extern crate bit; use bit::BitIndex; @@ -80,7 +80,6 @@ impl AudioInterface for MiniFb { } } - fn main() { let yaml = load_yaml!("cli.yml"); let matches = clap::App::from_yaml(yaml).get_matches(); @@ -118,8 +117,14 @@ fn main() { })); let mut fps_counter = FpsCounter::default(); - let mut gba: GameBoyAdvance = - GameBoyAdvance::new(cpu, bios_bin, cart, minifb.clone(), minifb.clone(), minifb.clone()); + let mut gba: GameBoyAdvance = GameBoyAdvance::new( + cpu, + bios_bin, + cart, + minifb.clone(), + minifb.clone(), + minifb.clone(), + ); if debug { gba.cpu.set_verbose(true);