fox32: added fox32core support :3

This commit is contained in:
Lua MacDougall 2022-05-12 01:37:55 -07:00 committed by Ry
parent 83a5d14775
commit fd3b83a15b
10 changed files with 564 additions and 156 deletions

169
Cargo.lock generated
View File

@ -29,6 +29,15 @@ dependencies = [
"memchr",
]
[[package]]
name = "ansi_term"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
dependencies = [
"winapi",
]
[[package]]
name = "anyhow"
version = "1.0.53"
@ -168,12 +177,46 @@ dependencies = [
"system-deps",
]
[[package]]
name = "atty"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
dependencies = [
"hermit-abi",
"libc",
"winapi",
]
[[package]]
name = "autocfg"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
[[package]]
name = "bindgen"
version = "0.59.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2bd2a9a458e8f4304c52c43ebb0cfbd520289f8379a52e329a38afda99bf8eb8"
dependencies = [
"bitflags",
"cexpr",
"clang-sys",
"clap",
"env_logger",
"lazy_static",
"lazycell",
"log",
"peeking_take_while",
"proc-macro2",
"quote",
"regex",
"rustc-hash",
"shlex",
"which",
]
[[package]]
name = "bit-set"
version = "0.5.2"
@ -260,6 +303,15 @@ dependencies = [
"jobserver",
]
[[package]]
name = "cexpr"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766"
dependencies = [
"nom",
]
[[package]]
name = "cfg-expr"
version = "0.9.0"
@ -300,6 +352,32 @@ dependencies = [
"winapi",
]
[[package]]
name = "clang-sys"
version = "1.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4cc00842eed744b858222c4c9faf7243aafc6d33f92f96935263ef4d8a41ce21"
dependencies = [
"glob",
"libc",
"libloading",
]
[[package]]
name = "clap"
version = "2.34.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c"
dependencies = [
"ansi_term",
"atty",
"bitflags",
"strsim 0.8.0",
"textwrap",
"unicode-width",
"vec_map",
]
[[package]]
name = "cocoa"
version = "0.24.0"
@ -534,7 +612,7 @@ dependencies = [
"ident_case",
"proc-macro2",
"quote",
"strsim",
"strsim 0.10.0",
"syn",
]
@ -643,6 +721,19 @@ dependencies = [
"syn",
]
[[package]]
name = "env_logger"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b2cf0344971ee6c64c31be0d530793fba457d322dfec2810c453d0ef228f9c3"
dependencies = [
"atty",
"humantime",
"log",
"regex",
"termcolor",
]
[[package]]
name = "event-listener"
version = "2.5.2"
@ -735,6 +826,7 @@ name = "fox32"
version = "0.1.0"
dependencies = [
"anyhow",
"fox32core",
"image",
"log",
"pixels",
@ -745,6 +837,15 @@ dependencies = [
"winit_input_helper",
]
[[package]]
name = "fox32core"
version = "0.1.0"
dependencies = [
"bindgen",
"cc",
"libc",
]
[[package]]
name = "futures"
version = "0.3.19"
@ -959,6 +1060,12 @@ dependencies = [
"system-deps",
]
[[package]]
name = "glob"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
[[package]]
name = "glow"
version = "0.11.2"
@ -1090,6 +1197,12 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df"
[[package]]
name = "humantime"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
[[package]]
name = "ident_case"
version = "1.0.1"
@ -1219,6 +1332,12 @@ version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "lazycell"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
[[package]]
name = "lebe"
version = "0.5.1"
@ -1227,9 +1346,9 @@ checksum = "7efd1d698db0759e6ef11a7cd44407407399a910c774dd804c64c032da7826ff"
[[package]]
name = "libc"
version = "0.2.115"
version = "0.2.125"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a8d982fa7a96a000f6ec4cfe966de9703eccde29750df2bb8949da91b0e818d"
checksum = "5916d2ae698f6de9bfb891ad7a8d65c09d232dc58cc4ac433c7da3b2fd84bc2b"
[[package]]
name = "libgit2-sys"
@ -1673,6 +1792,12 @@ dependencies = [
"winapi",
]
[[package]]
name = "peeking_take_while"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099"
[[package]]
name = "percent-encoding"
version = "2.1.0"
@ -2058,6 +2183,12 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012"
[[package]]
name = "shlex"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3"
[[package]]
name = "slab"
version = "0.4.5"
@ -2133,6 +2264,12 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
[[package]]
name = "strsim"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
[[package]]
name = "strsim"
version = "0.10.0"
@ -2172,6 +2309,15 @@ dependencies = [
"winapi-util",
]
[[package]]
name = "textwrap"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
dependencies = [
"unicode-width",
]
[[package]]
name = "thiserror"
version = "1.0.30"
@ -2307,6 +2453,12 @@ version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
[[package]]
name = "vec_map"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
[[package]]
name = "vergen"
version = "6.0.2"
@ -2602,6 +2754,17 @@ dependencies = [
"bitflags",
]
[[package]]
name = "which"
version = "4.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c4fb54e6113b6a8772ee41c3404fb0301ac79604489467e0a9ce1f3e97c24ae"
dependencies = [
"either",
"lazy_static",
"libc",
]
[[package]]
name = "wide"
version = "0.6.5"

View File

@ -8,6 +8,7 @@ build = "build.rs"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
fox32core = { path = "../fox32core" }
image = "0.24"
log = "0.4"
pixels = "0.9.0"

View File

@ -1,15 +1,18 @@
// bus.rs
use crate::{DiskController, Keyboard, Memory, Mouse, Overlay};
use crate::{Memory, DiskController, Keyboard, Mouse, Overlay};
use std::io::{stdout, Write};
use std::sync::{Arc, Mutex};
use std::io::{Write, stdout};
pub struct Bus {
pub memory: Box<dyn Memory>,
pub disk_controller: DiskController,
pub keyboard: Arc<Mutex<Keyboard>>,
pub memory: Memory,
pub mouse: Arc<Mutex<Mouse>>,
pub overlays: Arc<Mutex<Vec<Overlay>>>,
}
@ -90,7 +93,7 @@ impl Bus {
panic!("invalid disk ID");
}
match &self.disk_controller.disk[id as usize] {
Some(disk) => disk.size as u32, // return size if this disk is inserted
Some(disk) => disk.size() as u32, // return size if this disk is inserted
None => 0, // return 0 if this disk is not inserted
}
}
@ -212,3 +215,14 @@ impl Bus {
}
}
}
impl fox32core::Bus for Bus {
fn io_read(&mut self, port: u32) -> Option<u32> {
Some(self.read_io(port))
}
fn io_write(&mut self, port: u32, value: u32) -> Option<()> {
self.write_io(port, value);
Some(())
}
}

View File

@ -2,13 +2,13 @@
use crate::memory::MemoryRam;
use std::fs::File;
use std::io::{Seek, SeekFrom, Read, Write};
use std::fs::File;
use rfd::FileDialog;
pub struct Disk {
file: File,
pub size: u64,
size: u64,
current_sector: u32,
}
@ -20,6 +20,10 @@ impl Disk {
current_sector: 0,
}
}
pub fn size(&self) -> u64 {
self.size
}
}
pub struct DiskController {
@ -29,11 +33,12 @@ pub struct DiskController {
impl DiskController {
pub fn new() -> Self {
DiskController {
Self {
disk: [None, None, None, None],
buffer_pointer: 0x00000000
}
}
pub fn select_file(&self) -> Option<File> {
let path = FileDialog::new()
.add_filter("Disk Image", &["img", "dsk"])
@ -43,31 +48,36 @@ impl DiskController {
.set_title(&format!("Select a file to insert"))
.pick_file();
match path {
Some(path) => Some(File::open(path).unwrap()),
Some(path) => Some(File::open(path).expect("failed to open disk image")),
None => None,
}
}
pub fn insert(&mut self, file: File, disk_id: u8) {
self.disk[disk_id as usize] = Some(Disk::new(file));
}
pub fn remove(&mut self, disk_id: u8) {
self.disk[disk_id as usize] = None;
}
pub fn get_size(&mut self, disk_id: u8) -> u64 {
let disk = self.disk[disk_id as usize].as_mut().expect("attempted to access unmounted disk");
disk.size
fn disk_mut(&mut self, disk_id: u8) -> &mut Disk {
self.disk[disk_id as usize].as_mut().expect("attempted to access unmounted disk")
}
pub fn set_current_sector(&mut self, disk_id: u8, sector: u32) {
let disk = self.disk[disk_id as usize].as_mut().expect("attempted to access unmounted disk");
disk.current_sector = sector;
disk.file.seek(SeekFrom::Start(sector as u64 * 512)).unwrap();
pub fn get_size(&mut self, disk_id: u8) -> u64 {
self.disk_mut(disk_id).size
}
pub fn get_current_sector(&mut self, disk_id: u8) -> u32 {
let disk = self.disk[disk_id as usize].as_mut().expect("attempted to access unmounted disk");
disk.current_sector
self.disk_mut(disk_id).current_sector
}
pub fn set_current_sector(&mut self, disk_id: u8, sector: u32) {
let disk = self.disk_mut(disk_id);
disk.current_sector = sector;
disk.file.seek(SeekFrom::Start(sector as u64 * 512)).expect("attempted to seek to sector beyond edge of disk");
}
pub fn read_into_memory(&mut self, disk_id: u8, ram: &mut MemoryRam) -> usize {
let disk = self.disk[disk_id as usize].as_mut().expect("attempted to access unmounted disk");
let disk = self.disk_mut(disk_id);
let mut temp_buffer = [0u8; 512];
let number_of_bytes_read = disk.file.read(&mut temp_buffer).unwrap();

View File

@ -17,12 +17,10 @@ impl Keyboard {
}
pub fn push(&mut self, scancode: u32) {
self.producer.push(scancode).unwrap_or_else(|_| {
warn("keyboard buffer full!");
});
self.producer.push(scancode).unwrap_or_else(|_| warn("keyboard buffer full!"));
}
pub fn pop(&mut self) -> u32 {
self.consumer.pop().unwrap_or_else(|| 0)
self.consumer.pop().unwrap_or_default()
}
}

View File

@ -1,23 +1,31 @@
// main.rs
pub mod memory;
pub mod bus;
pub mod cpu;
pub mod disk;
pub mod keyboard;
pub mod memory;
pub mod mouse;
pub mod disk;
pub mod runtime;
pub mod wrapped;
use bus::Bus;
use cpu::{Cpu, Interrupt};
use disk::DiskController;
use keyboard::Keyboard;
use memory::{MEMORY_RAM_START, MEMORY_ROM_START, Memory, MemoryRam};
use mouse::Mouse;
use disk::DiskController;
use memory::{MEMORY_RAM_START, MEMORY_ROM_START, MemoryRam, Memory, MemoryStub, MemoryBuffer};
use runtime::Runtime;
use wrapped::*;
use std::env;
use std::fs::{File, read};
use std::process::exit;
use std::io::Write;
use std::mem;
use std::ops::Deref;
use std::sync::{Arc, mpsc, Mutex};
use std::thread;
use std::process::exit;
use std::env;
use std::fs::{File, read};
use image;
use log::error;
@ -78,28 +86,60 @@ fn main() {
let keyboard = Arc::new(Mutex::new(Keyboard::new()));
let mouse = Arc::new(Mutex::new(Mouse::new()));
let memory = Memory::new(read_rom().as_slice());
let mut bus = Bus {
memory: Box::new(MemoryStub()),
disk_controller: DiskController::new(),
keyboard: keyboard.clone(),
mouse: mouse.clone(),
overlays: display.overlays.clone(),
};
if args.len() > 1 {
bus.disk_controller.insert(File::open(&args[1]).expect("failed to load provided disk image"), 0);
}
let (mut runtime, memory): (Box<dyn Runtime>, MemoryWrapped) = {
if env::var("FOX32_RUNTIME").unwrap_or_default() == "core" {
println!("Using \"fox32core\" runtime");
let bus_wrapped = BusWrapped::new(bus);
let state_wrapped = CoreWrapped::new(fox32core::State::new(bus_wrapped.clone()));
let state_memory_wrapped = MemoryWrapped::new(CoreMemoryWrapped::new(state_wrapped.clone()));
mem::drop(mem::replace(&mut bus_wrapped.deref().borrow_mut().memory, Box::new(state_memory_wrapped.clone())));
(Box::new(state_wrapped.clone()), state_memory_wrapped.clone())
} else {
println!("Using \"rust\" runtime");
let memory = MemoryWrapped::new(MemoryBuffer::new());
mem::drop(mem::replace(&mut bus.memory, Box::new(memory.clone())));
(Box::new(Cpu::new(bus)), memory)
}
};
memory.rom().as_mut_slice().write(read_rom().as_slice()).expect("failed to write ROM");
runtime.halted_set(false);
let memory_cpu = memory.clone();
let memory_eventloop = memory.clone();
let mut cpu = {
let ram_size = memory_cpu.ram().len();
let ram_bottom_address = MEMORY_RAM_START;
let ram_top_address = ram_bottom_address + ram_size - 1;
println!("RAM: {:.2} MiB mapped at {:#010X}-{:#010X}", ram_size / 1048576, ram_bottom_address, ram_top_address);
let ram_size = memory_cpu.ram().len();
let ram_bottom_address = MEMORY_RAM_START;
let ram_top_address = ram_bottom_address + ram_size - 1;
println!("RAM: {:.2} MiB mapped at {:#010X}-{:#010X}", ram_size / 1048576, ram_bottom_address, ram_top_address);
let rom_size = memory_cpu.rom().len();
let rom_bottom_address = MEMORY_ROM_START;
let rom_top_address = rom_bottom_address + rom_size - 1;
println!("ROM: {:.2} KiB mapped at {:#010X}-{:#010X}", rom_size / 1024, rom_bottom_address, rom_top_address);
let bus_keyboard = Arc::clone(&keyboard);
let bus_mouse = Arc::clone(&mouse);
let bus_overlays = Arc::clone(&display.overlays);
let disk_controller = DiskController::new();
let bus = Bus { disk_controller, keyboard: bus_keyboard, memory: memory_cpu, mouse: bus_mouse, overlays: bus_overlays };
Cpu::new(bus)
};
let rom_size = memory_cpu.rom().len();
let rom_bottom_address = MEMORY_ROM_START;
let rom_top_address = rom_bottom_address + rom_size - 1;
println!("ROM: {:.2} KiB mapped at {:#010X}-{:#010X}", rom_size / 1024, rom_bottom_address, rom_top_address);
let event_loop = EventLoop::new();
let mut input = WinitInputHelper::new();
@ -123,29 +163,26 @@ fn main() {
Pixels::new(WIDTH as u32, HEIGHT as u32, surface_texture).unwrap()
};
let (interrupt_sender, interrupt_receiver) = mpsc::channel::<Interrupt>();
let (interrupt_sender, interrupt_receiver) = mpsc::channel::<u16>();
let builder = thread::Builder::new().name("cpu".to_string());
builder.spawn({
move || {
if args.len() > 1 {
cpu.bus.disk_controller.insert(File::open(&args[1]).unwrap(), 0);
}
loop {
while !cpu.halted {
while !runtime.halted_get() {
if let Ok(interrupt) = interrupt_receiver.try_recv() {
cpu.interrupt(interrupt);
runtime.raise(interrupt);
}
cpu.execute_memory_instruction();
runtime.step();
}
if !cpu.interrupts_enabled {
if !runtime.interrupts_enabled_get() {
// the cpu was halted and interrupts are disabled
// at this point, the cpu is dead and cannot resume, break out of the loop
break;
}
if let Ok(interrupt) = interrupt_receiver.recv() {
cpu.halted = false;
cpu.interrupt(interrupt);
runtime.halted_set(false);
runtime.raise(interrupt);
} else {
// sender is closed, break
break;
@ -162,7 +199,7 @@ fn main() {
if let Event::MainEventsCleared = event {
// update internal state and request a redraw
match interrupt_sender.send(Interrupt::Request(0xFF)) { // vsync interrupt
match interrupt_sender.send(0xFF) { // vsync interrupt
Ok(_) => {},
Err(_) => {
*control_flow = ControlFlow::Exit;

View File

@ -1,7 +1,6 @@
// memory.rs
use crate::error;
use std::alloc;
use std::cell::UnsafeCell;
use std::sync::Arc;
use std::io::Write;
@ -16,103 +15,104 @@ pub const MEMORY_ROM_START: usize = 0xF0000000;
pub type MemoryRam = [u8; MEMORY_RAM_SIZE];
pub type MemoryRom = [u8; MEMORY_ROM_SIZE];
struct MemoryInner {
ram: Box<MemoryRam>,
rom: Box<MemoryRom>,
pub trait Memory: Send {
fn ram(&self) -> &mut MemoryRam;
fn rom(&self) -> &mut MemoryRom;
}
impl MemoryInner {
pub fn new(rom: &[u8]) -> Self {
let mut this = Self {
// HACK: allocate directly on the heap to avoid a stack overflow
// at runtime while trying to move around a 64MB array
ram: unsafe { Box::from_raw(Box::into_raw(vec![0u8; MEMORY_RAM_SIZE].into_boxed_slice()) as *mut MemoryRam) },
rom: unsafe { Box::from_raw(Box::into_raw(vec![0u8; MEMORY_ROM_SIZE].into_boxed_slice()) as *mut MemoryRom) },
};
this.rom.as_mut_slice().write(rom).expect("failed to copy ROM to memory");
this
}
}
#[derive(Clone)]
pub struct Memory(Arc<UnsafeCell<MemoryInner>>);
// SAFETY: once MemoryInner is initialzed, there is no way to modify the Box
// pointers it contains and it does not matter if contents of the byte
// arrays are corrupted
unsafe impl Send for Memory {}
unsafe impl Sync for Memory {}
impl Memory {
pub fn new(rom: &[u8]) -> Self {
Self(Arc::new(UnsafeCell::new(MemoryInner::new(rom))))
impl dyn Memory {
fn arrayslice<T, const ARRAY_LEN: usize, const SLICE_LEN: usize>(array: &mut [T; ARRAY_LEN], offset: usize) -> &mut [T; SLICE_LEN] {
assert!(ARRAY_LEN >= SLICE_LEN + offset, "attempted slice [{}..{}] of array with length {}", offset, offset + SLICE_LEN, ARRAY_LEN);
unsafe { &mut *array.as_mut_ptr().offset(offset as isize).cast() }
}
fn inner(&self) -> &mut MemoryInner {
unsafe { &mut *self.0.get() }
fn find<const S: usize>(&self, address: u32) -> Option<&mut [u8; S]> {
let head = address as usize;
let tail = head.checked_add(S).expect("unexpected overflow computing tail address");
if head >= MEMORY_RAM_START && tail - MEMORY_RAM_START <= MEMORY_RAM_SIZE {
return Self::arrayslice(self.ram(), head - MEMORY_RAM_START).into()
}
if head >= MEMORY_ROM_START && tail - MEMORY_ROM_START <= MEMORY_ROM_SIZE {
return Self::arrayslice(self.rom(), head - MEMORY_ROM_START).into()
}
return None
}
fn find_unwrapped<const S: usize>(&self, address: u32) -> &mut [u8; S] {
self.find(address).unwrap_or_else(|| panic!("attempted to access unmapped memory at {:#010X}", address))
}
pub fn ram(&self) -> &mut MemoryRam { &mut self.inner().ram }
pub fn rom(&self) -> &mut MemoryRom { &mut self.inner().rom }
pub fn read_8(&self, address: u32) -> u8 { u8::from_le_bytes(*self.find_unwrapped(address)) }
pub fn read_16(&self, address: u32) -> u16 { u16::from_le_bytes(*self.find_unwrapped(address)) }
pub fn read_32(&self, address: u32) -> u32 { u32::from_le_bytes(*self.find_unwrapped(address)) }
pub fn write_8(&self, address: u32, value: u8) { *self.find_unwrapped(address) = u8::to_le_bytes(value) }
pub fn write_16(&self, address: u32, value: u16) { *self.find_unwrapped(address) = u16::to_le_bytes(value) }
pub fn write_32(&self, address: u32, value: u32) { *self.find_unwrapped(address) = u32::to_le_bytes(value) }
pub fn dump(&self) {
let mut file = File::create("memory.dump").expect("failed to open memory dump file");
file.write_all(self.ram()).expect("failed to write memory dump file");
}
pub fn read_8(&self, address: u32) -> u8 {
let address = address as usize;
let result = if address >= MEMORY_ROM_START && address < MEMORY_ROM_START + MEMORY_ROM_SIZE {
self.rom().get(address - MEMORY_ROM_START)
} else {
self.ram().get(address - MEMORY_RAM_START)
};
match result {
Some(value) => {
*value
}
None => {
error(&format!("attempting to read from unmapped memory address: {:#010X}", address));
}
}
}
pub fn read_16(&self, address: u32) -> u16 {
(self.read_8(address) as u16) |
(self.read_8(address + 1) as u16) << 8
}
pub fn read_32(&self, address: u32) -> u32 {
(self.read_8(address) as u32) |
(self.read_8(address + 1) as u32) << 8 |
(self.read_8(address + 2) as u32) << 16 |
(self.read_8(address + 3) as u32) << 24
}
pub fn write_8(&self, address: u32, byte: u8) {
let address = address as usize;
if address >= MEMORY_ROM_START && address < MEMORY_ROM_START + MEMORY_ROM_SIZE {
error(&format!("attempting to write to ROM address: {:#010X}", address));
}
match self.ram().get_mut(address - MEMORY_RAM_START) {
Some(value) => {
*value = byte;
}
None => {
error(&format!("attempting to write to unmapped memory address: {:#010X}", address));
}
}
}
pub fn write_16(&self, address: u32, half: u16) {
self.write_8(address, (half & 0x00FF) as u8);
self.write_8(address + 1, (half >> 8) as u8);
}
pub fn write_32(&self, address: u32, word: u32) {
self.write_8(address, (word & 0x000000FF) as u8);
self.write_8(address + 1, ((word & 0x0000FF00) >> 8) as u8);
self.write_8(address + 2, ((word & 0x00FF0000) >> 16) as u8);
self.write_8(address + 3, ((word & 0xFF000000) >> 24) as u8);
File::create("memory.dump")
.expect("failed to open memory dump file")
.write_all(self.ram())
.expect("failed to write memory dump file");
}
}
struct MemoryBufferInner {
ram: *mut MemoryRam,
rom: *mut MemoryRom,
}
static MEMORY_RAM_LAYOUT: alloc::Layout = alloc::Layout::new::<MemoryRam>();
static MEMORY_ROM_LAYOUT: alloc::Layout = alloc::Layout::new::<MemoryRom>();
impl Default for MemoryBufferInner {
fn default() -> Self {
unsafe {
let ram = alloc::alloc_zeroed(MEMORY_RAM_LAYOUT);
if ram.is_null() { alloc::handle_alloc_error(MEMORY_RAM_LAYOUT) }
let rom = alloc::alloc_zeroed(MEMORY_ROM_LAYOUT);
if rom.is_null() { alloc::handle_alloc_error(MEMORY_ROM_LAYOUT) }
Self { ram: ram.cast(), rom: rom.cast() }
}
}
}
impl Drop for MemoryBufferInner {
fn drop(&mut self) {
unsafe {
alloc::dealloc(self.ram.cast(), MEMORY_RAM_LAYOUT);
alloc::dealloc(self.rom.cast(), MEMORY_ROM_LAYOUT);
}
}
}
#[derive(Default, Clone)]
pub struct MemoryBuffer(Arc<UnsafeCell<MemoryBufferInner>>);
unsafe impl Send for MemoryBuffer {}
unsafe impl Sync for MemoryBuffer {}
impl MemoryBuffer {
pub fn new() -> Self {
Self::default()
}
fn inner(&self) -> &mut MemoryBufferInner {
unsafe { &mut *self.0.get() }
}
}
impl Memory for MemoryBuffer {
fn ram(&self) -> &mut MemoryRam { unsafe { &mut *self.inner().ram } }
fn rom(&self) -> &mut MemoryRom { unsafe { &mut *self.inner().rom } }
}
#[derive(Default, Clone, Copy)]
pub struct MemoryStub();
impl Memory for MemoryStub {
fn ram(&self) -> &mut MemoryRam { unimplemented!() }
fn rom(&self) -> &mut MemoryRom { unimplemented!() }
}

68
src/runtime.rs Normal file
View File

@ -0,0 +1,68 @@
// runtime.rs
pub trait Runtime: Send {
fn halted_get(&mut self) -> bool;
fn halted_set(&mut self, halted: bool);
fn interrupts_enabled_get(&mut self) -> bool;
fn interrupts_enabled_set(&mut self, interrupts_enabled: bool);
fn raise(&mut self, vector: u16);
fn step(&mut self);
}
impl Runtime for crate::Cpu {
fn halted_get(&mut self) -> bool {
self.halted
}
fn halted_set(&mut self, halted: bool) {
self.halted = halted
}
fn interrupts_enabled_get(&mut self) -> bool {
self.interrupts_enabled
}
fn interrupts_enabled_set(&mut self, interrupts_enabled: bool) {
self.interrupts_enabled = interrupts_enabled
}
fn raise(&mut self, vector: u16) {
self.interrupt(crate::Interrupt::Request(vector as u8));
}
fn step(&mut self) {
self.execute_memory_instruction();
}
}
impl Runtime for fox32core::State {
fn halted_get(&mut self) -> bool {
*self.halted()
}
fn halted_set(&mut self, halted: bool) {
*self.halted() = halted;
}
fn interrupts_enabled_get(&mut self) -> bool {
*self.interrupts_enabled()
}
fn interrupts_enabled_set(&mut self, interrupts_enabled: bool) {
*self.interrupts_enabled() = interrupts_enabled;
}
fn raise(&mut self, vector: u16) {
match fox32core::State::raise(self, vector) {
Some(fox32core::Error::InterruptsDisabled) | None => {}
Some(error) => {
panic!("fox32core failed to raise interrupt {:#06X}: {}", vector, error);
}
}
}
fn step(&mut self) {
if let Some(error) = fox32core::State::step(self) {
panic!("fox32core failed to execute next instruction: {}", error);
}
}
}

117
src/wrapped.rs Normal file
View File

@ -0,0 +1,117 @@
// wrapped.rs
use std::ops::Deref;
use std::cell::RefCell;
use std::cell::UnsafeCell;
use std::sync::Arc;
use crate::bus::Bus;
use crate::memory::*;
use crate::runtime::*;
#[derive(Clone)]
pub struct MemoryWrapped(Arc<dyn Memory>);
unsafe impl Send for MemoryWrapped {}
unsafe impl Sync for MemoryWrapped {}
impl MemoryWrapped {
pub fn new(memory: impl Memory + 'static) -> Self {
Self(Arc::new(memory))
}
}
impl Memory for MemoryWrapped {
fn ram(&self) -> &mut MemoryRam { self.0.ram() }
fn rom(&self) -> &mut MemoryRom { self.0.rom() }
}
impl Memory for fox32core::State {
fn ram(&self) -> &mut MemoryRam { self.memory_ram() }
fn rom(&self) -> &mut MemoryRom { self.memory_rom() }
}
#[derive(Clone)]
pub struct BusWrapped(Arc<RefCell<Bus>>);
impl BusWrapped {
pub fn new(bus: Bus) -> Self {
Self(Arc::new(RefCell::new(bus)))
}
}
impl Deref for BusWrapped {
type Target = RefCell<Bus>;
fn deref(&self) -> &Self::Target { &self.0 }
}
impl fox32core::Bus for BusWrapped {
fn io_read(&mut self, port: u32) -> Option<u32> {
self.borrow_mut().io_read(port)
}
fn io_write(&mut self, port: u32, value: u32) -> Option<()> {
self.borrow_mut().io_write(port, value)
}
}
#[derive(Clone)]
pub struct CoreWrapped(Arc<UnsafeCell<fox32core::State>>);
unsafe impl Send for CoreWrapped {}
impl CoreWrapped {
pub fn new(state: fox32core::State) -> Self {
Self(Arc::new(UnsafeCell::new(state)))
}
fn inner(&self) -> *mut fox32core::State {
self.0.get()
}
}
impl Memory for CoreWrapped {
fn ram(&self) -> &mut MemoryRam { unsafe { (*self.inner()).ram() } }
fn rom(&self) -> &mut MemoryRom { unsafe { (*self.inner()).rom() } }
}
impl Runtime for CoreWrapped {
fn halted_get(&mut self) -> bool {
unsafe { (*self.inner()).halted_get() }
}
fn halted_set(&mut self, halted: bool) {
unsafe { (*self.inner()).halted_set(halted) }
}
fn interrupts_enabled_get(&mut self) -> bool {
unsafe { (*self.inner()).interrupts_enabled_get() }
}
fn interrupts_enabled_set(&mut self, interrupts_enabled: bool) {
unsafe { (*self.inner()).interrupts_enabled_set(interrupts_enabled) }
}
fn raise(&mut self, vector: u16) {
unsafe { Runtime::raise(&mut *self.inner(), vector) }
}
fn step(&mut self) {
unsafe { Runtime::step(&mut *self.inner()) }
}
}
#[derive(Clone)]
pub struct CoreMemoryWrapped(CoreWrapped);
unsafe impl Send for CoreMemoryWrapped {}
unsafe impl Sync for CoreMemoryWrapped {}
impl CoreMemoryWrapped {
pub fn new(state_wrapped: CoreWrapped) -> Self {
Self(state_wrapped)
}
}
impl Memory for CoreMemoryWrapped {
fn ram(&self) -> &mut MemoryRam { self.0.ram() }
fn rom(&self) -> &mut MemoryRom { self.0.rom() }
}