-
Notifications
You must be signed in to change notification settings - Fork 3
/
sync_line.rs
134 lines (114 loc) · 4.14 KB
/
sync_line.rs
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
//! The synchronous line keyboard component offers a simple UTF-8 line reading system.
//! See [`SyncLineKeyboard`] for more details.
use lrvm::board::Bus;
use lrvm_tools::{
exceptions::AuxHwException,
metadata::{DeviceMetadata, KeyboardType},
};
/// The keyboard works with a buffer and a handler.
///
/// When it receives a read request, the data is read from the buffer.
///
/// Writing into the buffer is forbidden but writing to the last word of the component results in it interpreting the provided action code:
///
/// * `0xAA`: trigger a synchronous input and put the result in the buffer
/// * `0xFF`: clear the buffer's content
///
/// The buffer is guaranteed to contain valid UTF-8 data.
pub struct SyncLineKeyboard {
buffer: Vec<u32>,
words: u32,
handler: Box<dyn FnMut() -> String>,
hw_id: u64,
}
impl SyncLineKeyboard {
/// Create a synchronous keyboard component.
/// The provided capacity must be a multiple of 4, and 4 bytes will be substracted for handling the action code.
/// This means a capacity of 64 bytes will allow 60 bytes of data or 15 words.
/// Returns an error message if the capacity is 0, not a multiple or 4 bytes or too large for the running CPU architecture.
pub fn new(
capacity: u32,
handler: Box<dyn FnMut() -> String>,
hw_id: u64,
) -> Result<Self, &'static str> {
let _: usize = capacity.try_into().map_err(|_| {
"Display's buffer's capacity must not exceed your CPU architecture (e.g. 32-bit size)"
})?;
if capacity % 4 != 0 {
return Err("Synchronous keyboard's buffer capacity must be aligned");
}
if capacity == 0 {
return Err("Synchronous keyboard's buffer capacity cannot be 0");
}
let capacity = capacity / 4;
Ok(Self {
buffer: vec![0; (capacity - 1) as usize],
words: capacity - 1,
handler,
hw_id,
})
}
}
impl Bus for SyncLineKeyboard {
fn name(&self) -> &'static str {
"Synchronous Line Keyboard"
}
fn metadata(&self) -> [u32; 8] {
DeviceMetadata::new(
self.hw_id,
self.words * 4 + 4,
KeyboardType::ReadLineSynchronous.into(),
None,
None,
)
.encode()
}
fn read(&mut self, addr: u32, _ex: &mut u16) -> u32 {
let addr = addr / 4;
if addr == self.words {
0
} else {
self.buffer[addr as usize]
}
}
fn write(&mut self, addr: u32, word: u32, ex: &mut u16) {
if addr / 4 != self.words {
*ex = 0x31 << 8;
} else {
match word {
0xAA => {
let mut word = 0;
let mut byte_index = 0;
let mut pos = 0;
for byte in (self.handler)().bytes() {
word += (byte as u32) << ((3 - byte_index) * 8);
if byte_index == 3 {
if pos >= self.buffer.len() {
eprintln!("Warning: input is too long for synchronous keyboard's buffer (max. {} bytes)", self.words * 4);
return;
}
self.buffer[pos] = word;
pos += 1;
byte_index = 0;
word = 0;
} else {
byte_index += 1;
}
}
if byte_index > 0 {
if pos >= self.buffer.len() {
eprintln!("Warning: input is too long for synchronous keyboard's buffer (max. {} bytes)", self.words * 4);
return;
}
self.buffer[pos] = word;
}
}
0xFF => self.reset(),
code => *ex = AuxHwException::UnknownOperation(code as u8).into(),
}
}
}
fn reset(&mut self) {
self.buffer = vec![0; self.buffer.len()];
}
}