virtual-machine 0.0.4 → 0.0.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/{chunk-V6I6APCK.mjs → chunk-H6WI4NRK.mjs} +122 -123
- package/build/cli.js +590 -610
- package/build/index.d.ts +5 -5
- package/build/index.js +117 -118
- package/build/index.mjs +4 -4
- package/build/{riscv_vm-QIJGD3E2.mjs → riscv_vm-MAZK3LFN.mjs} +1 -1
- package/package.json +12 -5
- package/.yarn/install-state.gz +0 -0
- package/.yarnrc.yml +0 -1
- package/Cargo.toml +0 -49
- package/build.sh +0 -25
- package/cli.ts +0 -268
- package/index.ts +0 -16
- package/src/bus.rs +0 -558
- package/src/clint.rs +0 -132
- package/src/console.rs +0 -83
- package/src/cpu.rs +0 -1913
- package/src/csr.rs +0 -67
- package/src/decoder.rs +0 -789
- package/src/dram.rs +0 -146
- package/src/emulator.rs +0 -603
- package/src/lib.rs +0 -249
- package/src/main.rs +0 -449
- package/src/mmu.rs +0 -331
- package/src/net.rs +0 -121
- package/src/net_webtransport.rs +0 -446
- package/src/plic.rs +0 -261
- package/src/uart.rs +0 -231
- package/src/virtio.rs +0 -1074
- package/tsconfig.json +0 -19
- package/tsup/index.ts +0 -79
- package/tsup/tsup.cli.ts +0 -8
- package/tsup/tsup.core.cjs.ts +0 -7
- package/tsup/tsup.core.esm.ts +0 -8
package/src/plic.rs
DELETED
|
@@ -1,261 +0,0 @@
|
|
|
1
|
-
use crate::dram::MemoryError;
|
|
2
|
-
|
|
3
|
-
pub const PLIC_BASE: u64 = 0x0C00_0000;
|
|
4
|
-
pub const PLIC_SIZE: u64 = 0x400_0000;
|
|
5
|
-
|
|
6
|
-
pub const UART_IRQ: u32 = 10;
|
|
7
|
-
pub const VIRTIO0_IRQ: u32 = 1;
|
|
8
|
-
|
|
9
|
-
const NUM_SOURCES: usize = 32;
|
|
10
|
-
const NUM_CONTEXTS: usize = 2; // 0 = M-mode hart0, 1 = S-mode hart0
|
|
11
|
-
|
|
12
|
-
pub struct Plic {
|
|
13
|
-
pub priority: [u32; NUM_SOURCES],
|
|
14
|
-
pub pending: u32, // Level-triggered mirror of device IRQ lines (bit per source)
|
|
15
|
-
pub enable: [u32; NUM_CONTEXTS],
|
|
16
|
-
pub threshold: [u32; NUM_CONTEXTS],
|
|
17
|
-
pub active: [u32; NUM_CONTEXTS], // Per-context in-flight IRQs (claimed but not completed)
|
|
18
|
-
pub debug: bool,
|
|
19
|
-
// Multi-context arrays enable SMP readiness while preserving single-hart behavior.
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
impl Plic {
|
|
23
|
-
pub fn new() -> Self {
|
|
24
|
-
Self {
|
|
25
|
-
priority: [0; NUM_SOURCES],
|
|
26
|
-
pending: 0,
|
|
27
|
-
enable: [0; NUM_CONTEXTS],
|
|
28
|
-
threshold: [0; NUM_CONTEXTS],
|
|
29
|
-
active: [0; NUM_CONTEXTS],
|
|
30
|
-
debug: false,
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
pub fn update_pending(&mut self, source: u32) {
|
|
35
|
-
// Backward compatibility helper: set as pending (edge → level).
|
|
36
|
-
// Bus.refresh_irqs() may later clear this if device line is low.
|
|
37
|
-
if source < 32 {
|
|
38
|
-
if self.debug {
|
|
39
|
-
eprintln!("[PLIC] Update Pending source={}", source);
|
|
40
|
-
}
|
|
41
|
-
self.pending |= 1 << source;
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
// New: level-triggered source line setter
|
|
46
|
-
pub fn set_source_level(&mut self, source: u32, level: bool) {
|
|
47
|
-
if source >= 32 {
|
|
48
|
-
return;
|
|
49
|
-
}
|
|
50
|
-
let was_pending = (self.pending & (1 << source)) != 0;
|
|
51
|
-
if level {
|
|
52
|
-
if self.debug && !was_pending {
|
|
53
|
-
eprintln!("[PLIC] IRQ Line High: source={} enable[0]=0x{:x} enable[1]=0x{:x} prio={}",
|
|
54
|
-
source, self.enable[0], self.enable[1], self.priority[source as usize]);
|
|
55
|
-
}
|
|
56
|
-
self.pending |= 1 << source;
|
|
57
|
-
} else {
|
|
58
|
-
self.pending &= !(1 << source);
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
pub fn load(&mut self, offset: u64, size: u64) -> Result<u64, MemoryError> {
|
|
63
|
-
if size != 4 {
|
|
64
|
-
return Ok(0);
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
// Priority registers: 0x000000 .. 0x0000FC (4 bytes each)
|
|
68
|
-
if offset < 0x001000 {
|
|
69
|
-
let idx = (offset >> 2) as usize;
|
|
70
|
-
if idx < NUM_SOURCES {
|
|
71
|
-
return Ok(self.priority[idx] as u64);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
// Pending bits: 0x001000
|
|
75
|
-
if offset == 0x001000 {
|
|
76
|
-
return Ok(self.pending as u64);
|
|
77
|
-
}
|
|
78
|
-
// Enable per context: 0x002000 + 0x80 * context
|
|
79
|
-
if offset >= 0x002000 && offset < 0x002000 + 0x80 * (NUM_CONTEXTS as u64) {
|
|
80
|
-
let ctx = ((offset - 0x002000) / 0x80) as usize;
|
|
81
|
-
let inner = (offset - 0x002000) % 0x80;
|
|
82
|
-
if ctx < NUM_CONTEXTS && inner == 0 {
|
|
83
|
-
return Ok(self.enable[ctx] as u64);
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
// Context registers: threshold @ 0x200000 + 0x1000 * ctx, claim @ +4
|
|
87
|
-
if offset >= 0x200000 {
|
|
88
|
-
let ctx = ((offset - 0x200000) / 0x1000) as usize;
|
|
89
|
-
if ctx < NUM_CONTEXTS {
|
|
90
|
-
let base = 0x200000 + (0x1000 * ctx as u64);
|
|
91
|
-
if offset == base {
|
|
92
|
-
return Ok(self.threshold[ctx] as u64);
|
|
93
|
-
}
|
|
94
|
-
if offset == base + 4 {
|
|
95
|
-
let claim = self.claim_interrupt_for(ctx);
|
|
96
|
-
if crate::plic::Plic::debug_trace() {
|
|
97
|
-
eprintln!("[PLIC] SCLAIM ctx={} -> {}", ctx, claim);
|
|
98
|
-
}
|
|
99
|
-
return Ok(claim as u64);
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
Ok(0)
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
fn debug_trace() -> bool {
|
|
108
|
-
// Helper to check if trace logging is enabled without importing log everywhere if not needed
|
|
109
|
-
// or just use std::env
|
|
110
|
-
std::env::var("RUST_LOG").map(|s| s.contains("trace")).unwrap_or(false)
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
pub fn store(&mut self, offset: u64, size: u64, value: u64) -> Result<(), MemoryError> {
|
|
114
|
-
if size != 4 {
|
|
115
|
-
return Ok(());
|
|
116
|
-
}
|
|
117
|
-
let val = value as u32;
|
|
118
|
-
|
|
119
|
-
// Priority
|
|
120
|
-
if offset < 0x001000 {
|
|
121
|
-
let idx = (offset >> 2) as usize;
|
|
122
|
-
if idx < NUM_SOURCES {
|
|
123
|
-
self.priority[idx] = val;
|
|
124
|
-
}
|
|
125
|
-
return Ok(());
|
|
126
|
-
}
|
|
127
|
-
// Pending is read-only to software
|
|
128
|
-
if offset == 0x001000 {
|
|
129
|
-
return Ok(());
|
|
130
|
-
}
|
|
131
|
-
// Enable per context
|
|
132
|
-
if offset >= 0x002000 && offset < 0x002000 + 0x80 * (NUM_CONTEXTS as u64) {
|
|
133
|
-
let ctx = ((offset - 0x002000) / 0x80) as usize;
|
|
134
|
-
let inner = (offset - 0x002000) % 0x80;
|
|
135
|
-
if ctx < NUM_CONTEXTS && inner == 0 {
|
|
136
|
-
self.enable[ctx] = val;
|
|
137
|
-
}
|
|
138
|
-
return Ok(());
|
|
139
|
-
}
|
|
140
|
-
// Threshold / Claim-Complete per context
|
|
141
|
-
if offset >= 0x200000 {
|
|
142
|
-
let ctx = ((offset - 0x200000) / 0x1000) as usize;
|
|
143
|
-
if ctx < NUM_CONTEXTS {
|
|
144
|
-
let base = 0x200000 + (0x1000 * ctx as u64);
|
|
145
|
-
if offset == base {
|
|
146
|
-
self.threshold[ctx] = val;
|
|
147
|
-
return Ok(());
|
|
148
|
-
}
|
|
149
|
-
if offset == base + 4 {
|
|
150
|
-
// Completion: value is the source ID to complete
|
|
151
|
-
let id = (val & 0xffff) as u32;
|
|
152
|
-
if id > 0 && (id as usize) < NUM_SOURCES {
|
|
153
|
-
// eprintln!("[PLIC] Completed IRQ {} for context {}", id, ctx);
|
|
154
|
-
self.active[ctx] &= !(1 << id);
|
|
155
|
-
}
|
|
156
|
-
return Ok(());
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
return Ok(());
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
Ok(())
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
fn eligible_for_context(&self, source: usize, ctx: usize) -> bool {
|
|
166
|
-
let pending = ((self.pending >> source) & 1) == 1;
|
|
167
|
-
let enabled = ((self.enable[ctx] >> source) & 1) == 1;
|
|
168
|
-
let over_threshold = self.priority[source] > self.threshold[ctx];
|
|
169
|
-
let not_active = ((self.active[ctx] >> source) & 1) == 0;
|
|
170
|
-
pending && enabled && over_threshold && not_active
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
pub fn claim_interrupt_for(&mut self, ctx: usize) -> u32 {
|
|
174
|
-
let mut max_prio = 0;
|
|
175
|
-
let mut max_id = 0;
|
|
176
|
-
|
|
177
|
-
for i in 1..NUM_SOURCES {
|
|
178
|
-
if self.eligible_for_context(i, ctx) {
|
|
179
|
-
let prio = self.priority[i];
|
|
180
|
-
if prio > max_prio {
|
|
181
|
-
max_prio = prio;
|
|
182
|
-
max_id = i as u32;
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
if max_id != 0 {
|
|
188
|
-
// eprintln!("[PLIC] Claimed IRQ {} for context {} (prio {})", max_id, ctx, max_prio);
|
|
189
|
-
// Mark in-flight for this context until completed.
|
|
190
|
-
self.active[ctx] |= 1 << max_id;
|
|
191
|
-
}
|
|
192
|
-
max_id
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
pub fn is_interrupt_pending(&self) -> bool {
|
|
196
|
-
// For current single-hart flow, report S-mode context (1) if available, else context 0.
|
|
197
|
-
let ctx = if NUM_CONTEXTS > 1 { 1 } else { 0 };
|
|
198
|
-
self.is_interrupt_pending_for(ctx)
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
pub fn is_interrupt_pending_for(&self, ctx: usize) -> bool {
|
|
202
|
-
if ctx >= NUM_CONTEXTS {
|
|
203
|
-
return false;
|
|
204
|
-
}
|
|
205
|
-
for i in 1..NUM_SOURCES {
|
|
206
|
-
if self.eligible_for_context(i, ctx) {
|
|
207
|
-
if self.debug {
|
|
208
|
-
eprintln!("[PLIC] Interrupt pending for ctx={} source={}", ctx, i);
|
|
209
|
-
}
|
|
210
|
-
return true;
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
// Debug: show why no interrupt
|
|
214
|
-
if self.debug && self.pending != 0 {
|
|
215
|
-
for i in 1..NUM_SOURCES {
|
|
216
|
-
let pending = ((self.pending >> i) & 1) == 1;
|
|
217
|
-
let enabled = ((self.enable[ctx] >> i) & 1) == 1;
|
|
218
|
-
let over_threshold = self.priority[i] > self.threshold[ctx];
|
|
219
|
-
let not_active = ((self.active[ctx] >> i) & 1) == 0;
|
|
220
|
-
if pending {
|
|
221
|
-
eprintln!("[PLIC] Source {} pending but not eligible for ctx={}: enabled={} over_threshold={} (prio={} > thresh={}) not_active={}",
|
|
222
|
-
i, ctx, enabled, over_threshold, self.priority[i], self.threshold[ctx], not_active);
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
false
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
#[cfg(test)]
|
|
231
|
-
mod tests {
|
|
232
|
-
use super::*;
|
|
233
|
-
|
|
234
|
-
#[test]
|
|
235
|
-
fn test_plic_claim_complete_context1() {
|
|
236
|
-
let mut plic = Plic::new();
|
|
237
|
-
// Priorities
|
|
238
|
-
plic.priority[1] = 5;
|
|
239
|
-
plic.priority[10] = 3;
|
|
240
|
-
// Enable sources 1 and 10 for context 1 (S-mode)
|
|
241
|
-
let enable_val = (1u32 << 1) | (1u32 << 10);
|
|
242
|
-
let _ = plic.store(0x002000 + 0x80 * 1, 4, enable_val as u64);
|
|
243
|
-
// Threshold 0 for context 1
|
|
244
|
-
let _ = plic.store(0x200000 + 0x1000 * 1, 4, 0);
|
|
245
|
-
// Assert device lines
|
|
246
|
-
plic.set_source_level(1, true);
|
|
247
|
-
plic.set_source_level(10, true);
|
|
248
|
-
|
|
249
|
-
// Claim highest priority first (source 1)
|
|
250
|
-
let id1 = plic.claim_interrupt_for(1);
|
|
251
|
-
assert_eq!(id1, 1);
|
|
252
|
-
// Next claim should return source 10 (since 1 is active)
|
|
253
|
-
let id2 = plic.claim_interrupt_for(1);
|
|
254
|
-
assert_eq!(id2, 10);
|
|
255
|
-
// Complete source 1
|
|
256
|
-
let _ = plic.store(0x200004 + 0x1000 * 1, 4, 1);
|
|
257
|
-
// Claim again should allow source 1
|
|
258
|
-
let id3 = plic.claim_interrupt_for(1);
|
|
259
|
-
assert_eq!(id3, 1);
|
|
260
|
-
}
|
|
261
|
-
}
|
package/src/uart.rs
DELETED
|
@@ -1,231 +0,0 @@
|
|
|
1
|
-
use crate::dram::MemoryError;
|
|
2
|
-
use std::collections::VecDeque;
|
|
3
|
-
|
|
4
|
-
pub const UART_BASE: u64 = 0x1000_0000;
|
|
5
|
-
pub const UART_SIZE: u64 = 0x100;
|
|
6
|
-
|
|
7
|
-
// Registers (offset)
|
|
8
|
-
const RBR: u64 = 0x00; // Receiver Buffer (Read)
|
|
9
|
-
const THR: u64 = 0x00; // Transmitter Holding (Write)
|
|
10
|
-
const IER: u64 = 0x01; // Interrupt Enable
|
|
11
|
-
const IIR: u64 = 0x02; // Interrupt Identity (Read)
|
|
12
|
-
const FCR: u64 = 0x02; // FIFO Control (Write)
|
|
13
|
-
const LCR: u64 = 0x03; // Line Control
|
|
14
|
-
const MCR: u64 = 0x04; // Modem Control
|
|
15
|
-
const LSR: u64 = 0x05; // Line Status
|
|
16
|
-
const MSR: u64 = 0x06; // Modem Status
|
|
17
|
-
const SCR: u64 = 0x07; // Scratch
|
|
18
|
-
|
|
19
|
-
pub struct Uart {
|
|
20
|
-
pub input: VecDeque<u8>,
|
|
21
|
-
pub output: VecDeque<u8>,
|
|
22
|
-
|
|
23
|
-
// Registers
|
|
24
|
-
pub ier: u8,
|
|
25
|
-
pub iir: u8,
|
|
26
|
-
pub fcr: u8,
|
|
27
|
-
pub lcr: u8,
|
|
28
|
-
pub mcr: u8,
|
|
29
|
-
pub lsr: u8,
|
|
30
|
-
pub msr: u8,
|
|
31
|
-
pub scr: u8,
|
|
32
|
-
|
|
33
|
-
// Divisor
|
|
34
|
-
pub dll: u8,
|
|
35
|
-
pub dlm: u8,
|
|
36
|
-
|
|
37
|
-
pub interrupting: bool,
|
|
38
|
-
|
|
39
|
-
/// Internal state to track if THRE interrupt is pending (waiting for IIR read or THR write).
|
|
40
|
-
/// This separates the "condition" (THR empty) from the "event" (Interrupt Pending).
|
|
41
|
-
thre_ip: bool,
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
impl Uart {
|
|
45
|
-
pub fn new() -> Self {
|
|
46
|
-
Self {
|
|
47
|
-
input: VecDeque::new(),
|
|
48
|
-
output: VecDeque::new(),
|
|
49
|
-
|
|
50
|
-
ier: 0x00,
|
|
51
|
-
iir: 0x01, // Default: no interrupt pending
|
|
52
|
-
fcr: 0x00,
|
|
53
|
-
lcr: 0x00,
|
|
54
|
-
mcr: 0x00,
|
|
55
|
-
lsr: 0x60, // Transmitter Empty (bit 5) | Transmitter Holding Register Empty (bit 6)
|
|
56
|
-
msr: 0x00,
|
|
57
|
-
scr: 0x00,
|
|
58
|
-
|
|
59
|
-
dll: 0x00,
|
|
60
|
-
dlm: 0x00,
|
|
61
|
-
|
|
62
|
-
interrupting: false,
|
|
63
|
-
thre_ip: true, // Starts empty, so initial state could be pending if enabled
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
pub fn update_interrupts(&mut self) {
|
|
68
|
-
self.interrupting = false;
|
|
69
|
-
self.iir = 0x01; // Default: no interrupt pending
|
|
70
|
-
|
|
71
|
-
// Priority 1: Receiver Line Status (not implemented extensively)
|
|
72
|
-
|
|
73
|
-
// Priority 2: Received Data Available
|
|
74
|
-
if (self.lsr & 0x01) != 0 && (self.ier & 0x01) != 0 {
|
|
75
|
-
self.interrupting = true;
|
|
76
|
-
self.iir = 0x04; // Recieved Data Available
|
|
77
|
-
return;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
// Priority 3: Transmitter Holding Register Empty
|
|
81
|
-
// Triggered if THR is empty AND IER bit 1 is set AND we haven't acknowledged it yet.
|
|
82
|
-
if self.thre_ip && (self.ier & 0x02) != 0 {
|
|
83
|
-
self.interrupting = true;
|
|
84
|
-
self.iir = 0x02; // THRE
|
|
85
|
-
return;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
// Priority 4: Modem Status (not implemented)
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
pub fn load(&mut self, offset: u64, size: u64) -> Result<u64, MemoryError> {
|
|
92
|
-
if size != 1 {
|
|
93
|
-
// Some OS might try 4-byte reads, technically not allowed by spec but we can be lenient or strict.
|
|
94
|
-
// Spec says 8-bit width. Let's return 0 for now if not byte access.
|
|
95
|
-
return Ok(0);
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
let val = match offset {
|
|
99
|
-
RBR => {
|
|
100
|
-
if (self.lcr & 0x80) != 0 {
|
|
101
|
-
self.dll
|
|
102
|
-
} else {
|
|
103
|
-
// RBR: Read from input FIFO
|
|
104
|
-
let byte = self.input.pop_front().unwrap_or(0);
|
|
105
|
-
// Update LSR: if more data, set bit 0, else clear it
|
|
106
|
-
if self.input.is_empty() {
|
|
107
|
-
self.lsr &= !0x01;
|
|
108
|
-
} else {
|
|
109
|
-
self.lsr |= 0x01;
|
|
110
|
-
}
|
|
111
|
-
self.update_interrupts();
|
|
112
|
-
byte
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
IER => {
|
|
116
|
-
if (self.lcr & 0x80) != 0 {
|
|
117
|
-
self.dlm
|
|
118
|
-
} else {
|
|
119
|
-
self.ier
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
IIR => {
|
|
123
|
-
let val = self.iir;
|
|
124
|
-
// Reading IIR clears THRE interrupt if it is the indicated interrupt
|
|
125
|
-
if (val & 0x0F) == 0x02 {
|
|
126
|
-
self.thre_ip = false;
|
|
127
|
-
self.update_interrupts();
|
|
128
|
-
log::trace!("[UART] IIR read cleared THRE ip");
|
|
129
|
-
} else {
|
|
130
|
-
log::trace!("[UART] IIR read val={:x} (thre_ip={})", val, self.thre_ip);
|
|
131
|
-
}
|
|
132
|
-
val
|
|
133
|
-
}
|
|
134
|
-
LCR => self.lcr,
|
|
135
|
-
MCR => self.mcr,
|
|
136
|
-
LSR => self.lsr,
|
|
137
|
-
MSR => self.msr,
|
|
138
|
-
SCR => self.scr,
|
|
139
|
-
_ => 0,
|
|
140
|
-
};
|
|
141
|
-
|
|
142
|
-
Ok(val as u64)
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
pub fn store(&mut self, offset: u64, size: u64, value: u64) -> Result<(), MemoryError> {
|
|
146
|
-
if size != 1 {
|
|
147
|
-
return Ok(());
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
let val = (value & 0xff) as u8;
|
|
151
|
-
|
|
152
|
-
match offset {
|
|
153
|
-
THR => {
|
|
154
|
-
if (self.lcr & 0x80) != 0 {
|
|
155
|
-
self.dll = val;
|
|
156
|
-
} else {
|
|
157
|
-
// THR: Write to output
|
|
158
|
-
log::trace!(
|
|
159
|
-
"[UART] TX '{}' (0x{:02x})",
|
|
160
|
-
if val.is_ascii_graphic() {
|
|
161
|
-
val as char
|
|
162
|
-
} else {
|
|
163
|
-
'.'
|
|
164
|
-
},
|
|
165
|
-
val
|
|
166
|
-
);
|
|
167
|
-
self.output.push_back(val);
|
|
168
|
-
// We instantly "transmit", so THRE (bit 5) is always set.
|
|
169
|
-
// Writing to THR clears the THRE interrupt (if pending),
|
|
170
|
-
// but since it becomes empty immediately, we set thre_ip to true again?
|
|
171
|
-
// In real HW, it goes Not Empty -> Empty.
|
|
172
|
-
// So we should clear it, then re-assert it.
|
|
173
|
-
// For edge-triggered emulation, simply re-asserting is correct because we transitioned.
|
|
174
|
-
self.lsr |= 0x20;
|
|
175
|
-
self.thre_ip = true;
|
|
176
|
-
self.update_interrupts();
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
IER => {
|
|
180
|
-
if (self.lcr & 0x80) != 0 {
|
|
181
|
-
self.dlm = val;
|
|
182
|
-
} else {
|
|
183
|
-
self.ier = val;
|
|
184
|
-
self.update_interrupts();
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
FCR => {
|
|
188
|
-
self.fcr = val;
|
|
189
|
-
if (self.fcr & 0x02) != 0 {
|
|
190
|
-
self.input.clear();
|
|
191
|
-
self.lsr &= !0x01;
|
|
192
|
-
}
|
|
193
|
-
if (self.fcr & 0x04) != 0 {
|
|
194
|
-
self.output.clear();
|
|
195
|
-
self.lsr |= 0x60; // Empty
|
|
196
|
-
}
|
|
197
|
-
self.update_interrupts();
|
|
198
|
-
}
|
|
199
|
-
LCR => {
|
|
200
|
-
self.lcr = val;
|
|
201
|
-
}
|
|
202
|
-
MCR => {
|
|
203
|
-
self.mcr = val;
|
|
204
|
-
}
|
|
205
|
-
LSR => {
|
|
206
|
-
// Usually read-only, but factory test mode might write. Ignore.
|
|
207
|
-
}
|
|
208
|
-
MSR => {
|
|
209
|
-
// Read-only.
|
|
210
|
-
}
|
|
211
|
-
SCR => {
|
|
212
|
-
self.scr = val;
|
|
213
|
-
}
|
|
214
|
-
_ => {}
|
|
215
|
-
}
|
|
216
|
-
Ok(())
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
// Interface for the Host
|
|
220
|
-
pub fn push_input(&mut self, byte: u8) {
|
|
221
|
-
self.input.push_back(byte);
|
|
222
|
-
self.lsr |= 0x01; // Data Ready
|
|
223
|
-
self.update_interrupts();
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
pub fn pop_output(&mut self) -> Option<u8> {
|
|
227
|
-
self.output.pop_front()
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
|