2727// Imports
2828// ===========================================================================
2929
30+ use std:: io:: prelude:: * ;
3031use std:: sync:: atomic:: { AtomicU32 , AtomicU8 , Ordering } ;
3132
33+ use clap:: Parser ;
3234use common:: video:: RGBColour ;
3335use log:: { debug, info} ;
3436use 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();
7499const 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
80105static BIOS_API : common:: Api = common:: Api {
@@ -667,10 +692,20 @@ static EV_QUEUE: std::sync::Mutex<Option<std::sync::mpsc::Receiver<AppEvent>>> =
667692fn 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
13131342extern "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
13311362extern "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
13361385extern "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
13541425extern "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
13671462extern "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
13801502extern "C" fn power_idle ( ) {
@@ -1534,7 +1656,7 @@ impl AppState for MyApp {
15341656
15351657impl < 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