From fd3b83a15b39a25d63a911a9b0a7e935333b4271 Mon Sep 17 00:00:00 2001 From: Lua MacDougall Date: Thu, 12 May 2022 01:37:55 -0700 Subject: [PATCH] fox32: added fox32core support :3 --- Cargo.lock | 169 +++++++++++++++++++++++++++++++++++++++++++- Cargo.toml | 1 + src/bus.rs | 22 ++++-- src/disk.rs | 40 +++++++---- src/keyboard.rs | 8 +-- src/main.rs | 109 ++++++++++++++++++---------- src/memory.rs | 184 ++++++++++++++++++++++++------------------------ src/mouse.rs | 2 +- src/runtime.rs | 68 ++++++++++++++++++ src/wrapped.rs | 117 ++++++++++++++++++++++++++++++ 10 files changed, 564 insertions(+), 156 deletions(-) create mode 100644 src/runtime.rs create mode 100644 src/wrapped.rs diff --git a/Cargo.lock b/Cargo.lock index 1a27790..648a035 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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" diff --git a/Cargo.toml b/Cargo.toml index a80c11e..4fb25ab 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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" diff --git a/src/bus.rs b/src/bus.rs index 8928672..d87da62 100644 --- a/src/bus.rs +++ b/src/bus.rs @@ -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, + pub disk_controller: DiskController, + pub keyboard: Arc>, - pub memory: Memory, pub mouse: Arc>, + pub overlays: Arc>>, } @@ -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 { + Some(self.read_io(port)) + } + + fn io_write(&mut self, port: u32, value: u32) -> Option<()> { + self.write_io(port, value); + Some(()) + } +} diff --git a/src/disk.rs b/src/disk.rs index ed0228b..a8ed301 100644 --- a/src/disk.rs +++ b/src/disk.rs @@ -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 { 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(); @@ -80,4 +90,4 @@ impl DiskController { let number_of_bytes_written = disk.file.write(ram.get(self.buffer_pointer..self.buffer_pointer+512).unwrap()).unwrap(); number_of_bytes_written } -} \ No newline at end of file +} diff --git a/src/keyboard.rs b/src/keyboard.rs index f2af456..f9ec083 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -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() } -} \ No newline at end of file +} diff --git a/src/main.rs b/src/main.rs index 1b7e895..63ec6a0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -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, 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::(); + let (interrupt_sender, interrupt_receiver) = mpsc::channel::(); 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; diff --git a/src/memory.rs b/src/memory.rs index 23ccd3d..d49d19a 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -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, - rom: Box, +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>); - -// 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(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(&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(&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::(); +static MEMORY_ROM_LAYOUT: alloc::Layout = alloc::Layout::new::(); + +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>); + +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!() } +} diff --git a/src/mouse.rs b/src/mouse.rs index f6846f7..7cc2b40 100644 --- a/src/mouse.rs +++ b/src/mouse.rs @@ -12,4 +12,4 @@ impl Mouse { pub fn new() -> Self { Mouse { x: 0, y: 0, clicked: false, released: false, held: false } } -} \ No newline at end of file +} diff --git a/src/runtime.rs b/src/runtime.rs new file mode 100644 index 0000000..d75190c --- /dev/null +++ b/src/runtime.rs @@ -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); + } + } +} diff --git a/src/wrapped.rs b/src/wrapped.rs new file mode 100644 index 0000000..49d1259 --- /dev/null +++ b/src/wrapped.rs @@ -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); + +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>); + +impl BusWrapped { + pub fn new(bus: Bus) -> Self { + Self(Arc::new(RefCell::new(bus))) + } +} + +impl Deref for BusWrapped { + type Target = RefCell; + fn deref(&self) -> &Self::Target { &self.0 } +} + +impl fox32core::Bus for BusWrapped { + fn io_read(&mut self, port: u32) -> Option { + 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>); + +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() } +} +