diff options
author | Astatin <[email protected]> | 2025-06-29 00:32:41 +0200 |
---|---|---|
committer | Astatin <[email protected]> | 2025-06-29 00:32:41 +0200 |
commit | 4cb95575feedda5e2006d706a9e85db6569043ca (patch) | |
tree | 15f89bc7d0845f8920237961bf9bdedee38ed8b7 | |
parent | 00c5b36a37d9b7e8b7009b3a176397e5ce808cbd (diff) |
Fix (rewrite ?) FIFO serial communication
-rw-r--r-- | src/desktop/serial.rs | 140 | ||||
-rw-r--r-- | src/io.rs | 31 | ||||
-rw-r--r-- | src/main.rs | 8 | ||||
-rw-r--r-- | src/mmio.rs | 15 | ||||
-rw-r--r-- | src/state.rs | 32 |
5 files changed, 104 insertions, 122 deletions
diff --git a/src/desktop/serial.rs b/src/desktop/serial.rs index 342f7f5..29fad9d 100644 --- a/src/desktop/serial.rs +++ b/src/desktop/serial.rs @@ -1,130 +1,128 @@ use std::fs::File; use std::io::{Read, Write}; use std::sync::mpsc::{self, Receiver, Sender}; +use std::sync::{Arc, Mutex}; use std::thread; use crate::io::Serial; +use crate::consts::CPU_CLOCK_SPEED; pub struct UnconnectedSerial {} impl Serial for UnconnectedSerial { - fn write(&mut self, byte: u8) { - println!("Writing {} to unconnected serial", byte); + fn read_data(&self) -> u8 { + 0xff } - - fn read(&mut self) -> u8 { - println!("Reading 0 from unconnected serial"); + fn read_control(&self) -> u8 { 0 } - - fn new_transfer(&mut self) -> bool { - false + fn write_data(&mut self, _data: u8) { } - - fn clock_master(&mut self) -> bool { + fn write_control(&mut self, _control: u8) { + } + fn update_serial(&mut self, _cycles: u128) -> bool { false } - - fn set_clock_master(&mut self, _clock_master: bool) {} } -pub struct FIFOPackage { - t: bool, - value: u8, +enum FIFOMessage { + Request(u8), + Response(u8), } pub struct FIFOSerial { + transfer_requested: bool, + current_transfer: bool, + current_data: u8, + + external_clock: bool, + next_byte_transfer_cycle: u128, + input: Receiver<u8>, - output: Sender<FIFOPackage>, - clock_change: Receiver<bool>, - last_read_byte: u8, - clock_master: bool, + output: Sender<u8>, } impl FIFOSerial { pub fn new(input_path: String, output_path: String) -> FIFOSerial { let (tx, input) = mpsc::channel::<u8>(); - let (clock_tx, clock_change) = mpsc::channel::<bool>(); thread::spawn(move || { let mut input_f = File::open(input_path).unwrap(); loop { - let mut byte = [0, 0]; + let mut byte = [0]; input_f.read(&mut byte).unwrap(); - if byte[0] == 1 { - tx.send(byte[1]).unwrap(); - } else { - clock_tx.send(byte[1] == 0).unwrap(); - } + + tx.send(byte[0]).unwrap(); } }); - let (output, rx) = mpsc::channel::<FIFOPackage>(); + let (output, rx) = mpsc::channel::<u8>(); thread::spawn(move || { let mut output_f = File::create(output_path).unwrap(); for b in rx.iter() { - if b.t { - output_f.write(&[1, b.value]).unwrap(); - } else { - output_f.write(&[0, b.value]).unwrap(); - } + output_f.write(&[b]).unwrap(); } }); FIFOSerial { + transfer_requested: false, + current_transfer: false, + current_data: 0, + + external_clock: false, + next_byte_transfer_cycle: 0, + input, output, - clock_change, - last_read_byte: 0xff, - clock_master: false, } } } impl Serial for FIFOSerial { - fn write(&mut self, byte: u8) { - println!("Writing {} to fifo serial", byte); - if let Err(err) = self.output.send(FIFOPackage { - t: true, - value: byte, - }) { - eprintln!("Error while sending serial package: {}", err); - }; + fn read_data(&self) -> u8 { + self.current_data } - fn read(&mut self) -> u8 { - println!("Reading {} from fifo serial", self.last_read_byte); - self.last_read_byte + fn read_control(&self) -> u8 { + (if self.external_clock { 0 } else { 0x01 }) | + (if self.transfer_requested { 0x80 } else { 0 }) } - fn new_transfer(&mut self) -> bool { - match self.input.try_recv() { - Ok(byte) => { - println!("Received: {}", byte); - self.last_read_byte = byte; - true - } - _ => false, - } + fn write_data(&mut self, data: u8) { + self.current_data = data; } - fn clock_master(&mut self) -> bool { - match self.clock_change.try_recv() { - Ok(byte) => { - println!("Received clock change, master: {}", byte); - self.clock_master = byte; - } - _ => {} - }; - self.clock_master + + fn write_control(&mut self, control: u8) { + self.external_clock = (control & 0b01) == 0; + self.transfer_requested = (control & 0x80) != 0; } - fn set_clock_master(&mut self, clock_master: bool) { - self.clock_master = clock_master; - if let Err(err) = self.output.send(FIFOPackage { - t: false, - value: (if clock_master { 1 } else { 0 }), - }) { - eprintln!("Error while sending serial package: {}", err); + fn update_serial(&mut self, cycles: u128) -> bool { + if let Ok(x) = self.input.try_recv() { + if self.current_transfer { + self.current_data = x; + self.external_clock = false; + self.transfer_requested = false; + self.next_byte_transfer_cycle = cycles + ((CPU_CLOCK_SPEED as u128) / 1024); + self.current_transfer = false; + } else { + self.output.send(self.current_data).unwrap(); + println!("recv {:02x}, send back {:02x}", x, self.current_data); + self.current_data = x; + self.external_clock = true; + self.transfer_requested = false; + } + true + } else if !self.external_clock && !self.current_transfer + && self.transfer_requested { + if cycles > self.next_byte_transfer_cycle { + self.output.send(self.current_data).unwrap(); + println!("send {:02x}", self.current_data); + self.current_transfer = true; + } + false + } else { + false } } } @@ -35,14 +35,29 @@ pub trait Window { } pub trait Serial { - // Should not be blocking - fn write(&mut self, byte: u8); - fn read(&mut self) -> u8; - - fn new_transfer(&mut self) -> bool; // since last read - fn clock_master(&mut self) -> bool; + fn read_data(&self) -> u8; + fn read_control(&self) -> u8; + fn write_data(&mut self, data: u8); + fn write_control(&mut self, control: u8); + fn update_serial(&mut self, cycles: u128) -> bool; +} - fn set_clock_master(&mut self, clock_master: bool); +impl<T: Serial + ?Sized> Serial for Box<T> { + fn read_data(&self) -> u8 { + (**self).read_data() + } + fn read_control(&self) -> u8 { + (**self).read_data() + } + fn write_data(&mut self, data: u8) { + (**self).write_data(data); + } + fn write_control(&mut self, control: u8) { + (**self).write_control(control); + } + fn update_serial(&mut self, cycles: u128) -> bool { + (**self).update_serial(cycles) + } } pub trait Audio { @@ -154,7 +169,7 @@ impl<I: Input, W: Window, S: Serial, A: Audio, LS: LoadSave> Gameboy<I, W, S, A, state.tima_timer(c); state.update_display_interrupts(c); state.check_interrupts(); - state.mem.update_serial(); + state.mem.update_serial(total_cycle_counter); nanos_sleep += c as f64 * (consts::CPU_CYCLE_LENGTH_NANOS as f64 / *speed) as f64; diff --git a/src/main.rs b/src/main.rs index 20bfc9c..d4cdb34 100644 --- a/src/main.rs +++ b/src/main.rs @@ -10,7 +10,7 @@ pub mod state; use crate::desktop::input::{Gamepad, GamepadRecorder, GamepadReplay, Keyboard}; use crate::desktop::load_save::FSLoadSave; -use crate::io::Input; +use crate::io::{Serial, Input}; use clap::Parser; #[derive(Parser)] @@ -73,9 +73,13 @@ fn main() { println!("Starting {:?}...", &cli.rom); - let serial = desktop::serial::UnconnectedSerial {}; let window = desktop::window::DesktopWindow::new(cli.title).unwrap(); + let serial: Box<dyn Serial> = match (cli.fifo_input, cli.fifo_output) { + (Some(fifo_input), Some(fifo_output)) => Box::new(desktop::serial::FIFOSerial::new(fifo_input, fifo_output)), + _ => Box::new(desktop::serial::UnconnectedSerial {}) + }; + let mut gamepad: Box<dyn Input> = if let Some(record_file) = cli.replay_input { Box::new(GamepadReplay::new(record_file)) } else if cli.keyboard { diff --git a/src/mmio.rs b/src/mmio.rs index 366c94b..fe37b7d 100644 --- a/src/mmio.rs +++ b/src/mmio.rs @@ -14,8 +14,8 @@ impl<S: Serial, A: Audio> Memory<S, A> { (self.joypad_reg & 0xf) | 0b11100000 } } - 0x01 => self.serial_data, - 0x02 => self.serial_control, + 0x01 => self.serial.read_data(), + 0x02 => self.serial.read_control(), 0x04 => self.div, 0x0f => self.io[0x0f], 0x40 => self.display.lcdc, @@ -67,17 +67,10 @@ impl<S: Serial, A: Audio> Memory<S, A> { self.joypad_is_action = !value & 0b00100000 != 0; } 0x01 => { - self.serial_data = value; + self.serial.write_data(value); } 0x02 => { - if value & 0x01 != 0 { - self.serial.set_clock_master(true); - println!("Set as master"); - } else if value & 0x01 != 0 { - self.serial.set_clock_master(false); - println!("Set as slave"); - } - self.serial_control = value; + self.serial.write_control(value); } 0x04 => { self.div = 0; diff --git a/src/state.rs b/src/state.rs index 980cbad..2c54a44 100644 --- a/src/state.rs +++ b/src/state.rs @@ -167,15 +167,6 @@ pub struct Memory<S: Serial, A: Audio> { pub timer_enabled: bool, pub timer_speed: u8, - - pub serial_data: u8, - - pub serial_control: u8, -} - -mod serial_control_flags { - pub const CLOCK_SELECT: u8 = 0b1; - pub const TRANSFER_ENABLE: u8 = 0b10000000; } impl<S: Serial, A: Audio> Memory<S, A> { @@ -214,30 +205,11 @@ impl<S: Serial, A: Audio> Memory<S, A> { timer_enabled: false, timer_speed: 0, serial, - serial_control: 0, - serial_data: 0, } } - pub fn update_serial(&mut self) { - if self.serial.new_transfer() { - println!("TRANSFER ENABLED"); - self.serial_control |= serial_control_flags::TRANSFER_ENABLE; - } - - if self.serial.clock_master() { - self.serial_control |= serial_control_flags::CLOCK_SELECT; - } else { - self.serial_control &= !serial_control_flags::CLOCK_SELECT; - } - - if (self.serial_control & serial_control_flags::CLOCK_SELECT != 0 - || self.serial.new_transfer()) - && self.serial_control & serial_control_flags::TRANSFER_ENABLE != 0 - { - self.serial.write(self.serial_data); - self.serial_data = self.serial.read(); - self.serial_control &= !serial_control_flags::TRANSFER_ENABLE; + pub fn update_serial(&mut self, cycles: u128) { + if self.serial.update_serial(cycles) { self.io[0x0f] |= 0b1000; } } |