-
Notifications
You must be signed in to change notification settings - Fork 3
/
run.rs
145 lines (119 loc) · 3.87 KB
/
run.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
135
136
137
138
139
140
141
142
143
144
145
use std::fmt;
use lrvm::cpu::Cpu;
use crate::exceptions::NativeException;
use super::RunConfig;
/// State of the VM when exited
#[derive(Debug, Clone)]
pub struct StoppedState {
/// Cycles count when the VM stopped
pub cycles: u128,
/// The address the VM was stopped at
pub addr: u32,
/// If the VM was stopped due to an exception, contains the faulty exception
pub ex: Option<ExWithMode>,
}
/// Native exception, with mode
#[derive(Debug, Clone)]
pub struct ExWithMode {
/// The raw exception
pub raw: u32,
/// Did the exception occurred in supervisor mode?
pub sv_mode: bool,
/// Exception's code
pub code: u8,
/// Exception's eventual associated data
pub associated: u16,
}
/// Run a virtual machine until the CPU halt, eventually encounters an exception or reaches a given number of cycles.
pub fn run_vm(cpu: &mut Cpu, config: RunConfig) -> StoppedState {
// If the VM is stopped because of an exception, it will be put in here
let mut stop_ex = None;
// Address the CPU was at when the VM was stopped
let mut was_at = cpu.regs.pc;
// Run the VM until it halts
while !cpu.halted() {
// Ensure cycles limit isn't exceeded yet
if let Some(cycles_limit) = config.cycles_limit {
if cpu.cycles() > cycles_limit {
break;
}
}
// Update the current instruction address
was_at = cpu.regs.pc;
if config.print_cycles {
println!(
"[lrvm] Running cycle {:#010X} at address {:#010X}",
cpu.cycles(),
cpu.regs.pc
);
}
// Run the next instruction
cpu.next();
// Check if an exception occurred
if cpu.regs.et != 0 {
let exception_bytes = cpu.regs.et.to_be_bytes();
// Complete the exception with the mode it occurred in
let ex = ExWithMode {
raw: cpu.regs.et,
sv_mode: exception_bytes[0] != 0,
code: exception_bytes[1],
associated: u16::from_be_bytes([exception_bytes[2], exception_bytes[3]]),
};
if config.print_exceptions && !(config.halt_on_exception && config.print_finish) {
println!(
"[lrvm] At address {:#010X} - Exception occurred: {}",
was_at,
prettify_ex_with_mode(&ex)
);
}
if config.halt_on_exception {
stop_ex = Some(ex);
break;
}
}
}
let state = StoppedState {
cycles: cpu.cycles(),
addr: was_at,
ex: stop_ex,
};
if config.print_finish {
if config.newline_on_finish {
println!();
}
println!("[lrvm] {}", prettify_stop(&state));
}
state
}
/// Prettify an exception with mode
pub fn prettify_ex_with_mode(ex: &ExWithMode) -> String {
match NativeException::decode_parts(ex.code, Some(ex.associated)) {
Ok(ex) => format!("{}", ex),
Err(()) => "<invalid exception code or data>".to_string(),
}
}
/// Prettify a stop state
pub fn prettify_stop(state: &StoppedState) -> String {
let mut output = format!(
"Cycle {:#010X}: CPU halted at address {:#010X}",
state.cycles, state.addr
);
if let Some(ex) = &state.ex {
output.push_str(&format!(
" because of exception in {} mode: {}",
if ex.sv_mode { "supervisor" } else { "userland" },
prettify_ex_with_mode(ex)
));
}
output
}
impl fmt::Display for ExWithMode {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", prettify_ex_with_mode(self))
}
}
impl fmt::Display for StoppedState {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", prettify_stop(self))
}
}