Skip to content

Commit b6dda07

Browse files
committed
Applications can open/close/read/write files.
1 parent 78bde4b commit b6dda07

File tree

2 files changed

+171
-28
lines changed

2 files changed

+171
-28
lines changed

src/fs.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,11 @@ impl File {
110110
FILESYSTEM.file_read(self, buffer)
111111
}
112112

113+
/// Write to a file
114+
pub fn write(&self, buffer: &[u8]) -> Result<(), Error> {
115+
FILESYSTEM.file_write(self, buffer)
116+
}
117+
113118
/// Are we at the end of the file
114119
pub fn is_eof(&self) -> bool {
115120
FILESYSTEM
@@ -202,6 +207,17 @@ impl Filesystem {
202207
Ok(bytes_read)
203208
}
204209

210+
/// Write to an open file
211+
pub fn file_write(&self, file: &File, buffer: &[u8]) -> Result<(), Error> {
212+
let mut fs = self.volume_manager.lock();
213+
if fs.is_none() {
214+
*fs = Some(embedded_sdmmc::VolumeManager::new(BiosBlock(), BiosTime()));
215+
}
216+
let fs = fs.as_mut().unwrap();
217+
fs.write(file.inner, buffer)?;
218+
Ok(())
219+
}
220+
205221
/// How large is a file?
206222
pub fn file_length(&self, file: &File) -> Result<u32, Error> {
207223
let mut fs = self.volume_manager.lock();

src/program.rs

Lines changed: 155 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//! Program Loading and Execution
22
3-
use crate::{osprintln, FILESYSTEM};
3+
use crate::{fs, osprintln, refcell::CsRefCell, FILESYSTEM};
44

55
#[allow(unused)]
66
static 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)]
3267
pub 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.
295343
extern "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
420521
extern "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

Comments
 (0)