mizu_core/
serial.rs

1use crate::memory::{InterruptManager, InterruptType};
2use crate::GameBoyConfig;
3use bitflags::bitflags;
4use save_state::Savable;
5
6/// A [`GameBoy`](crate::GameBoy) custom serial device, which can be used to communicate
7/// between the GameBoy and any other device.
8///
9/// Check [`Printer`](crate::Printer) for a sample implementation of the
10/// [GameBoy printer](https://en.wikipedia.org/wiki/Game_Boy_Printer) device.
11pub trait SerialDevice {
12    /// A device implemnts this, when receiving a call from this function will
13    /// send a bit (return) and get a bit from the sender (`bit` argument)
14    fn exchange_bit_external_clock(&mut self, bit: bool) -> bool;
15}
16
17bitflags! {
18    #[derive(Savable)]
19    #[savable(bitflags)]
20    struct SerialControl: u8 {
21        const IN_TRANSFER  = 1 << 7;
22        const CLOCK_SPEED  = 1 << 1;
23        const CLOCK_SOURCE = 1 << 0;
24    }
25}
26
27impl SerialControl {
28    fn is_internal_clock(&self) -> bool {
29        self.contains(Self::CLOCK_SOURCE)
30    }
31
32    fn in_transfer(&self) -> bool {
33        self.contains(Self::IN_TRANSFER)
34    }
35
36    fn end_transfere(&mut self) {
37        self.set(Self::IN_TRANSFER, false);
38    }
39
40    /// Which bit in `internal_timer` should be examined to trigger a serial
41    /// clock, the clock is given on the falling (negative) edge
42    fn clock_bit(&self) -> u8 {
43        if self.contains(Self::CLOCK_SPEED) {
44            // Fast
45            1
46        } else {
47            // Normal
48            6
49        }
50    }
51}
52
53#[derive(Savable)]
54pub struct Serial {
55    serial_control: SerialControl,
56    transfere_data: u8,
57    bits_remaining: u8,
58    pub internal_timer: u8,
59    config: GameBoyConfig,
60}
61
62impl Serial {
63    pub fn new(config: GameBoyConfig) -> Self {
64        Self {
65            serial_control: SerialControl::from_bits_truncate(0),
66            transfere_data: 0,
67            bits_remaining: 0,
68            internal_timer: 2,
69            config,
70        }
71    }
72
73    pub fn new_skip_boot_rom(config: GameBoyConfig) -> Self {
74        Self {
75            // FIXME: the internal_timer is not constant for CGB games
76            //  This is done temporary for testing, as testing properly should
77            //  use the bootrom
78            internal_timer: if config.is_dmg { 0xF3 } else { 0 },
79            ..Self::new(config)
80        }
81    }
82
83    pub fn read_data(&self) -> u8 {
84        self.transfere_data
85    }
86
87    pub fn write_data(&mut self, data: u8) {
88        self.transfere_data = data
89    }
90
91    pub fn read_control(&self) -> u8 {
92        0x7E | self.serial_control.bits()
93    }
94
95    pub fn write_control(&mut self, mut data: u8) {
96        if self.config.is_dmg {
97            // The clock speed parameter is not available in DMG
98            data &= 0x81;
99        }
100
101        self.serial_control = SerialControl::from_bits_truncate(data);
102        // should start transfere
103        if self.serial_control.in_transfer() {
104            self.bits_remaining = 8;
105        }
106    }
107
108    /// Clocks the serial and will return `Some` if a bit should be sent,
109    /// the bus must call `receive_bit` afterwards if there is a device.
110    /// 1 is received automatically if `receive_bit` is not called
111    pub fn clock_for_bit<I: InterruptManager>(&mut self, interrupt: &mut I) -> Option<bool> {
112        let old_bit = (self.internal_timer >> self.serial_control.clock_bit()) & 1 == 1;
113        self.internal_timer = self.internal_timer.wrapping_add(1);
114        let new_bit = (self.internal_timer >> self.serial_control.clock_bit()) & 1 == 1;
115        let can_clock = old_bit && !new_bit;
116
117        if can_clock && self.bits_remaining > 0 && self.serial_control.is_internal_clock() {
118            let out = self.transfere_data & 0x80 != 0;
119            self.transfere_data = self.transfere_data.wrapping_shl(1);
120
121            // data received from the other side, 1 for now meaning its
122            // disconnected
123            self.transfere_data |= 1;
124
125            self.bits_remaining -= 1;
126
127            if self.bits_remaining == 0 {
128                self.serial_control.end_transfere();
129                interrupt.request_interrupt(InterruptType::Serial);
130            }
131
132            Some(out)
133        } else {
134            // transfere should not complete as there is no external clock
135            // support for now
136            //
137            // TODO: implement external transfere using interet or something
138            None
139        }
140    }
141
142    pub fn receive_bit(&mut self, bit: bool) {
143        // we cannot receive from this method unless we are the master clock
144        assert!(self.serial_control.is_internal_clock());
145
146        // clear lowest bit
147        self.transfere_data &= !1;
148        self.transfere_data |= bit as u8;
149    }
150}