serial as mmio peripheral
This commit is contained in:
parent
2609fb966d
commit
c622a2ae4a
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
99
src/processor/memory/mmio/serial.rs
Normal file
99
src/processor/memory/mmio/serial.rs
Normal 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(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in a new issue