serial as mmio peripheral

This commit is contained in:
Alex Janka 2023-02-21 10:10:24 +11:00
parent 2609fb966d
commit c622a2ae4a
5 changed files with 137 additions and 46 deletions

View file

@ -1,9 +1,9 @@
use self::mmio::{Apu, Joypad}; use self::mmio::{Apu, Joypad, Serial};
pub use self::rom::Rom; 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 gilrs::ConnectedGamepadsIterator;
use minifb::Key; use minifb::Key;
use std::io::{stdout, Write}; // use std::io::{stdout, Write};
mod mmio; mod mmio;
pub(crate) mod rom; pub(crate) mod rom;
@ -27,12 +27,11 @@ pub struct Memory {
pub(super) user_mode: bool, pub(super) user_mode: bool,
joypad: Joypad, joypad: Joypad,
apu: Apu, apu: Apu,
serial: Serial,
} }
impl Memory { impl Memory {
pub fn init(bootrom: Vec<u8>, bootrom_enabled: bool, rom: Rom) -> Self { pub fn init(bootrom: Vec<u8>, bootrom_enabled: bool, rom: Rom) -> Self {
let mut apu = Apu::default();
apu.init();
Self { Self {
bootrom, bootrom,
bootrom_enabled, bootrom_enabled,
@ -48,7 +47,8 @@ impl Memory {
io: [0xFF; 76], io: [0xFF; 76],
user_mode: false, user_mode: false,
joypad: Joypad::default(), joypad: Joypad::default(),
apu, apu: Apu::init_default(),
serial: Serial::default(),
} }
} }
@ -85,33 +85,16 @@ impl Memory {
// change this with MBC code... // change this with MBC code...
self.rom.set(address, data); self.rom.set(address, data);
} }
0x8000..0xA000 => { 0x8000..0xA000 => self.vram[(address - 0x8000) as usize] = data,
self.vram[(address - 0x8000) as usize] = data; 0xA000..0xC000 => self.rom.set_ram(address, data),
} 0xC000..0xE000 => self.ram[(address - 0xC000) as usize] = data,
0xA000..0xC000 => { 0xE000..0xFE00 => self.ram[(address - 0xE000) as usize] = data,
self.rom.set_ram(address, data); 0xFE00..0xFEA0 => self.oam[(address - 0xFE00) as usize] = 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 => {} 0xFEA0..0xFF00 => {}
0xFF00..0xFF4C => { 0xFF00..0xFF4C => self.set_io(address, data),
self.set_io(address, data); 0xFF50 => self.bootrom_enabled = false,
stdout().flush().unwrap();
}
0xFF50 => {
self.bootrom_enabled = false;
}
0xFF4C..0xFF50 | 0xFF51..0xFF80 => {} 0xFF4C..0xFF50 | 0xFF51..0xFF80 => {}
0xFF80..0xFFFF => { 0xFF80..0xFFFF => self.cpu_ram[(address - 0xFF80) as usize] = data,
self.cpu_ram[(address - 0xFF80) as usize] = data;
}
0xFFFF => { 0xFFFF => {
verbose_println!("interrupts set to {:#b}", data); verbose_println!("interrupts set to {:#b}", data);
verbose_println!(" / {:#X}", data); verbose_println!(" / {:#X}", data);
@ -124,6 +107,8 @@ impl Memory {
// range: 0xFF00 - 0xFF4B inclusive // range: 0xFF00 - 0xFF4B inclusive
match address { match address {
0xFF00 => self.joypad.as_register(), 0xFF00 => self.joypad.as_register(),
0xFF01 => self.serial.get_queued(),
0xFF02 => self.serial.get_control(),
0xFF10..0xFF40 => self.apu.get_register(address), 0xFF10..0xFF40 => self.apu.get_register(address),
_ => self.io[(address - 0xFF00) as usize], _ => self.io[(address - 0xFF00) as usize],
} }
@ -140,12 +125,8 @@ impl Memory {
// joypad // joypad
self.joypad.mmio_write(data); self.joypad.mmio_write(data);
} }
0xFF02 => { 0xFF01 => self.serial.update_queued(data),
if data == 0x81 { 0xFF02 => self.serial.update_control(data),
print!("{}", self.get(0xFF01) as char);
stdout().flush().unwrap();
}
}
0xFF04 => self.io[addr_l] = 0, 0xFF04 => self.io[addr_l] = 0,
0xFF07 => self.masked_io(addr_l, data, 0b111), 0xFF07 => self.masked_io(addr_l, data, 0b111),
0xFF0F => self.masked_io(addr_l, data, 0b11111), 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 { pub fn masked_update(current: u8, data: u8, mask: u8) -> u8 {
(current & (!mask)) | (data & mask) (current & (!mask)) | (data & mask)
} }

View file

@ -1,6 +1,6 @@
use self::types::{Channels, DacSample, Mixer, VinEnable, Volume}; use self::types::{Channels, DacSample, Mixer, VinEnable, Volume};
use crate::{ use crate::{
processor::{memory::Address, timer::CLOCK_SPEED, Cpu}, processor::{memory::Address, timer::CLOCK_SPEED},
util::{get_bit, set_or_clear_bit}, util::{get_bit, set_or_clear_bit},
}; };
use async_ringbuf::{AsyncHeapProducer, AsyncHeapRb}; use async_ringbuf::{AsyncHeapProducer, AsyncHeapRb};
@ -95,6 +95,12 @@ impl Default for Apu {
const CYCLES_PER_FRAME: usize = 70224; const CYCLES_PER_FRAME: usize = 70224;
impl Apu { impl Apu {
pub fn init_default() -> Self {
let mut r = Self::default();
r.init();
r
}
pub fn init(&mut self) { pub fn init(&mut self) {
let sample_rate = self.config.sample_rate.0; let sample_rate = self.config.sample_rate.0;
let rb_len = sample_rate as usize / 60; 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( self.buffer.append(
&mut izip!( &mut izip!(
self.channels.one.tick(steps).into_iter(), 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);
}
}

View file

@ -1,4 +1,6 @@
mod apu; mod apu;
mod joypad; mod joypad;
mod serial;
pub use apu::Apu; pub use apu::Apu;
pub use joypad::Joypad; pub use joypad::Joypad;
pub use serial::Serial;

View file

@ -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(),
}
}
}

View file

@ -28,7 +28,7 @@ impl Cpu {
let clock_cycles = (machine_cycles as usize) * 4; let clock_cycles = (machine_cycles as usize) * 4;
self.advance_gpu_clock(clock_cycles); self.advance_gpu_clock(clock_cycles);
self.advance_apu_clock(clock_cycles); self.advance_mmio_clocks(clock_cycles);
self.timers.div_counter += clock_cycles; self.timers.div_counter += clock_cycles;
let mut div_diff = (self.timers.div_counter / 256) as u8; let mut div_diff = (self.timers.div_counter / 256) as u8;