-
Notifications
You must be signed in to change notification settings - Fork 3
/
buffered.rs
128 lines (108 loc) · 4.07 KB
/
buffered.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
//! The buffered display component offers a simple UTF-8 display system.
//! See [`BufferedDisplay`] for more details.
use std::str::{from_utf8, Utf8Error};
use lrvm::board::Bus;
use lrvm_tools::{
bytes::words_to_bytes,
exceptions::AuxHwException,
metadata::{DeviceMetadata, DisplayType},
};
pub type DecodedStr<'a> = Result<&'a str, (Utf8Error, &'a [u8])>;
/// The buffered display works with a buffer and a handler.
///
/// When it receives a write request, it writes it into the buffer unless the
/// write address is on its last word ; in this case, in interprets the word as:
///
/// * `0xAA`: display the buffer's content and clear it afterwards
/// * `0xBB`: display the buffer's content lossiliy (handles invalid UTF-8 characters) and clear it afterwards
/// * `0xFF`: clear the buffer's content
///
/// The buffer may contain invalid UTF-8 data. When a display request is received, the handler is called with the decoded UTF-8 string,
/// which is a result object handling either the valid UTF-8 string or a decoding error object with the faulty raw buffer's content.
pub struct BufferedDisplay {
buffer: Vec<u32>,
words: u32,
handler: Box<dyn FnMut(DecodedStr)>,
hw_id: u64,
}
impl BufferedDisplay {
/// Create a buffered display 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.
/// NOTE: The handler should not display a newline symbol at the end of the provided string.
pub fn new(
capacity: u32,
handler: Box<dyn FnMut(DecodedStr)>,
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 == 0 {
return Err("Buffered display's capacity cannot be 0");
}
if capacity % 4 != 0 {
return Err("Buffered display's capacity must be aligned");
}
let capacity = capacity / 4;
Ok(Self {
buffer: vec![0; (capacity - 1) as usize],
words: capacity - 1,
handler,
hw_id,
})
}
/// Create a print!-based buffered display component, converting invalid UTF-8 strings to lossy ones
pub fn new_print_lossy(capacity: u32, hw_id: u64) -> Result<Self, &'static str> {
Self::new(
capacity,
Box::new(|message| match message {
Ok(message) => print!("{}", message),
Err((_, bytes)) => print!("{}", String::from_utf8_lossy(bytes)),
}),
hw_id,
)
}
}
impl Bus for BufferedDisplay {
fn name(&self) -> &'static str {
"Buffered Display"
}
fn metadata(&self) -> [u32; 8] {
DeviceMetadata::new(
self.hw_id,
self.words * 4 + 4,
DisplayType::Buffered.into(),
None,
None,
)
.encode()
}
fn read(&mut self, _addr: u32, ex: &mut u16) -> u32 {
*ex = AuxHwException::MemoryNotReadable.into();
0
}
fn write(&mut self, addr: u32, word: u32, ex: &mut u16) {
let addr = addr / 4;
if addr < self.words {
self.buffer[addr as usize] = word;
return;
}
match word {
0xAA => {
let bytes = words_to_bytes(&self.buffer);
(self.handler)(from_utf8(&bytes).map_err(|err| (err, bytes.as_ref())))
}
0xBB => {
let bytes = words_to_bytes(&self.buffer);
(self.handler)(Ok(&String::from_utf8_lossy(&bytes)))
}
0xFF => self.reset(),
code => *ex = AuxHwException::UnknownOperation(code as u8).into(),
}
}
fn reset(&mut self) {
self.buffer = vec![0; self.buffer.len()];
}
}