diff --git a/src/cpu.rs b/src/cpu.rs index 9be9b16944dfd31c5fd9c66401c6058f64551e71..4f3b03ed23e893233f20090081cb490b60d16b27 100644 --- a/src/cpu.rs +++ b/src/cpu.rs @@ -7,6 +7,7 @@ use crate::{ mmu::Mmu, pad::Pad, ppu::Ppu, + serial::Serial, timer::Timer, }; @@ -297,6 +298,11 @@ impl Cpu { self.mmu().timer() } + #[inline(always)] + pub fn serial(&mut self) -> &mut Serial { + self.mmu().serial() + } + #[inline(always)] pub fn halted(&self) -> bool { self.halted diff --git a/src/gb.rs b/src/gb.rs index 5fc389dc5f42b3f1f09b86c1a0e84c4e67b24209..327e15b47a8cb23c0229468fb541e03263ab2bc2 100644 --- a/src/gb.rs +++ b/src/gb.rs @@ -7,6 +7,7 @@ use crate::{ pad::{Pad, PadKey}, ppu::{Ppu, PpuMode, Tile, FRAME_BUFFER_SIZE}, rom::Cartridge, + serial::Serial, timer::Timer, util::read_file, }; @@ -44,6 +45,9 @@ pub struct GameBoy { /// If the timer is enabled, it will be clocked. timer_enabled: bool, + /// If the serial is enabled, it will be clocked. + serial_enabled: bool, + /// The current frequency at which the Game Boy /// emulator is being handled. This is a "hint" that /// may help components to adjust their internal @@ -86,13 +90,15 @@ impl GameBoy { let apu = Apu::default(); let pad = Pad::default(); let timer = Timer::default(); - let mmu = Mmu::new(ppu, apu, pad, timer); + let serial = Serial::default(); + let mmu = Mmu::new(ppu, apu, pad, timer, serial); let cpu = Cpu::new(mmu); Self { cpu, ppu_enabled: true, apu_enabled: true, timer_enabled: true, + serial_enabled: true, clock_freq: GameBoy::CPU_FREQ, } } @@ -100,6 +106,8 @@ impl GameBoy { pub fn reset(&mut self) { self.ppu().reset(); self.apu().reset(); + self.timer().reset(); + self.serial().reset(); self.mmu().reset(); self.cpu.reset(); } @@ -115,6 +123,9 @@ impl GameBoy { if self.timer_enabled { self.timer_clock(cycles); } + if self.serial_enabled { + self.serial_clock(cycles); + } cycles } @@ -142,6 +153,10 @@ impl GameBoy { self.timer().clock(cycles) } + pub fn serial_clock(&mut self, cycles: u8) { + self.serial().clock(cycles) + } + pub fn ppu_ly(&mut self) -> u8 { self.ppu().ly() } @@ -325,6 +340,14 @@ impl GameBoy { self.timer_enabled = value; } + pub fn serial_enabled(&self) -> bool { + self.serial_enabled + } + + pub fn set_serial_enabled(&mut self, value: bool) { + self.serial_enabled = value; + } + pub fn clock_freq(&self) -> u32 { self.clock_freq } @@ -378,6 +401,10 @@ impl GameBoy { self.cpu.timer() } + pub fn serial(&mut self) -> &mut Serial { + self.cpu.serial() + } + pub fn frame_buffer(&mut self) -> &[u8; FRAME_BUFFER_SIZE] { &(self.ppu().frame_buffer) } diff --git a/src/lib.rs b/src/lib.rs index 7decfab2b80948c67ae0c8c3765191dda99b7743..86a725760ebe3f191118a6f09f3bfa9e2070ca06 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,5 +11,6 @@ pub mod mmu; pub mod pad; pub mod ppu; pub mod rom; +pub mod serial; pub mod timer; pub mod util; diff --git a/src/mmu.rs b/src/mmu.rs index bc4a9ddd43ca2d8a814f4d0af2c09bce448d2a6d..ff7b66b920d967bb6ac44eb58ac9357491f13597 100644 --- a/src/mmu.rs +++ b/src/mmu.rs @@ -1,4 +1,4 @@ -use crate::{apu::Apu, debugln, pad::Pad, ppu::Ppu, rom::Cartridge, timer::Timer}; +use crate::{apu::Apu, debugln, pad::Pad, ppu::Ppu, rom::Cartridge, serial::Serial, timer::Timer}; pub const BOOT_SIZE: usize = 2304; pub const RAM_SIZE: usize = 8192; @@ -26,6 +26,10 @@ pub struct Mmu { /// that is memory mapped. timer: Timer, + /// The serial data transfer controller to be used to control the + /// link cable connection, this component is memory mapped. + serial: Serial, + /// The cartridge ROM that is currently loaded into the system, /// going to be used to access ROM and external RAM banks. rom: Cartridge, @@ -49,12 +53,13 @@ pub struct Mmu { } impl Mmu { - pub fn new(ppu: Ppu, apu: Apu, pad: Pad, timer: Timer) -> Self { + pub fn new(ppu: Ppu, apu: Apu, pad: Pad, timer: Timer, serial: Serial) -> Self { Self { ppu, apu, pad, timer, + serial, rom: Cartridge::new(), boot_active: true, boot: [0u8; BOOT_SIZE], @@ -91,6 +96,10 @@ impl Mmu { &mut self.timer } + pub fn serial(&mut self) -> &mut Serial { + &mut self.serial + } + pub fn boot_active(&self) -> bool { self.boot_active } diff --git a/src/serial.rs b/src/serial.rs new file mode 100644 index 0000000000000000000000000000000000000000..cb51f2861a51d35faf99679ae70bfa5973235c38 --- /dev/null +++ b/src/serial.rs @@ -0,0 +1,79 @@ +use crate::warnln; + +pub struct Serial { + data: u8, + control: u8, + shift_clock: bool, + clock_speed: bool, + transferring: bool, +} + +impl Serial { + pub fn new() -> Self { + Self { + data: 0x0, + control: 0x0, + shift_clock: false, + clock_speed: false, + transferring: false, + } + } + + pub fn reset(&mut self) { + self.data = 0x0; + self.control = 0x0; + self.shift_clock = false; + self.clock_speed = false; + self.transferring = false; + } + + pub fn clock(&mut self, cycles: u8) {} + + pub fn read(&mut self, addr: u16) -> u8 { + match addr & 0x00ff { + 0x01 => self.data, + 0x02 => { + (if self.shift_clock { 0x01 } else { 0x00 } + | if self.clock_speed { 0x02 } else { 0x00 } + | if self.transferring { 0x80 } else { 0x00 }) + } + _ => { + warnln!("Reding from unknown Timer location 0x{:04x}", addr); + 0xff + } + } + } + + pub fn write(&mut self, addr: u16, value: u8) { + match addr & 0x00ff { + 0x01 => self.data = value, + 0x02 => { + self.shift_clock = value & 0x01 == 0x01; + self.clock_speed = value & 0x02 == 0x02; + self.transferring = value & 0x80 == 0x80; + } + _ => warnln!("Writing to unknown Serial location 0x{:04x}", addr), + } + } + + fn send(&self) -> bool { + if self.shift_clock { + true + } else { + self.data & 0x80 == 0x80 + } + } + + fn receive(&self, bit: bool) { + if !self.shift_clock { + //data = (data << 1) | bit; + //check_transfer(); + } + } +} + +impl Default for Serial { + fn default() -> Self { + Self::new() + } +}