Skip to content

Commit 030edec

Browse files
committed
Support block read/write on a disk image.
Also switches to clap for args.
1 parent 03b4f9c commit 030edec

4 files changed

Lines changed: 150 additions & 22 deletions

File tree

.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: 144 additions & 22 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::*;
@@ -60,10 +62,33 @@ struct Framebuffer<const N: usize> {
6062
contents: std::cell::UnsafeCell<[u8; N]>,
6163
}
6264

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+
6385
// ===========================================================================
6486
// Global Variables
6587
// ===========================================================================
6688

89+
/// We only have 'normal' sectored emulated disks
90+
const BLOCK_SIZE: usize = 512;
91+
6792
/// The VRAM we share in a very hazardous way with the OS.
6893
///
6994
/// Big enough for 640x480 @ 256 colour.
@@ -74,7 +99,7 @@ static FRAMEBUFFER: Framebuffer<{ 640 * 480 }> = Framebuffer::new();
7499
const SCALE_FACTOR: f32 = 2.0;
75100

76101
/// When we booted up
77-
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);
78103

79104
/// The functions we export to the OS
80105
static BIOS_API: common::Api = common::Api {
@@ -667,10 +692,20 @@ static EV_QUEUE: std::sync::Mutex<Option<std::sync::mpsc::Receiver<AppEvent>>> =
667692
fn main() {
668693
env_logger::init();
669694

695+
let args = Args::parse();
696+
670697
// Let's go!
671698
info!("Netron Desktop BIOS");
672699

673-
*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+
}
674709

675710
let white_on_black = common::video::Attr::new(
676711
common::video::TextForegroundColour::WHITE,
@@ -685,14 +720,9 @@ fn main() {
685720
}
686721

687722
// Process args
688-
let mut lib = None;
689-
for arg in std::env::args() {
690-
if let Some(os_path) = arg.strip_prefix("--os=") {
691-
info!("Loading OS from {:?}", os_path);
692-
lib = unsafe { Some(libloading::Library::new(os_path).expect("library to load")) };
693-
println!("Loaded!");
694-
}
695-
}
723+
info!("Loading OS from {}", args.os.display());
724+
let lib = unsafe { libloading::Library::new(args.os).expect("library to load") };
725+
println!("Loaded!");
696726

697727
// Make a window
698728
let mut engine = PixEngine::builder()
@@ -713,7 +743,6 @@ fn main() {
713743
EV_QUEUE.lock().unwrap().replace(receiver);
714744

715745
// Run the OS
716-
let lib = lib.unwrap();
717746
std::thread::spawn(move || unsafe {
718747
// Wait for Started message
719748
let queue = EV_QUEUE.lock().unwrap();
@@ -1311,7 +1340,9 @@ extern "C" fn bus_exchange(_buffer: common::ApiBuffer) -> common::Result<()> {
13111340
}
13121341

13131342
extern "C" fn time_ticks_get() -> common::Ticks {
1314-
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;
13151346
let difference = boot_time.elapsed();
13161347
debug!("time_ticks_get() -> {}", difference.as_millis());
13171348
common::Ticks(difference.as_millis() as u64)
@@ -1330,7 +1361,25 @@ extern "C" fn bus_interrupt_status() -> u32 {
13301361

13311362
extern "C" fn block_dev_get_info(dev_id: u8) -> common::Option<common::block_dev::DeviceInfo> {
13321363
debug!("block_dev_get_info(dev_id: {})", dev_id);
1333-
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+
}
13341383
}
13351384

13361385
extern "C" fn block_dev_eject(dev_id: u8) -> common::Result<()> {
@@ -1348,20 +1397,66 @@ extern "C" fn block_write(
13481397
"block_write(dev_id: {}, block_id: {}, num_blocks: {}, buffer_len: {})",
13491398
dev_id, block_idx.0, num_blocks, buffer.data_len
13501399
);
1351-
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+
}
13521423
}
13531424

13541425
extern "C" fn block_read(
13551426
dev_id: u8,
13561427
block_idx: common::block_dev::BlockIdx,
13571428
num_blocks: u8,
1358-
buffer: common::ApiBuffer,
1429+
mut buffer: common::ApiBuffer,
13591430
) -> common::Result<()> {
13601431
debug!(
13611432
"block_read(dev_id: {}, block_id: {}, num_blocks: {}, buffer_len: {})",
13621433
dev_id, block_idx.0, num_blocks, buffer.data_len
13631434
);
1364-
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+
}
13651460
}
13661461

13671462
extern "C" fn block_verify(
@@ -1374,7 +1469,34 @@ extern "C" fn block_verify(
13741469
"block_read(dev_id: {}, block_id: {}, num_blocks: {}, buffer_len: {})",
13751470
dev_id, block_idx.0, num_blocks, buffer.data_len
13761471
);
1377-
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+
}
13781500
}
13791501

13801502
extern "C" fn power_idle() {
@@ -1534,7 +1656,7 @@ impl AppState for MyApp {
15341656

15351657
impl<const N: usize> Framebuffer<N> {
15361658
/// Create a new blank Framebuffer.
1537-
///
1659+
///
15381660
/// Everything is zero initialised.
15391661
const fn new() -> Framebuffer<N> {
15401662
Framebuffer {
@@ -1543,9 +1665,9 @@ impl<const N: usize> Framebuffer<N> {
15431665
}
15441666

15451667
/// Set a byte in the framebuffer.
1546-
///
1668+
///
15471669
/// Panics if you try and write out of bounds.
1548-
///
1670+
///
15491671
/// Uses volatile writes.
15501672
fn write_at(&self, offset: usize, value: u8) {
15511673
if offset > std::mem::size_of_val(&self.contents) {
@@ -1559,9 +1681,9 @@ impl<const N: usize> Framebuffer<N> {
15591681
}
15601682

15611683
/// Get a byte from the framebuffer.
1562-
///
1684+
///
15631685
/// Panics if you try and read out of bounds.
1564-
///
1686+
///
15651687
/// Uses volatile reads.
15661688
fn get_at(&self, offset: usize) -> u8 {
15671689
if offset > std::mem::size_of_val(&self.contents) {

0 commit comments

Comments
 (0)