Skip to content

Commit e1176f2

Browse files
authored
Merge pull request #5 from Neotron-Compute/disk-support
Add disk support
2 parents aeef59f + 030edec commit e1176f2

File tree

4 files changed

+200
-57
lines changed

4 files changed

+200
-57
lines changed

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,7 @@ Cargo.lock
1212
# Shared libraries from OS build
1313
*.dylib
1414
*.so
15+
16+
# Our uncompressed disk image
17+
disk.img
18+

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,5 @@ libloading = "0.7"
1414
pix-engine = "0.5.4"
1515
env_logger = "0.9"
1616
log = "0.4"
17+
clap = { version="4.2", features=["derive"] }
18+

disk.img.gz

509 KB
Binary file not shown.

src/main.rs

Lines changed: 194 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,10 @@
2727
// Imports
2828
// ===========================================================================
2929

30+
use std::io::prelude::*;
3031
use std::sync::atomic::{AtomicU32, AtomicU8, Ordering};
3132

33+
use clap::Parser;
3234
use common::video::RGBColour;
3335
use log::{debug, info};
3436
use pix_engine::prelude::*;
@@ -55,61 +57,49 @@ enum AppEvent {
5557
KeyDown(Key),
5658
}
5759

60+
/// Our video RAM
61+
struct Framebuffer<const N: usize> {
62+
contents: std::cell::UnsafeCell<[u8; N]>,
63+
}
64+
65+
/// A Desktop GUI version of a Neotron BIOS
66+
#[derive(Parser)]
67+
#[command(author, version, about)]
68+
struct Args {
69+
/// Path to the OS library
70+
#[arg(long)]
71+
os: std::path::PathBuf,
72+
/// Path to a file to use as a disk image
73+
#[arg(long)]
74+
disk: Option<std::path::PathBuf>,
75+
}
76+
77+
/// All our emulated hardware
78+
struct Hardware {
79+
/// When we booted up
80+
boot_time: std::time::Instant,
81+
/// Our disk image
82+
disk_file: Option<std::fs::File>,
83+
}
84+
5885
// ===========================================================================
5986
// Global Variables
6087
// ===========================================================================
6188

89+
/// We only have 'normal' sectored emulated disks
90+
const BLOCK_SIZE: usize = 512;
91+
6292
/// The VRAM we share in a very hazardous way with the OS.
6393
///
6494
/// Big enough for 640x480 @ 256 colour.
6595
// static mut FRAMEBUFFER: [u8; 307200] = [0u8; 307200];
6696
static FRAMEBUFFER: Framebuffer<{ 640 * 480 }> = Framebuffer::new();
6797

68-
struct Framebuffer<const N: usize> {
69-
contents: std::cell::UnsafeCell<[u8; N]>,
70-
}
71-
72-
impl<const N: usize> Framebuffer<N> {
73-
const fn new() -> Framebuffer<N> {
74-
Framebuffer {
75-
contents: std::cell::UnsafeCell::new([0u8; N]),
76-
}
77-
}
78-
79-
fn write_at(&self, offset: usize, value: u8) {
80-
if offset > std::mem::size_of_val(&self.contents) {
81-
panic!("Out of bounds framebuffer write");
82-
}
83-
unsafe {
84-
let array_ptr = self.contents.get() as *mut u8;
85-
let byte_ptr = array_ptr.add(offset);
86-
byte_ptr.write_volatile(value);
87-
}
88-
}
89-
90-
fn get_at(&self, offset: usize) -> u8 {
91-
if offset > std::mem::size_of_val(&self.contents) {
92-
panic!("Out of bounds framebuffer read");
93-
}
94-
unsafe {
95-
let array_ptr = self.contents.get() as *const u8;
96-
let byte_ptr = array_ptr.add(offset);
97-
byte_ptr.read_volatile()
98-
}
99-
}
100-
101-
fn get_pointer(&self) -> *mut u8 {
102-
self.contents.get() as *mut u8
103-
}
104-
}
105-
106-
unsafe impl<const N: usize> Sync for Framebuffer<N> {}
107-
10898
/// Scale the display to make it readable on a modern monitor
10999
const SCALE_FACTOR: f32 = 2.0;
110100

111101
/// When we booted up
112-
static BOOT_TIME: std::sync::Mutex<Option<std::time::Instant>> = std::sync::Mutex::new(None);
102+
static HARDWARE: std::sync::Mutex<Option<Hardware>> = std::sync::Mutex::new(None);
113103

114104
/// The functions we export to the OS
115105
static BIOS_API: common::Api = common::Api {
@@ -702,10 +692,20 @@ static EV_QUEUE: std::sync::Mutex<Option<std::sync::mpsc::Receiver<AppEvent>>> =
702692
fn main() {
703693
env_logger::init();
704694

695+
let args = Args::parse();
696+
705697
// Let's go!
706698
info!("Netron Desktop BIOS");
707699

708-
*BOOT_TIME.lock().unwrap() = Some(std::time::Instant::now());
700+
{
701+
let mut hw = HARDWARE.lock().unwrap();
702+
*hw = Some(Hardware {
703+
boot_time: std::time::Instant::now(),
704+
disk_file: args
705+
.disk
706+
.map(|path| std::fs::File::open(path).expect("open disk file")),
707+
});
708+
}
709709

710710
let white_on_black = common::video::Attr::new(
711711
common::video::TextForegroundColour::WHITE,
@@ -720,14 +720,9 @@ fn main() {
720720
}
721721

722722
// Process args
723-
let mut lib = None;
724-
for arg in std::env::args() {
725-
if let Some(os_path) = arg.strip_prefix("--os=") {
726-
info!("Loading OS from {:?}", os_path);
727-
lib = unsafe { Some(libloading::Library::new(os_path).expect("library to load")) };
728-
println!("Loaded!");
729-
}
730-
}
723+
info!("Loading OS from {}", args.os.display());
724+
let lib = unsafe { libloading::Library::new(args.os).expect("library to load") };
725+
println!("Loaded!");
731726

732727
// Make a window
733728
let mut engine = PixEngine::builder()
@@ -748,7 +743,6 @@ fn main() {
748743
EV_QUEUE.lock().unwrap().replace(receiver);
749744

750745
// Run the OS
751-
let lib = lib.unwrap();
752746
std::thread::spawn(move || unsafe {
753747
// Wait for Started message
754748
let queue = EV_QUEUE.lock().unwrap();
@@ -1346,7 +1340,9 @@ extern "C" fn bus_exchange(_buffer: common::ApiBuffer) -> common::Result<()> {
13461340
}
13471341

13481342
extern "C" fn time_ticks_get() -> common::Ticks {
1349-
let boot_time = BOOT_TIME.lock().unwrap().unwrap();
1343+
let mut hw_guard = HARDWARE.lock().unwrap();
1344+
let hw = hw_guard.as_mut().unwrap();
1345+
let boot_time = hw.boot_time;
13501346
let difference = boot_time.elapsed();
13511347
debug!("time_ticks_get() -> {}", difference.as_millis());
13521348
common::Ticks(difference.as_millis() as u64)
@@ -1365,7 +1361,25 @@ extern "C" fn bus_interrupt_status() -> u32 {
13651361

13661362
extern "C" fn block_dev_get_info(dev_id: u8) -> common::Option<common::block_dev::DeviceInfo> {
13671363
debug!("block_dev_get_info(dev_id: {})", dev_id);
1368-
common::Option::None
1364+
let mut hw_guard = HARDWARE.lock().unwrap();
1365+
let hw = hw_guard.as_mut().unwrap();
1366+
if dev_id == 0 {
1367+
match &mut hw.disk_file {
1368+
Some(file) => common::Option::Some(common::block_dev::DeviceInfo {
1369+
name: common::ApiString::new("File0"),
1370+
device_type: common::block_dev::DeviceType::HardDiskDrive,
1371+
block_size: BLOCK_SIZE as u32,
1372+
num_blocks: file.metadata().unwrap().len() / (BLOCK_SIZE as u64),
1373+
ejectable: false,
1374+
removable: false,
1375+
media_present: true,
1376+
read_only: false,
1377+
}),
1378+
None => common::Option::None,
1379+
}
1380+
} else {
1381+
common::Option::None
1382+
}
13691383
}
13701384

13711385
extern "C" fn block_dev_eject(dev_id: u8) -> common::Result<()> {
@@ -1383,20 +1397,66 @@ extern "C" fn block_write(
13831397
"block_write(dev_id: {}, block_id: {}, num_blocks: {}, buffer_len: {})",
13841398
dev_id, block_idx.0, num_blocks, buffer.data_len
13851399
);
1386-
common::Result::Ok(())
1400+
let mut hw_guard = HARDWARE.lock().unwrap();
1401+
let hw = hw_guard.as_mut().unwrap();
1402+
if dev_id == 0 {
1403+
match &mut hw.disk_file {
1404+
Some(file) => {
1405+
if file
1406+
.seek(std::io::SeekFrom::Start(block_idx.0 * BLOCK_SIZE as u64))
1407+
.is_err()
1408+
{
1409+
return common::Result::Err(common::Error::BlockOutOfBounds);
1410+
}
1411+
let buffer_slice = &buffer.as_slice()[0..usize::from(num_blocks) * BLOCK_SIZE];
1412+
if let Err(e) = file.write_all(buffer_slice) {
1413+
log::warn!("Failed to write to disk image: {:?}", e);
1414+
return common::Result::Err(common::Error::DeviceError(0));
1415+
}
1416+
common::Result::Ok(())
1417+
}
1418+
None => common::Result::Err(common::Error::DeviceError(0)),
1419+
}
1420+
} else {
1421+
common::Result::Err(common::Error::InvalidDevice)
1422+
}
13871423
}
13881424

13891425
extern "C" fn block_read(
13901426
dev_id: u8,
13911427
block_idx: common::block_dev::BlockIdx,
13921428
num_blocks: u8,
1393-
buffer: common::ApiBuffer,
1429+
mut buffer: common::ApiBuffer,
13941430
) -> common::Result<()> {
13951431
debug!(
13961432
"block_read(dev_id: {}, block_id: {}, num_blocks: {}, buffer_len: {})",
13971433
dev_id, block_idx.0, num_blocks, buffer.data_len
13981434
);
1399-
common::Result::Ok(())
1435+
let mut hw_guard = HARDWARE.lock().unwrap();
1436+
let hw = hw_guard.as_mut().unwrap();
1437+
if dev_id == 0 {
1438+
match &mut hw.disk_file {
1439+
Some(file) => {
1440+
if file
1441+
.seek(std::io::SeekFrom::Start(block_idx.0 * BLOCK_SIZE as u64))
1442+
.is_err()
1443+
{
1444+
return common::Result::Err(common::Error::BlockOutOfBounds);
1445+
}
1446+
if let Some(buffer_slice) = buffer.as_mut_slice() {
1447+
let buffer_slice = &mut buffer_slice[0..usize::from(num_blocks) * BLOCK_SIZE];
1448+
if let Err(e) = file.read_exact(buffer_slice) {
1449+
log::warn!("Failed to read from disk image: {:?}", e);
1450+
return common::Result::Err(common::Error::DeviceError(0));
1451+
}
1452+
}
1453+
common::Result::Ok(())
1454+
}
1455+
None => common::Result::Err(common::Error::DeviceError(0)),
1456+
}
1457+
} else {
1458+
common::Result::Err(common::Error::InvalidDevice)
1459+
}
14001460
}
14011461

14021462
extern "C" fn block_verify(
@@ -1409,7 +1469,34 @@ extern "C" fn block_verify(
14091469
"block_read(dev_id: {}, block_id: {}, num_blocks: {}, buffer_len: {})",
14101470
dev_id, block_idx.0, num_blocks, buffer.data_len
14111471
);
1412-
common::Result::Ok(())
1472+
let mut hw_guard = HARDWARE.lock().unwrap();
1473+
let hw = hw_guard.as_mut().unwrap();
1474+
if dev_id == 0 {
1475+
match &mut hw.disk_file {
1476+
Some(file) => {
1477+
if file
1478+
.seek(std::io::SeekFrom::Start(block_idx.0 * BLOCK_SIZE as u64))
1479+
.is_err()
1480+
{
1481+
return common::Result::Err(common::Error::BlockOutOfBounds);
1482+
}
1483+
let buffer_slice = &buffer.as_slice()[0..usize::from(num_blocks) * BLOCK_SIZE];
1484+
let mut read_buffer = vec![0u8; buffer_slice.len()];
1485+
if let Err(e) = file.read_exact(&mut read_buffer) {
1486+
log::warn!("Failed to write to disk image: {:?}", e);
1487+
return common::Result::Err(common::Error::DeviceError(0));
1488+
}
1489+
if read_buffer.as_slice() == buffer_slice {
1490+
common::Result::Ok(())
1491+
} else {
1492+
common::Result::Err(common::Error::DeviceError(1))
1493+
}
1494+
}
1495+
None => common::Result::Err(common::Error::DeviceError(0)),
1496+
}
1497+
} else {
1498+
common::Result::Err(common::Error::InvalidDevice)
1499+
}
14131500
}
14141501

14151502
extern "C" fn power_idle() {
@@ -1567,6 +1654,56 @@ impl AppState for MyApp {
15671654
}
15681655
}
15691656

1657+
impl<const N: usize> Framebuffer<N> {
1658+
/// Create a new blank Framebuffer.
1659+
///
1660+
/// Everything is zero initialised.
1661+
const fn new() -> Framebuffer<N> {
1662+
Framebuffer {
1663+
contents: std::cell::UnsafeCell::new([0u8; N]),
1664+
}
1665+
}
1666+
1667+
/// Set a byte in the framebuffer.
1668+
///
1669+
/// Panics if you try and write out of bounds.
1670+
///
1671+
/// Uses volatile writes.
1672+
fn write_at(&self, offset: usize, value: u8) {
1673+
if offset > std::mem::size_of_val(&self.contents) {
1674+
panic!("Out of bounds framebuffer write");
1675+
}
1676+
unsafe {
1677+
let array_ptr = self.contents.get() as *mut u8;
1678+
let byte_ptr = array_ptr.add(offset);
1679+
byte_ptr.write_volatile(value);
1680+
}
1681+
}
1682+
1683+
/// Get a byte from the framebuffer.
1684+
///
1685+
/// Panics if you try and read out of bounds.
1686+
///
1687+
/// Uses volatile reads.
1688+
fn get_at(&self, offset: usize) -> u8 {
1689+
if offset > std::mem::size_of_val(&self.contents) {
1690+
panic!("Out of bounds framebuffer read");
1691+
}
1692+
unsafe {
1693+
let array_ptr = self.contents.get() as *const u8;
1694+
let byte_ptr = array_ptr.add(offset);
1695+
byte_ptr.read_volatile()
1696+
}
1697+
}
1698+
1699+
/// Get a pointer to the framebuffer you can give to the OS.
1700+
fn get_pointer(&self) -> *mut u8 {
1701+
self.contents.get() as *mut u8
1702+
}
1703+
}
1704+
1705+
unsafe impl<const N: usize> Sync for Framebuffer<N> {}
1706+
15701707
// ===========================================================================
15711708
// End of File
15721709
// ===========================================================================

0 commit comments

Comments
 (0)