fox32: added fox32core support :3
This commit is contained in:
parent
83a5d14775
commit
fd3b83a15b
169
Cargo.lock
generated
169
Cargo.lock
generated
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
22
src/bus.rs
22
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<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(())
|
||||
}
|
||||
}
|
||||
|
|
38
src/disk.rs
38
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<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();
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
109
src/main.rs
109
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<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;
|
||||
|
|
184
src/memory.rs
184
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<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
68
src/runtime.rs
Normal 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
117
src/wrapped.rs
Normal 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() }
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user