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:
parent
4f278bc58b
commit
b957b58fb2
810
Cargo.lock
generated
810
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
|
@ -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"
|
||||
|
|
70
src/bus.rs
70
src/bus.rs
|
@ -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
79
src/disk.rs
Normal 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
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user