aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAstatin <[email protected]>2025-06-29 00:32:41 +0200
committerAstatin <[email protected]>2025-06-29 00:32:41 +0200
commit4cb95575feedda5e2006d706a9e85db6569043ca (patch)
tree15f89bc7d0845f8920237961bf9bdedee38ed8b7
parent00c5b36a37d9b7e8b7009b3a176397e5ce808cbd (diff)
Fix (rewrite ?) FIFO serial communication
-rw-r--r--src/desktop/serial.rs140
-rw-r--r--src/io.rs31
-rw-r--r--src/main.rs8
-rw-r--r--src/mmio.rs15
-rw-r--r--src/state.rs32
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
}
}
}
diff --git a/src/io.rs b/src/io.rs
index bd36b3a..4b7ca08 100644
--- a/src/io.rs
+++ b/src/io.rs
@@ -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;
}
}