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:: * ;
@@ -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];
6696static 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
10999const 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
115105static BIOS_API : common:: Api = common:: Api {
@@ -702,10 +692,20 @@ static EV_QUEUE: std::sync::Mutex<Option<std::sync::mpsc::Receiver<AppEvent>>> =
702692fn 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
13481342extern "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
13661362extern "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
13711385extern "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
13891425extern "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
14021462extern "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
14151502extern "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