fox32: Implement a very basic disk controller

Any file type can be mounted as a disk, as long as the code running
inside fox32 can understand its data.
This commit is contained in:
ry755 2022-01-29 23:17:51 -08:00 committed by Ry
parent 4f278bc58b
commit b957b58fb2
5 changed files with 961 additions and 4 deletions

810
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -10,6 +10,7 @@ build = "build.rs"
[dependencies]
log = "0.4"
pixels = "0.9.0"
rfd = "0.7.0"
rodio = "0.14.0"
winit = "0.26"
winit_input_helper = "0.11"

View File

@ -1,6 +1,6 @@
// bus.rs
use crate::{Memory, Mouse};
use crate::{DiskController, Memory, Mouse};
use std::convert::TryInto;
use std::io::{stdout, Write};
@ -10,6 +10,7 @@ use std::thread;
use rodio::{OutputStream, buffer::SamplesBuffer, Sink};
pub struct Bus {
pub disk_controller: DiskController,
pub memory: Memory,
pub mouse: Arc<Mutex<Mouse>>,
}
@ -74,6 +75,31 @@ impl Bus {
_ => panic!("invalid mouse control port"),
}
}
0x80001000..=0x80002200 => { // disk controller port
let address_or_id = (port & 0x00000FFF) as usize;
let operation = (port & 0x0000F000) >> 8;
match operation {
0x10 => {
// we're reading the current mount state of the specified disk id
if address_or_id > 3 {
panic!("invalid disk ID");
}
match &self.disk_controller.disk[address_or_id] {
Some(disk) => disk.size as u32, // return size if this disk is mounted
None => 0, // return 0 if this disk is not mounted
}
}
0x20 => {
// we're reading from the sector buffer
if address_or_id > 512 {
panic!("attempted to read past the end of the disk controller sector buffer");
}
self.disk_controller.sector_buffer[address_or_id] as u32
}
_ => panic!("invalid disk controller port"),
}
}
_ => 0,
}
}
@ -160,6 +186,48 @@ impl Bus {
_ => panic!("invalid mouse control port"),
}
}
0x80001000..=0x80004200 => { // disk controller port
let address_or_id = (port & 0x00000FFF) as usize;
let operation = (port & 0x0000F000) >> 8;
match operation {
0x10 => {
// we're requesting a disk to be mounted to the specified disk id
if address_or_id > 3 {
panic!("invalid disk ID");
}
let file = self.disk_controller.select_file();
match file {
Some(file) => self.disk_controller.mount(file, address_or_id as u8),
None => {},
};
}
0x20 => {
// we're writing to the sector buffer
if address_or_id > 512 {
panic!("attempted to read past the end of the disk controller sector buffer");
}
self.disk_controller.sector_buffer[address_or_id] = word as u8;
}
0x30 => {
// we're reading the specified sector of the specified disk id into the sector buffer
if address_or_id > 3 {
panic!("invalid disk ID");
}
self.disk_controller.set_current_sector(address_or_id as u8, word);
self.disk_controller.read_into_buffer(address_or_id as u8);
}
0x40 => {
// we're writing the specified sector to the specified disk id from the sector buffer
if address_or_id > 3 {
panic!("invalid disk ID");
}
self.disk_controller.set_current_sector(address_or_id as u8, word);
self.disk_controller.write_from_buffer(address_or_id as u8);
}
_ => panic!("invalid disk controller port"),
}
}
_ => return,
}
}

79
src/disk.rs Normal file
View File

@ -0,0 +1,79 @@
// disk.rs
use std::fs::File;
use std::io::{Seek, SeekFrom, Read, Write};
use rfd::FileDialog;
pub struct Disk {
file: File,
pub size: u64,
current_sector: u32,
}
impl Disk {
pub fn new(file: File) -> Self {
Disk {
size: file.metadata().unwrap().len(),
file,
current_sector: 0,
}
}
}
pub struct DiskController {
pub disk: [Option<Disk>; 4],
pub sector_buffer: [u8; 512],
}
impl DiskController {
pub fn new() -> Self {
DiskController {
disk: [None, None, None, None],
sector_buffer: [0; 512]
}
}
pub fn select_file(&self) -> Option<File> {
let path = FileDialog::new()
.add_filter("disk image", &["img", "dsk"])
.add_filter("f32 binary", &["f32"])
.add_filter("raw binary", &["bin"])
.pick_file();
match path {
Some(path) => Some(File::open(path).unwrap()),
None => None,
}
}
pub fn mount(&mut self, file: File, disk_id: u8) {
self.disk[disk_id as usize] = Some(Disk::new(file));
}
pub fn unmount(&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
}
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_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
}
pub fn read_into_buffer(&mut self, disk_id: u8) -> usize {
let disk = self.disk[disk_id as usize].as_mut().expect("attempted to access unmounted disk");
let mut temp_buffer = [0u8; 512];
let number_of_bytes_read = disk.file.read(&mut temp_buffer).unwrap();
self.sector_buffer = temp_buffer;
number_of_bytes_read
}
pub fn write_from_buffer(&mut self, disk_id: u8) -> usize {
let disk = self.disk[disk_id as usize].as_mut().expect("attempted to access unmounted disk");
let number_of_bytes_written = disk.file.write(&mut self.sector_buffer).unwrap();
number_of_bytes_written
}
}

View File

@ -2,10 +2,12 @@
pub mod bus;
pub mod cpu;
pub mod disk;
pub mod memory;
pub mod mouse;
use bus::Bus;
use cpu::{Cpu, Interrupt};
use disk::DiskController;
use memory::Memory;
use mouse::Mouse;
@ -92,7 +94,8 @@ fn main() {
let cpu_mouse = Arc::clone(&mouse);
let bus = Bus { memory, mouse: cpu_mouse };
let disk_controller = DiskController::new();
let bus = Bus { disk_controller, memory, mouse: cpu_mouse };
Cpu::new(bus)
};