No longer use setjmp/longjmp to ensure things are atomic
This commit is contained in:
parent
3aaebeaf3e
commit
f7b2320138
949
src/cpu.rs
949
src/cpu.rs
File diff suppressed because it is too large
Load Diff
15
src/main.rs
15
src/main.rs
|
@ -7,11 +7,10 @@ pub mod cpu;
|
||||||
pub mod keyboard;
|
pub mod keyboard;
|
||||||
pub mod mouse;
|
pub mod mouse;
|
||||||
pub mod disk;
|
pub mod disk;
|
||||||
pub mod setjmp;
|
|
||||||
|
|
||||||
use audio::AudioChannel;
|
use audio::AudioChannel;
|
||||||
use bus::Bus;
|
use bus::Bus;
|
||||||
use cpu::{Cpu, Interrupt};
|
use cpu::{Cpu, Exception, Interrupt};
|
||||||
use keyboard::Keyboard;
|
use keyboard::Keyboard;
|
||||||
use mouse::Mouse;
|
use mouse::Mouse;
|
||||||
use disk::DiskController;
|
use disk::DiskController;
|
||||||
|
@ -86,9 +85,9 @@ fn main() {
|
||||||
let audio_channel_2 = Arc::new(Mutex::new(AudioChannel::new(2)));
|
let audio_channel_2 = Arc::new(Mutex::new(AudioChannel::new(2)));
|
||||||
let audio_channel_3 = Arc::new(Mutex::new(AudioChannel::new(3)));
|
let audio_channel_3 = Arc::new(Mutex::new(AudioChannel::new(3)));
|
||||||
|
|
||||||
//let (exception_sender, exception_receiver) = mpsc::channel::<Exception>();
|
let (exception_sender, exception_receiver) = mpsc::channel::<Exception>();
|
||||||
|
|
||||||
let memory = Memory::new(read_rom().as_slice());
|
let memory = Memory::new(read_rom().as_slice(), exception_sender);
|
||||||
let mut bus = Bus {
|
let mut bus = Bus {
|
||||||
memory: memory.clone(),
|
memory: memory.clone(),
|
||||||
audio_channel_0: audio_channel_0.clone(),
|
audio_channel_0: audio_channel_0.clone(),
|
||||||
|
@ -159,16 +158,14 @@ fn main() {
|
||||||
builder.spawn({
|
builder.spawn({
|
||||||
move || {
|
move || {
|
||||||
loop {
|
loop {
|
||||||
if let Some(exception) = unsafe { cpu.setjmp() } {
|
|
||||||
cpu.interrupt(Interrupt::Exception(exception));
|
|
||||||
}
|
|
||||||
while !cpu.halted {
|
while !cpu.halted {
|
||||||
/*if let Ok(exception) = exception_receiver.try_recv() {
|
if let Ok(exception) = exception_receiver.try_recv() {
|
||||||
cpu.interrupt(Interrupt::Exception(exception));
|
cpu.interrupt(Interrupt::Exception(exception));
|
||||||
}*/
|
} else {
|
||||||
if let Ok(interrupt) = interrupt_receiver.try_recv() {
|
if let Ok(interrupt) = interrupt_receiver.try_recv() {
|
||||||
cpu.interrupt(interrupt);
|
cpu.interrupt(interrupt);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
cpu.execute_memory_instruction();
|
cpu.execute_memory_instruction();
|
||||||
if let Ok(_) = exit_receiver.try_recv() {
|
if let Ok(_) = exit_receiver.try_recv() {
|
||||||
// the rest of the VM has exited, stop the CPU thread
|
// the rest of the VM has exited, stop the CPU thread
|
||||||
|
|
|
@ -2,13 +2,13 @@
|
||||||
|
|
||||||
use crate::error;
|
use crate::error;
|
||||||
use crate::cpu::Exception;
|
use crate::cpu::Exception;
|
||||||
use crate::setjmp::{JumpEnv, longjmp};
|
|
||||||
|
|
||||||
use std::cell::UnsafeCell;
|
use std::cell::UnsafeCell;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
|
use std::sync::mpsc::Sender;
|
||||||
|
|
||||||
pub const MEMORY_RAM_SIZE: usize = 0x04000000; // 64 MiB
|
pub const MEMORY_RAM_SIZE: usize = 0x04000000; // 64 MiB
|
||||||
pub const MEMORY_ROM_SIZE: usize = 0x00080000; // 512 KiB
|
pub const MEMORY_ROM_SIZE: usize = 0x00080000; // 512 KiB
|
||||||
|
@ -32,10 +32,11 @@ struct MemoryInner {
|
||||||
mmu_enabled: Box<bool>,
|
mmu_enabled: Box<bool>,
|
||||||
tlb: Box<HashMap<u32, MemoryPage>>,
|
tlb: Box<HashMap<u32, MemoryPage>>,
|
||||||
paging_directory_address: Box<u32>,
|
paging_directory_address: Box<u32>,
|
||||||
|
exception_sender: Sender<Exception>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MemoryInner {
|
impl MemoryInner {
|
||||||
pub fn new(rom: &[u8]) -> Self {
|
pub fn new(rom: &[u8], exception_sender: Sender<Exception>) -> Self {
|
||||||
let mut this = Self {
|
let mut this = Self {
|
||||||
// HACK: allocate directly on the heap to avoid a stack overflow
|
// HACK: allocate directly on the heap to avoid a stack overflow
|
||||||
// at runtime while trying to move around a 64MB array
|
// at runtime while trying to move around a 64MB array
|
||||||
|
@ -44,6 +45,7 @@ impl MemoryInner {
|
||||||
mmu_enabled: Box::from(false),
|
mmu_enabled: Box::from(false),
|
||||||
tlb: Box::from(HashMap::with_capacity(1024)),
|
tlb: Box::from(HashMap::with_capacity(1024)),
|
||||||
paging_directory_address: Box::from(0x00000000),
|
paging_directory_address: Box::from(0x00000000),
|
||||||
|
exception_sender,
|
||||||
};
|
};
|
||||||
this.rom.as_mut_slice().write(rom).expect("failed to copy ROM to memory");
|
this.rom.as_mut_slice().write(rom).expect("failed to copy ROM to memory");
|
||||||
this
|
this
|
||||||
|
@ -60,8 +62,8 @@ unsafe impl Send for Memory {}
|
||||||
unsafe impl Sync for Memory {}
|
unsafe impl Sync for Memory {}
|
||||||
|
|
||||||
impl Memory {
|
impl Memory {
|
||||||
pub fn new(rom: &[u8]) -> Self {
|
pub fn new(rom: &[u8], exception_sender: Sender<Exception>) -> Self {
|
||||||
Self(Arc::new(UnsafeCell::new(MemoryInner::new(rom))))
|
Self(Arc::new(UnsafeCell::new(MemoryInner::new(rom, exception_sender))))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn inner(&self) -> &mut MemoryInner {
|
fn inner(&self) -> &mut MemoryInner {
|
||||||
|
@ -73,6 +75,7 @@ impl Memory {
|
||||||
pub fn mmu_enabled(&self) -> &mut bool { &mut self.inner().mmu_enabled }
|
pub fn mmu_enabled(&self) -> &mut bool { &mut self.inner().mmu_enabled }
|
||||||
pub fn tlb(&self) -> &mut HashMap<u32, MemoryPage> { &mut self.inner().tlb }
|
pub fn tlb(&self) -> &mut HashMap<u32, MemoryPage> { &mut self.inner().tlb }
|
||||||
pub fn paging_directory_address(&self) -> &mut u32 { &mut self.inner().paging_directory_address }
|
pub fn paging_directory_address(&self) -> &mut u32 { &mut self.inner().paging_directory_address }
|
||||||
|
pub fn exception_sender(&self) -> &mut Sender<Exception> { &mut self.inner().exception_sender }
|
||||||
|
|
||||||
pub fn dump(&self) {
|
pub fn dump(&self) {
|
||||||
let mut file = File::create("memory.dump").expect("failed to open memory dump file");
|
let mut file = File::create("memory.dump").expect("failed to open memory dump file");
|
||||||
|
@ -212,26 +215,45 @@ impl Memory {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read_8(&mut self, onfault: &JumpEnv<Exception>, address: u32) -> u8 {
|
pub fn read_8(&mut self, address: u32) -> Option<u8> {
|
||||||
self.read_opt_8(address).unwrap_or_else(|| unsafe { longjmp(onfault, Exception::PageFaultRead(address)) })
|
let mut read_ok = true;
|
||||||
|
let value = self.read_opt_8(address).unwrap_or_else(|| { read_ok = false; 0 });
|
||||||
|
if read_ok {
|
||||||
|
Some(value)
|
||||||
|
} else {
|
||||||
|
self.exception_sender().send(Exception::PageFaultRead(address)).unwrap();
|
||||||
|
None
|
||||||
}
|
}
|
||||||
pub fn read_16(&mut self, onfault: &JumpEnv<Exception>, address: u32) -> u16 {
|
|
||||||
(self.read_opt_8(address).unwrap_or_else(|| unsafe { longjmp(onfault, Exception::PageFaultRead(address)) }) as u16) |
|
|
||||||
(self.read_opt_8(address + 1).unwrap_or_else(|| unsafe { longjmp(onfault, Exception::PageFaultRead(address + 1)) }) as u16) << 8
|
|
||||||
}
|
}
|
||||||
pub fn read_32(&mut self, onfault: &JumpEnv<Exception>, address: u32) -> u32 {
|
pub fn read_16(&mut self, address: u32) -> Option<u16> {
|
||||||
(self.read_opt_8(address).unwrap_or_else(|| unsafe { longjmp(onfault, Exception::PageFaultRead(address)) }) as u32) |
|
let mut read_ok = true;
|
||||||
(self.read_opt_8(address + 1).unwrap_or_else(|| unsafe { longjmp(onfault, Exception::PageFaultRead(address + 1)) }) as u32) << 8 |
|
let value = self.read_8(address).unwrap_or_else(|| { read_ok = false; 0 }) as u16 |
|
||||||
(self.read_opt_8(address + 2).unwrap_or_else(|| unsafe { longjmp(onfault, Exception::PageFaultRead(address + 2)) }) as u32) << 16 |
|
(self.read_8(address + 1).unwrap_or_else(|| { read_ok = false; 0 }) as u16) << 8;
|
||||||
(self.read_opt_8(address + 3).unwrap_or_else(|| unsafe { longjmp(onfault, Exception::PageFaultRead(address + 3)) }) as u32) << 24
|
if read_ok {
|
||||||
|
Some(value)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn read_32(&mut self, address: u32) -> Option<u32> {
|
||||||
|
let mut read_ok = true;
|
||||||
|
let value = self.read_8(address).unwrap_or_else(|| { read_ok = false; 0 }) as u32 |
|
||||||
|
(self.read_8(address + 1).unwrap_or_else(|| { read_ok = false; 0 }) as u32) << 8 |
|
||||||
|
(self.read_8(address + 2).unwrap_or_else(|| { read_ok = false; 0 }) as u32) << 16 |
|
||||||
|
(self.read_8(address + 3).unwrap_or_else(|| { read_ok = false; 0 }) as u32) << 24;
|
||||||
|
if read_ok {
|
||||||
|
Some(value)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write_8(&mut self, onfault: &JumpEnv<Exception>, mut address: u32, byte: u8) {
|
pub fn write_8(&mut self, mut address: u32, byte: u8) -> Option<()> {
|
||||||
let original_address = address;
|
let original_address = address;
|
||||||
let mut writable = true;
|
let mut writable = true;
|
||||||
if *self.mmu_enabled() {
|
if *self.mmu_enabled() {
|
||||||
(address, writable) = self.virtual_to_physical(address as u32).unwrap_or_else(|| {
|
(address, writable) = self.virtual_to_physical(address as u32).unwrap_or_else(|| {
|
||||||
unsafe { longjmp(onfault, Exception::PageFaultWrite(original_address)) }
|
(0, false)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -247,21 +269,33 @@ impl Memory {
|
||||||
*value = byte;
|
*value = byte;
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
unsafe { longjmp(onfault, Exception::PageFaultWrite(original_address)) }
|
self.exception_sender().send(Exception::PageFaultWrite(original_address)).unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Some(())
|
||||||
} else {
|
} else {
|
||||||
unsafe { longjmp(onfault, Exception::PageFaultWrite(original_address)) }
|
self.exception_sender().send(Exception::PageFaultWrite(original_address)).unwrap();
|
||||||
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn write_16(&mut self, onfault: &JumpEnv<Exception>, address: u32, half: u16) {
|
pub fn write_16(&mut self, address: u32, half: u16) -> Option<()> {
|
||||||
self.write_8(onfault, address, (half & 0x00FF) as u8);
|
let result_0 = self.write_8(address, (half & 0x00FF) as u8);
|
||||||
self.write_8(onfault, address + 1, (half >> 8) as u8);
|
let result_1 = self.write_8(address + 1, (half >> 8) as u8);
|
||||||
|
if let (Some(_), Some(_)) = (result_0, result_1) {
|
||||||
|
Some(())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn write_32(&mut self, address: u32, word: u32) -> Option<()> {
|
||||||
|
let result_0 = self.write_8(address, (word & 0x000000FF) as u8);
|
||||||
|
let result_1 = self.write_8(address + 1, ((word & 0x0000FF00) >> 8) as u8);
|
||||||
|
let result_2 = self.write_8(address + 2, ((word & 0x00FF0000) >> 16) as u8);
|
||||||
|
let result_3 = self.write_8(address + 3, ((word & 0xFF000000) >> 24) as u8);
|
||||||
|
if let (Some(_), Some(_), Some(_), Some(_)) = (result_0, result_1, result_2, result_3) {
|
||||||
|
Some(())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
}
|
}
|
||||||
pub fn write_32(&mut self, onfault: &JumpEnv<Exception>, address: u32, word: u32) {
|
|
||||||
self.write_8(onfault, address, (word & 0x000000FF) as u8);
|
|
||||||
self.write_8(onfault, address + 1, ((word & 0x0000FF00) >> 8) as u8);
|
|
||||||
self.write_8(onfault, address + 2, ((word & 0x00FF0000) >> 16) as u8);
|
|
||||||
self.write_8(onfault, address + 3, ((word & 0xFF000000) >> 24) as u8);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,56 +0,0 @@
|
||||||
// setjmp.rs
|
|
||||||
|
|
||||||
use std::os::raw::*;
|
|
||||||
|
|
||||||
mod internal {
|
|
||||||
use std::os::raw::*;
|
|
||||||
use std::cell::*;
|
|
||||||
|
|
||||||
extern "C" {
|
|
||||||
pub fn setjmp(env: *mut c_void) -> c_int;
|
|
||||||
pub fn longjmp(env: *mut c_void, status: c_int) -> !;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type JumpBuf = [u8; 512];
|
|
||||||
|
|
||||||
pub struct JumpEnv<T>{
|
|
||||||
buffer: Box<RefCell<JumpBuf>>,
|
|
||||||
value: Cell<Option<T>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> JumpEnv<T> {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
buffer: Box::new(RefCell::new([0u8; 512])),
|
|
||||||
value: Cell::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn buffer(&self) -> RefMut<JumpBuf> {
|
|
||||||
self.buffer.borrow_mut()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn value_set(&self, value: T) {
|
|
||||||
self.value.set(Some(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn value_take(&self) -> Option<T> {
|
|
||||||
self.value.take()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub use internal::JumpEnv;
|
|
||||||
|
|
||||||
pub unsafe fn setjmp<T>(env: &JumpEnv<T>) -> Option<T> {
|
|
||||||
if internal::setjmp(env.buffer().as_mut_ptr() as *mut c_void) != 0 {
|
|
||||||
env.value_take()
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub unsafe fn longjmp<T>(env: &JumpEnv<T>, value: T) -> ! {
|
|
||||||
env.value_set(value);
|
|
||||||
internal::longjmp(env.buffer().as_mut_ptr() as *mut c_void, 1)
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user