11//! Program Loading and Execution
22
3- use crate :: { osprintln, FILESYSTEM } ;
3+ use crate :: { fs , osprintln, refcell :: CsRefCell , FILESYSTEM } ;
44
55#[ allow( unused) ]
66static CALLBACK_TABLE : neotron_api:: Api = neotron_api:: Api {
@@ -27,6 +27,41 @@ static CALLBACK_TABLE: neotron_api::Api = neotron_api::Api {
2727 free : api_free,
2828} ;
2929
30+ /// The different kinds of state each open handle can be in
31+ pub enum OpenHandle {
32+ /// Represents Standard Input
33+ StdIn ,
34+ /// Represents Standard Output
35+ Stdout ,
36+ /// Represents Standard Error
37+ StdErr ,
38+ /// Represents an open file in the filesystem
39+ File ( fs:: File ) ,
40+ /// Represents a closed handle.
41+ ///
42+ /// This is the default state for handles.
43+ Closed ,
44+ }
45+
46+ /// The open handle table
47+ ///
48+ /// This is indexed by the file descriptors (or handles) that the application
49+ /// uses. When an application says "write to handle 4", we look at the 4th entry
50+ /// in here to work out what they are writing to.
51+ ///
52+ /// The table is initialised when a program is started, and any open files are
53+ /// closed when the program ends.
54+ static OPEN_HANDLES : CsRefCell < [ OpenHandle ; 8 ] > = CsRefCell :: new ( [
55+ OpenHandle :: Closed ,
56+ OpenHandle :: Closed ,
57+ OpenHandle :: Closed ,
58+ OpenHandle :: Closed ,
59+ OpenHandle :: Closed ,
60+ OpenHandle :: Closed ,
61+ OpenHandle :: Closed ,
62+ OpenHandle :: Closed ,
63+ ] ) ;
64+
3065/// Ways in which loading a program can fail.
3166#[ derive( Debug ) ]
3267pub enum Error {
@@ -71,7 +106,6 @@ impl FileSource {
71106 }
72107
73108 fn uncached_read ( & self , offset : u32 , out_buffer : & mut [ u8 ] ) -> Result < ( ) , crate :: fs:: Error > {
74- osprintln ! ( "Reading from {}" , offset) ;
75109 self . file . seek_from_start ( offset) ?;
76110 self . file . read ( out_buffer) ?;
77111 Ok ( ( ) )
@@ -96,7 +130,6 @@ impl neotron_loader::traits::Source for &FileSource {
96130 }
97131 }
98132
99- osprintln ! ( "Reading from {}" , offset) ;
100133 self . file . seek_from_start ( offset) ?;
101134 self . file . read ( self . buffer . borrow_mut ( ) . as_mut_slice ( ) ) ?;
102135 self . offset_cached . set ( Some ( offset) ) ;
@@ -235,7 +268,15 @@ impl TransientProgramArea {
235268 return Err ( Error :: NothingLoaded ) ;
236269 }
237270
271+ // Setup the default file handles
272+ let mut open_handles = OPEN_HANDLES . lock ( ) ;
273+ open_handles[ 0 ] = OpenHandle :: StdIn ;
274+ open_handles[ 1 ] = OpenHandle :: Stdout ;
275+ open_handles[ 2 ] = OpenHandle :: StdErr ;
276+ drop ( open_handles) ;
277+
238278 // We support a maximum of four arguments.
279+ #[ allow( clippy:: get_first) ]
239280 let ffi_args = [
240281 neotron_api:: FfiString :: new ( args. get ( 0 ) . unwrap_or ( & "" ) ) ,
241282 neotron_api:: FfiString :: new ( args. get ( 1 ) . unwrap_or ( & "" ) ) ,
@@ -249,6 +290,13 @@ impl TransientProgramArea {
249290 code ( & CALLBACK_TABLE , args. len ( ) , ffi_args. as_ptr ( ) )
250291 } ;
251292
293+ // Close any files the program left open
294+ let mut open_handles = OPEN_HANDLES . lock ( ) ;
295+ for h in open_handles. iter_mut ( ) {
296+ * h = OpenHandle :: Closed ;
297+ }
298+ drop ( open_handles) ;
299+
252300 self . last_entry = 0 ;
253301 Ok ( result)
254302 }
@@ -293,15 +341,46 @@ impl TransientProgramArea {
293341/// Path may be relative to current directory, or it may be an absolute
294342/// path.
295343extern "C" fn api_open (
296- _path : neotron_api:: FfiString ,
344+ path : neotron_api:: FfiString ,
297345 _flags : neotron_api:: file:: Flags ,
298346) -> neotron_api:: Result < neotron_api:: file:: Handle > {
299- neotron_api:: Result :: Err ( neotron_api:: Error :: Unimplemented )
347+ let f = match FILESYSTEM . open_file ( path. as_str ( ) , embedded_sdmmc:: Mode :: ReadOnly ) {
348+ Ok ( f) => f,
349+ Err ( fs:: Error :: Io ( embedded_sdmmc:: Error :: NotFound ) ) => {
350+ return neotron_api:: Result :: Err ( neotron_api:: Error :: InvalidPath ) ;
351+ }
352+ Err ( _e) => {
353+ return neotron_api:: Result :: Err ( neotron_api:: Error :: DeviceSpecific ) ;
354+ }
355+ } ;
356+
357+ // 1. Put the file into the open handles array and get the index (or return an error)
358+ let mut result = None ;
359+ for ( idx, slot) in OPEN_HANDLES . lock ( ) . iter_mut ( ) . enumerate ( ) {
360+ if matches ! ( * slot, OpenHandle :: Closed ) {
361+ result = Some ( idx) ;
362+ * slot = OpenHandle :: File ( f) ;
363+ break ;
364+ }
365+ }
366+
367+ // 2. give the caller the index into the open handles array
368+ match result {
369+ Some ( n) => neotron_api:: Result :: Ok ( neotron_api:: file:: Handle :: new ( n as u8 ) ) ,
370+ None => neotron_api:: Result :: Err ( neotron_api:: Error :: OutOfMemory ) ,
371+ }
300372}
301373
302374/// Close a previously opened file.
303- extern "C" fn api_close ( _fd : neotron_api:: file:: Handle ) -> neotron_api:: Result < ( ) > {
304- neotron_api:: Result :: Err ( neotron_api:: Error :: Unimplemented )
375+ extern "C" fn api_close ( fd : neotron_api:: file:: Handle ) -> neotron_api:: Result < ( ) > {
376+ let mut open_handles = OPEN_HANDLES . lock ( ) ;
377+ match open_handles. get_mut ( fd. value ( ) as usize ) {
378+ Some ( h) => {
379+ * h = OpenHandle :: Closed ;
380+ neotron_api:: Result :: Ok ( ( ) )
381+ }
382+ None => neotron_api:: Result :: Err ( neotron_api:: Error :: BadHandle ) ,
383+ }
305384}
306385
307386/// Write to an open file handle, blocking until everything is written.
@@ -311,19 +390,28 @@ extern "C" fn api_write(
311390 fd : neotron_api:: file:: Handle ,
312391 buffer : neotron_api:: FfiByteSlice ,
313392) -> neotron_api:: Result < ( ) > {
314- if fd == neotron_api:: file:: Handle :: new_stdout ( ) {
315- let mut guard = crate :: VGA_CONSOLE . lock ( ) ;
316- if let Some ( console) = guard. as_mut ( ) {
317- console. write_bstr ( buffer. as_slice ( ) ) ;
393+ let mut open_handles = OPEN_HANDLES . lock ( ) ;
394+ match open_handles. get_mut ( fd. value ( ) as usize ) {
395+ Some ( OpenHandle :: StdErr | OpenHandle :: Stdout ) => {
396+ // Treat stderr and stdout the same
397+ let mut guard = crate :: VGA_CONSOLE . lock ( ) ;
398+ if let Some ( console) = guard. as_mut ( ) {
399+ console. write_bstr ( buffer. as_slice ( ) ) ;
400+ }
401+ let mut guard = crate :: SERIAL_CONSOLE . lock ( ) ;
402+ if let Some ( console) = guard. as_mut ( ) {
403+ // Ignore serial errors on stdout
404+ let _ = console. write_bstr ( buffer. as_slice ( ) ) ;
405+ }
406+ neotron_api:: Result :: Ok ( ( ) )
318407 }
319- let mut guard = crate :: SERIAL_CONSOLE . lock ( ) ;
320- if let Some ( console) = guard. as_mut ( ) {
321- // Ignore serial errors on stdout
322- let _ = console. write_bstr ( buffer. as_slice ( ) ) ;
408+ Some ( OpenHandle :: File ( f) ) => match f. write ( buffer. as_slice ( ) ) {
409+ Ok ( _) => neotron_api:: Result :: Ok ( ( ) ) ,
410+ Err ( _e) => neotron_api:: Result :: Err ( neotron_api:: Error :: DeviceSpecific ) ,
411+ } ,
412+ Some ( OpenHandle :: StdIn | OpenHandle :: Closed ) | None => {
413+ neotron_api:: Result :: Err ( neotron_api:: Error :: BadHandle )
323414 }
324- neotron_api:: Result :: Ok ( ( ) )
325- } else {
326- neotron_api:: Result :: Err ( neotron_api:: Error :: BadHandle )
327415 }
328416}
329417
@@ -334,15 +422,28 @@ extern "C" fn api_read(
334422 fd : neotron_api:: file:: Handle ,
335423 mut buffer : neotron_api:: FfiBuffer ,
336424) -> neotron_api:: Result < usize > {
337- if fd == neotron_api:: file:: Handle :: new_stdin ( ) {
338- if let Some ( buffer) = buffer. as_mut_slice ( ) {
339- let count = { crate :: STD_INPUT . lock ( ) . get_data ( buffer) } ;
340- Ok ( count) . into ( )
341- } else {
342- neotron_api:: Result :: Err ( neotron_api:: Error :: DeviceSpecific )
425+ let mut open_handles = OPEN_HANDLES . lock ( ) ;
426+ match open_handles. get_mut ( fd. value ( ) as usize ) {
427+ Some ( OpenHandle :: StdIn ) => {
428+ if let Some ( buffer) = buffer. as_mut_slice ( ) {
429+ let count = { crate :: STD_INPUT . lock ( ) . get_data ( buffer) } ;
430+ Ok ( count) . into ( )
431+ } else {
432+ neotron_api:: Result :: Err ( neotron_api:: Error :: DeviceSpecific )
433+ }
434+ }
435+ Some ( OpenHandle :: File ( f) ) => {
436+ let Some ( buffer) = buffer. as_mut_slice ( ) else {
437+ return neotron_api:: Result :: Err ( neotron_api:: Error :: InvalidArg ) ;
438+ } ;
439+ match f. read ( buffer) {
440+ Ok ( n) => neotron_api:: Result :: Ok ( n) ,
441+ Err ( _e) => neotron_api:: Result :: Err ( neotron_api:: Error :: DeviceSpecific ) ,
442+ }
443+ }
444+ Some ( OpenHandle :: Stdout | OpenHandle :: StdErr | OpenHandle :: Closed ) | None => {
445+ neotron_api:: Result :: Err ( neotron_api:: Error :: BadHandle )
343446 }
344- } else {
345- neotron_api:: Result :: Err ( neotron_api:: Error :: BadHandle )
346447 }
347448}
348449
@@ -418,9 +519,35 @@ extern "C" fn api_stat(
418519
419520/// Get information about an open file
420521extern "C" fn api_fstat (
421- _fd : neotron_api:: file:: Handle ,
522+ fd : neotron_api:: file:: Handle ,
422523) -> neotron_api:: Result < neotron_api:: file:: Stat > {
423- neotron_api:: Result :: Err ( neotron_api:: Error :: Unimplemented )
524+ let mut open_handles = OPEN_HANDLES . lock ( ) ;
525+ match open_handles. get_mut ( fd. value ( ) as usize ) {
526+ Some ( OpenHandle :: File ( f) ) => {
527+ let stat = neotron_api:: file:: Stat {
528+ file_size : f. length ( ) as u64 ,
529+ ctime : neotron_api:: file:: Time {
530+ year_since_1970 : 0 ,
531+ zero_indexed_month : 0 ,
532+ zero_indexed_day : 0 ,
533+ hours : 0 ,
534+ minutes : 0 ,
535+ seconds : 0 ,
536+ } ,
537+ mtime : neotron_api:: file:: Time {
538+ year_since_1970 : 0 ,
539+ zero_indexed_month : 0 ,
540+ zero_indexed_day : 0 ,
541+ hours : 0 ,
542+ minutes : 0 ,
543+ seconds : 0 ,
544+ } ,
545+ attr : neotron_api:: file:: Attributes :: empty ( ) ,
546+ } ;
547+ neotron_api:: Result :: Ok ( stat)
548+ }
549+ _ => neotron_api:: Result :: Err ( neotron_api:: Error :: InvalidArg ) ,
550+ }
424551}
425552
426553/// Delete a file.
0 commit comments