diff --git a/src/processor/memory.rs b/src/processor/memory.rs index 487d685..e76099b 100644 --- a/src/processor/memory.rs +++ b/src/processor/memory.rs @@ -1,9 +1,9 @@ -use self::mmio::{Apu, Joypad}; +use self::mmio::{Apu, Joypad, Serial}; pub use self::rom::Rom; -use crate::{processor::SplitRegister, verbose_println}; +use crate::{processor::SplitRegister, util::set_bit, verbose_println, Cpu}; use gilrs::ConnectedGamepadsIterator; use minifb::Key; -use std::io::{stdout, Write}; +// use std::io::{stdout, Write}; mod mmio; pub(crate) mod rom; @@ -27,12 +27,11 @@ pub struct Memory { pub(super) user_mode: bool, joypad: Joypad, apu: Apu, + serial: Serial, } impl Memory { pub fn init(bootrom: Vec, bootrom_enabled: bool, rom: Rom) -> Self { - let mut apu = Apu::default(); - apu.init(); Self { bootrom, bootrom_enabled, @@ -48,7 +47,8 @@ impl Memory { io: [0xFF; 76], user_mode: false, joypad: Joypad::default(), - apu, + apu: Apu::init_default(), + serial: Serial::default(), } } @@ -85,33 +85,16 @@ impl Memory { // change this with MBC code... self.rom.set(address, data); } - 0x8000..0xA000 => { - self.vram[(address - 0x8000) as usize] = data; - } - 0xA000..0xC000 => { - self.rom.set_ram(address, data); - } - 0xC000..0xE000 => { - self.ram[(address - 0xC000) as usize] = data; - } - 0xE000..0xFE00 => { - self.ram[(address - 0xE000) as usize] = data; - } - 0xFE00..0xFEA0 => { - self.oam[(address - 0xFE00) as usize] = data; - } + 0x8000..0xA000 => self.vram[(address - 0x8000) as usize] = data, + 0xA000..0xC000 => self.rom.set_ram(address, data), + 0xC000..0xE000 => self.ram[(address - 0xC000) as usize] = data, + 0xE000..0xFE00 => self.ram[(address - 0xE000) as usize] = data, + 0xFE00..0xFEA0 => self.oam[(address - 0xFE00) as usize] = data, 0xFEA0..0xFF00 => {} - 0xFF00..0xFF4C => { - self.set_io(address, data); - stdout().flush().unwrap(); - } - 0xFF50 => { - self.bootrom_enabled = false; - } + 0xFF00..0xFF4C => self.set_io(address, data), + 0xFF50 => self.bootrom_enabled = false, 0xFF4C..0xFF50 | 0xFF51..0xFF80 => {} - 0xFF80..0xFFFF => { - self.cpu_ram[(address - 0xFF80) as usize] = data; - } + 0xFF80..0xFFFF => self.cpu_ram[(address - 0xFF80) as usize] = data, 0xFFFF => { verbose_println!("interrupts set to {:#b}", data); verbose_println!(" / {:#X}", data); @@ -124,6 +107,8 @@ impl Memory { // range: 0xFF00 - 0xFF4B inclusive match address { 0xFF00 => self.joypad.as_register(), + 0xFF01 => self.serial.get_queued(), + 0xFF02 => self.serial.get_control(), 0xFF10..0xFF40 => self.apu.get_register(address), _ => self.io[(address - 0xFF00) as usize], } @@ -140,12 +125,8 @@ impl Memory { // joypad self.joypad.mmio_write(data); } - 0xFF02 => { - if data == 0x81 { - print!("{}", self.get(0xFF01) as char); - stdout().flush().unwrap(); - } - } + 0xFF01 => self.serial.update_queued(data), + 0xFF02 => self.serial.update_control(data), 0xFF04 => self.io[addr_l] = 0, 0xFF07 => self.masked_io(addr_l, data, 0b111), 0xFF0F => self.masked_io(addr_l, data, 0b11111), @@ -219,6 +200,15 @@ impl Memory { } } +impl Cpu { + pub fn advance_mmio_clocks(&mut self, steps: usize) { + self.memory.apu.tick(steps); + if self.memory.serial.tick(steps) { + self.memory.set(0xFF0F, set_bit(self.memory.get(0xFF0F), 3)); + } + } +} + pub fn masked_update(current: u8, data: u8, mask: u8) -> u8 { (current & (!mask)) | (data & mask) } diff --git a/src/processor/memory/mmio/apu.rs b/src/processor/memory/mmio/apu.rs index 869e040..45915eb 100644 --- a/src/processor/memory/mmio/apu.rs +++ b/src/processor/memory/mmio/apu.rs @@ -1,6 +1,6 @@ use self::types::{Channels, DacSample, Mixer, VinEnable, Volume}; use crate::{ - processor::{memory::Address, timer::CLOCK_SPEED, Cpu}, + processor::{memory::Address, timer::CLOCK_SPEED}, util::{get_bit, set_or_clear_bit}, }; use async_ringbuf::{AsyncHeapProducer, AsyncHeapRb}; @@ -95,6 +95,12 @@ impl Default for Apu { const CYCLES_PER_FRAME: usize = 70224; impl Apu { + pub fn init_default() -> Self { + let mut r = Self::default(); + r.init(); + r + } + pub fn init(&mut self) { let sample_rate = self.config.sample_rate.0; let rb_len = sample_rate as usize / 60; @@ -150,7 +156,7 @@ impl Apu { } } - fn tick(&mut self, steps: usize) { + pub fn tick(&mut self, steps: usize) { self.buffer.append( &mut izip!( self.channels.one.tick(steps).into_iter(), @@ -303,9 +309,3 @@ impl Apu { } } } - -impl Cpu { - pub fn advance_apu_clock(&mut self, steps: usize) { - self.memory.apu.tick(steps); - } -} diff --git a/src/processor/memory/mmio/mod.rs b/src/processor/memory/mmio/mod.rs index b00cd38..f14a15c 100644 --- a/src/processor/memory/mmio/mod.rs +++ b/src/processor/memory/mmio/mod.rs @@ -1,4 +1,6 @@ mod apu; mod joypad; +mod serial; pub use apu::Apu; pub use joypad::Joypad; +pub use serial::Serial; diff --git a/src/processor/memory/mmio/serial.rs b/src/processor/memory/mmio/serial.rs new file mode 100644 index 0000000..606f2f2 --- /dev/null +++ b/src/processor/memory/mmio/serial.rs @@ -0,0 +1,99 @@ +use std::io::{stdout, Write}; + +use crate::util::get_bit; + +enum ClockSource { + Internal, + External, +} + +#[allow(dead_code)] +enum ClockSpeed { + Normal, + Fast, +} + +#[allow(dead_code)] +struct SerialControl { + transfer_in_progress: bool, + clock_speed: ClockSpeed, + clock_source: ClockSource, +} + +impl Default for SerialControl { + fn default() -> Self { + Self { + transfer_in_progress: false, + clock_speed: ClockSpeed::Normal, + clock_source: ClockSource::Internal, + } + } +} + +pub struct Serial { + byte: u8, + bits_transferred: u8, + control: SerialControl, +} + +impl Serial { + pub fn tick(&mut self, steps: usize) -> bool { + let mut will_interrupt = false; + for _ in 0..steps { + if self.control.transfer_in_progress { + let (remainder, finished) = self.bits_transferred.overflowing_sub(1); + self.bits_transferred = if finished { + self.control.transfer_in_progress = false; + will_interrupt = true; + print!("{}", self.byte as char); + stdout().flush().unwrap(); + 7 + } else { + remainder + } + } + } + will_interrupt + } + + pub fn update_queued(&mut self, data: u8) { + self.byte = data; + } + + pub fn get_queued(&self) -> u8 { + self.byte + } + + pub fn update_control(&mut self, data: u8) { + self.control.transfer_in_progress = get_bit(data, 7); + // CGB - add clock speed + self.control.clock_source = if get_bit(data, 0) { + ClockSource::Internal + } else { + ClockSource::External + }; + } + + pub fn get_control(&self) -> u8 { + 0b01111110 + | (if self.control.transfer_in_progress { + 0b1 + } else { + 0b0 + } << 7) + | match self.control.clock_source { + ClockSource::Internal => 0b1, + ClockSource::External => 0b0, + } + } +} + +impl Default for Serial { + fn default() -> Self { + Self { + byte: 0, + bits_transferred: 7, + control: SerialControl::default(), + } + } +} diff --git a/src/processor/timer.rs b/src/processor/timer.rs index fcce700..d0d510a 100644 --- a/src/processor/timer.rs +++ b/src/processor/timer.rs @@ -28,7 +28,7 @@ impl Cpu { let clock_cycles = (machine_cycles as usize) * 4; self.advance_gpu_clock(clock_cycles); - self.advance_apu_clock(clock_cycles); + self.advance_mmio_clocks(clock_cycles); self.timers.div_counter += clock_cycles; let mut div_diff = (self.timers.div_counter / 256) as u8;