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