fixed a bug in the emulator backend where it would try to reset the memory twice.
This commit is contained in:
@@ -68,7 +68,7 @@ impl EmulatorHandle {
|
||||
self.memory.read_byte(addr)
|
||||
}
|
||||
|
||||
pub fn write_byte(&mut self, addr: PhysAddr, val: u8) {
|
||||
pub unsafe fn write_byte(&mut self, addr: PhysAddr, val: u8) {
|
||||
self.memory.write_byte(addr, val);
|
||||
}
|
||||
|
||||
@@ -76,35 +76,62 @@ impl EmulatorHandle {
|
||||
self.memory.read_bytes(addr, size as usize).to_vec()
|
||||
}
|
||||
|
||||
pub fn mem_write(&mut self, addr: PhysAddr, data: &[u8]) {
|
||||
pub unsafe fn mem_write(&mut self, addr: PhysAddr, data: &[u8]) {
|
||||
self.memory.write_bytes(addr, data);
|
||||
}
|
||||
|
||||
|
||||
pub fn interrupt(&mut self, code: Interrupt) -> Result<(), DsaError> {
|
||||
self.state.interrupt_queue.send(code)
|
||||
.map_err(|_| DsaError::CommunicationError("interrupt failed".into()) )
|
||||
}
|
||||
|
||||
pub fn request_update(&mut self) {
|
||||
self.state.update_req.store(true, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
pub fn status(&self) -> RunningState {
|
||||
self.state.proc.load().running.clone()
|
||||
}
|
||||
|
||||
pub fn pause(&mut self) {
|
||||
self.state.paused.store(true, Ordering::Relaxed);
|
||||
pub fn clock(&self) -> usize {
|
||||
self.state.proc.load().clock
|
||||
}
|
||||
pub fn resume(&mut self) {
|
||||
self.state.paused.store(false, Ordering::Relaxed);
|
||||
pub fn time(&self) -> Duration {
|
||||
self.state.proc.load().time
|
||||
}
|
||||
|
||||
|
||||
/// Request that the emulator backend provide an updated copy of its current internal state to `state.proc`
|
||||
pub fn request_update(&mut self) {
|
||||
self.state.update_requested.store(true, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
/// Request that the emulator backend pauses it's current execution
|
||||
/// no-op if in the `Paused` state
|
||||
pub fn request_pause(&mut self) {
|
||||
self.state.pause_requested.store(true, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
pub fn when_paused(&mut self, f: impl FnOnce(&mut Self)) {
|
||||
self.request_pause();
|
||||
|
||||
while self.state.proc.load().running != RunningState::Paused {
|
||||
continue;
|
||||
}
|
||||
|
||||
f(self);
|
||||
}
|
||||
|
||||
/// Request that the emulator backend resumes it's current execution
|
||||
/// no-op if not in the `Paused` state
|
||||
pub fn request_resume(&mut self) {
|
||||
self.state.pause_requested.store(false, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
/// Request that the emulator backend performs a full reset, clearing all memory, registers and other internal state.
|
||||
pub fn reset(&mut self) {
|
||||
self.state.resetting.store(true, Ordering::Relaxed);
|
||||
self.memory.clear();
|
||||
self.state.proc.store(Arc::new(Default::default()));
|
||||
self.state.resetting.store(false, Ordering::Relaxed);
|
||||
self.state.reset_requested.store(true, Ordering::Relaxed);
|
||||
|
||||
// wait until the reset is completed - this should be extremely quick.
|
||||
while self.state.reset_requested.load(Ordering::Relaxed) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_register(&self, rg: Register) -> u32 {
|
||||
@@ -114,14 +141,6 @@ impl EmulatorHandle {
|
||||
pub fn registers(&self) -> [u32; Register::COUNT as usize] {
|
||||
self.state.proc.load().registers.clone()
|
||||
}
|
||||
|
||||
pub fn clock(&self) -> usize {
|
||||
self.state.proc.load().clock
|
||||
}
|
||||
|
||||
pub fn time(&self) -> Duration {
|
||||
self.state.proc.load().time
|
||||
}
|
||||
}
|
||||
|
||||
enum InternalImplementation {
|
||||
|
||||
@@ -38,7 +38,7 @@ impl DsaImplementation for Emulator {
|
||||
|
||||
fn run(&mut self) -> Result<(), DsaError> {
|
||||
// wait for UI to initialise.
|
||||
if self.shared_state.paused.load(Ordering::Relaxed) {
|
||||
if self.shared_state.pause_requested.load(Ordering::Relaxed) {
|
||||
self.run_state_paused(Running)
|
||||
}
|
||||
|
||||
@@ -57,14 +57,17 @@ impl DsaImplementation for Emulator {
|
||||
// Responsibility: keep the UI thread up to date periodically
|
||||
// Update UI thread (roughly every 512k cycles targeting 60 UPS)
|
||||
if unlikely(self.core.internal_state.clock & 0x7FFFF == 0) {
|
||||
if self.shared_state.update_req.load(Ordering::Relaxed) {
|
||||
if self.shared_state.update_requested.load(Ordering::Relaxed) {
|
||||
self.core.internal_state.time = self.time.elapsed();
|
||||
self.share_state();
|
||||
|
||||
// Acknowledge update completed
|
||||
self.shared_state.update_requested.store(false, Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
// Transition: Paused
|
||||
if unlikely(self.shared_state.paused.load(Ordering::Relaxed)) {
|
||||
if unlikely(self.shared_state.pause_requested.load(Ordering::Relaxed)) {
|
||||
self.run_state_paused(Running);
|
||||
}
|
||||
|
||||
@@ -79,35 +82,29 @@ impl DsaImplementation for Emulator {
|
||||
// Responsibility: run fetch-decode-execute cycle.
|
||||
let pc = self.core.reg(Register::Pcx);
|
||||
let instruction = self.core.mem_read_word(pc);
|
||||
//
|
||||
// if cfg!(debug_assertions) {
|
||||
// println!(
|
||||
// "Clock: {} Executing {:?} PCX: {}, reg: {:?}",
|
||||
// self.core.internal_state.clock,
|
||||
// Instruction(instruction),
|
||||
// pc,
|
||||
// self.core.internal_state.registers
|
||||
// );
|
||||
// thread::sleep(Duration::from_micros(10));
|
||||
// }
|
||||
|
||||
if cfg!(debug_assertions) {
|
||||
println!(
|
||||
"Clock: {} Executing {:?} PCX: {}, reg: {:?}",
|
||||
self.core.internal_state.clock,
|
||||
Instruction(instruction),
|
||||
pc,
|
||||
self.core.internal_state.registers
|
||||
);
|
||||
thread::sleep(Duration::from_micros(10));
|
||||
}
|
||||
|
||||
// Check if the previous instruction caused a fault.
|
||||
if let Some(fault) = self.pending_fault.take() {
|
||||
println!("WARN fault: {:?}", fault);
|
||||
self.core.interrupt(fault);
|
||||
}
|
||||
|
||||
self.core.mut_reg(Register::Pcx).add_assign(4);
|
||||
*self.core.mut_reg(Register::Pcx) = self.core.reg(Register::Pcx).wrapping_add(4);
|
||||
|
||||
match self.core.execute(Instruction(instruction)) {
|
||||
ExecutionResult::Continue => {},
|
||||
ExecutionResult::Halt => self.run_state_halted(Running),
|
||||
|
||||
ExecutionResult::Interrupt(interrupt) => {
|
||||
if cfg!(debug_assertions) {
|
||||
println!("Interrupt: {:?}", interrupt);
|
||||
println!("Interrupt while at address {:04X}", self.core.reg(Register::Pcx));
|
||||
}
|
||||
|
||||
self.core.interrupt(interrupt);
|
||||
|
||||
if cfg!(debug_assertions) {
|
||||
@@ -189,13 +186,16 @@ impl Emulator {
|
||||
}
|
||||
|
||||
// Transition: pause signal received
|
||||
if self.shared_state.paused.load(Ordering::Relaxed) {
|
||||
if self.shared_state.pause_requested.load(Ordering::Relaxed) {
|
||||
self.run_state_paused(Halted);
|
||||
}
|
||||
|
||||
// Responsibility: update UI when requested
|
||||
if self.shared_state.update_req.load(Ordering::Relaxed) == true {
|
||||
if self.shared_state.update_requested.load(Ordering::Relaxed) == true {
|
||||
self.share_state();
|
||||
|
||||
// Acknowledge update completed
|
||||
self.shared_state.update_requested.store(false, Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -214,21 +214,21 @@ impl Emulator {
|
||||
|
||||
loop {
|
||||
// Responsibility: update UI when requested
|
||||
if self.shared_state.update_req.load(Ordering::Relaxed) == true {
|
||||
if self.shared_state.update_requested.load(Ordering::Relaxed) == true {
|
||||
self.share_state();
|
||||
|
||||
// Acknowledge update completed
|
||||
self.shared_state.update_requested.store(false, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
// Responsibility: reset when requested by the UI
|
||||
if self.shared_state.resetting.load(Ordering::Relaxed) == true {
|
||||
if self.shared_state.reset_requested.load(Ordering::Relaxed) == true {
|
||||
self.reset();
|
||||
|
||||
while self.shared_state.resetting.load(Ordering::Relaxed) == true {
|
||||
thread::sleep(Duration::from_millis(100));
|
||||
}
|
||||
self.shared_state.reset_requested.store(false, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
// Transition: run signal received
|
||||
if self.shared_state.paused.load(Ordering::Relaxed) == false {
|
||||
if self.shared_state.pause_requested.load(Ordering::Relaxed) == false {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,21 +8,21 @@ use super::interrupts::Interrupt;
|
||||
pub struct SharedState {
|
||||
pub proc: ArcSwap<ProcessorSnapshot>,
|
||||
|
||||
pub resetting: AtomicBool,
|
||||
pub paused: AtomicBool,
|
||||
pub update_req: AtomicBool,
|
||||
pub reset_requested: AtomicBool,
|
||||
pub pause_requested: AtomicBool,
|
||||
pub update_requested: AtomicBool,
|
||||
pub interrupt_queue: mpsc::Sender<Interrupt>,
|
||||
}
|
||||
|
||||
impl SharedState {
|
||||
pub fn new(sender: mpsc::Sender<Interrupt>) -> Self {
|
||||
Self {
|
||||
resetting: AtomicBool::new(false),
|
||||
reset_requested: AtomicBool::new(false),
|
||||
proc: ArcSwap::new(Arc::new(ProcessorSnapshot::default())),
|
||||
|
||||
paused: AtomicBool::new(true),
|
||||
pause_requested: AtomicBool::new(true),
|
||||
interrupt_queue: sender,
|
||||
update_req: AtomicBool::new(false),
|
||||
update_requested: AtomicBool::new(false),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,13 +34,13 @@ impl Controller {
|
||||
match status {
|
||||
RunningState::Paused | RunningState::Breakpoint(_) => {
|
||||
if PaddedWidget::button("▶ Run").color(Color32::GREEN).ui(ui).clicked() {
|
||||
self.emulator.resume()
|
||||
self.emulator.request_resume()
|
||||
}
|
||||
},
|
||||
|
||||
RunningState::Running | RunningState::Halted => {
|
||||
if PaddedWidget::button("⏸ Pause").color(Color32::YELLOW).ui(ui).clicked() {
|
||||
self.emulator.pause()
|
||||
self.emulator.request_pause()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ impl Controller {
|
||||
status.clone().ui(ui, padding);
|
||||
|
||||
if PaddedWidget::button("⟳ Reset All").color(Color32::YELLOW).ui(ui).clicked() {
|
||||
self.emulator.reset();
|
||||
self.emulator.when_paused(|emu| emu.reset());
|
||||
}
|
||||
|
||||
// Step
|
||||
|
||||
@@ -223,7 +223,9 @@ impl DamnSimpleUI {
|
||||
}
|
||||
|
||||
Action::LoadBinary(DataSource::Data(exe)) => {
|
||||
self.emulator.get_handle().mem_write(0, &exe);
|
||||
self.emulator.get_handle().when_paused(|emu| unsafe {
|
||||
emu.mem_write(0, &exe);
|
||||
});
|
||||
}
|
||||
|
||||
Action::LoadBinary(DataSource::File(path)) => {
|
||||
@@ -239,7 +241,10 @@ impl DamnSimpleUI {
|
||||
return;
|
||||
};
|
||||
|
||||
self.emulator.get_handle().mem_write(0, &binary.to_memory_image())
|
||||
self.emulator.get_handle().when_paused(|emu| unsafe {
|
||||
emu.reset();
|
||||
emu.mem_write(0, &binary.to_memory_image());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -40,27 +40,37 @@ impl Serial {
|
||||
write_buffer.push_back(byte);
|
||||
}
|
||||
|
||||
let ser_out_valid = handle.read_byte(uart_base + 0);
|
||||
// Kernel => ui: valid flag in byte 0, data in byte 1
|
||||
let ser_out_valid = handle.read_byte(uart_base + 0);
|
||||
if ser_out_valid == 0x01 {
|
||||
let ser_out = handle.read_byte(uart_base + 1);
|
||||
let _ = tx.send(ser_out);
|
||||
handle.write_byte(uart_base, 0x00); // clear the whole word
|
||||
|
||||
unsafe {
|
||||
handle.write_byte(uart_base, 0x00); // clear the whole word
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Emulator => kernel: valid flag in byte 2, data in byte 3
|
||||
if !write_buffer.is_empty() {
|
||||
// Emulator => kernel: valid flag in byte 2, data in byte 3
|
||||
let ser_in_valid = handle.read_byte(uart_base + 2);
|
||||
if ser_in_valid == 0x00 {
|
||||
if let Some(byte) = write_buffer.pop_front() {
|
||||
handle.write_byte(uart_base + 3, byte);
|
||||
handle.write_byte(uart_base + 2, 0x01);
|
||||
|
||||
unsafe {
|
||||
handle.write_byte(uart_base + 3, byte);
|
||||
handle.write_byte(uart_base + 2, 0x01);
|
||||
}
|
||||
|
||||
// Presumably emulator thread has shut down by this point.
|
||||
if handle.interrupt(Interrupt::SerialIn).is_err() {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::thread::sleep(std::time::Duration::from_millis(2));
|
||||
|
||||
handle.interrupt(Interrupt::SerialIn).unwrap();
|
||||
}
|
||||
|
||||
std::thread::sleep(std::time::Duration::from_millis(1));
|
||||
@@ -101,7 +111,7 @@ impl TilePane for Serial {
|
||||
if ui.button("Send").clicked() {
|
||||
self.write_buff.push(0 as char);
|
||||
for byte in self.write_buff.bytes() {
|
||||
self.write_tx.send(byte).unwrap();
|
||||
let _ = self.write_tx.send(byte);
|
||||
}
|
||||
self.write_buff.clear();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user