diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..dd26823 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,13 @@ +# These are supported funding model platforms + +github: Neotron-Compute +patreon: # Replace with a single Patreon username +open_collective: # Replace with a single Open Collective username +ko_fi: # Replace with a single Ko-fi username +tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel +community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry +liberapay: # Replace with a single Liberapay username +issuehunt: # Replace with a single IssueHunt username +otechie: # Replace with a single Otechie username +lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry +custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] diff --git a/.gitignore b/.gitignore index 31d9504..dd9db28 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ /target **/*.rs.bk -Cargo.lock /release diff --git a/CHANGELOG.md b/CHANGELOG.md index 3020dfe..710b428 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,14 @@ ## Unreleased changes +## v0.3.3 + +* Add `dir` command +* Change `load` command to load from disk +* Repository includes `Cargo.lock` file +* Update to `postcard` 1.0 +* Fix `readblk` help text, and print 32 bytes per line + ## v0.3.2 * Add `date` command. diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..5372c17 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,283 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "atomic-polyfill" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3ff7eb3f316534d83a8a2c3d1674ace8a5a71198eba31e2e2b597833f699b28" +dependencies = [ + "critical-section", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "chrono" +version = "0.4.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e3c5919066adf22df73762e50cffcde3a758f2a848b113b586d1f86728b673b" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "cobs" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67ba02a97a2bd10f4b59b25c7973101c79642302776489e030cd13cdab09ed15" + +[[package]] +name = "critical-section" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6548a0ad5d2549e111e1f6a11a6c2e2d00ce6a3dafe22948d67c2b443f775e52" + +[[package]] +name = "embedded-hal" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35949884794ad573cf46071e41c9b60efb0cb311e3ca01f7af807af1debc66ff" +dependencies = [ + "nb 0.1.3", + "void", +] + +[[package]] +name = "embedded-sdmmc" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f4d14180a76a8af24a45a0e1a4f9c97491b05a3b962d59d5e4ce0e6ab103736" +dependencies = [ + "byteorder", + "embedded-hal", +] + +[[package]] +name = "hash32" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67" +dependencies = [ + "byteorder", +] + +[[package]] +name = "heapless" +version = "0.7.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db04bc24a18b9ea980628ecf00e6c0264f3c1426dac36c00cb49b6fbad8b0743" +dependencies = [ + "atomic-polyfill", + "hash32", + "rustc_version", + "serde", + "spin", + "stable_deref_trait", +] + +[[package]] +name = "lock_api" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "menu" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b03d7f798bfe97329ad6df937951142eec93886b37d87010502dd25e8cc75fd5" + +[[package]] +name = "nb" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "801d31da0513b6ec5214e9bf433a77966320625a37860f910be265be6e18d06f" +dependencies = [ + "nb 1.1.0", +] + +[[package]] +name = "nb" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d" + +[[package]] +name = "neotron-common-bios" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4b43ef66a881b66110e2bafd11a8cd06613d0e99c52c9e8cdbc1195c61b94b" +dependencies = [ + "chrono", + "pc-keyboard", +] + +[[package]] +name = "neotron-os" +version = "0.3.3" +dependencies = [ + "chrono", + "embedded-sdmmc", + "menu", + "neotron-common-bios", + "pc-keyboard", + "postcard", + "r0", + "serde", +] + +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", +] + +[[package]] +name = "pc-keyboard" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed089a1fbffe3337a1a345501c981f1eb1e47e69de5a40e852433e12953c3174" + +[[package]] +name = "postcard" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfa512cd0d087cc9f99ad30a1bf64795b67871edbead083ffc3a4dfafa59aa00" +dependencies = [ + "cobs", + "heapless", + "serde", +] + +[[package]] +name = "proc-macro2" +version = "1.0.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f4f29d145265ec1c483c7c654450edde0bfe043d3938d6972630663356d9500" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r0" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd7a31eed1591dcbc95d92ad7161908e72f4677f8fabf2a32ca49b4237cbf211" + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "semver" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" + +[[package]] +name = "serde" +version = "1.0.163" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2113ab51b87a539ae008b5c6c02dc020ffa39afd2d83cffcb3f4eb2722cebec2" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.163" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c805777e3930c8883389c602315a24224bcc738b63905ef87cd1420353ea93e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "syn" +version = "2.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6f671d4b5ffdb8eadec19c0ae67fe2639df8684bd7bc4b83d986b8db549cf01" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" + +[[package]] +name = "void" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" diff --git a/Cargo.toml b/Cargo.toml index 8ad29bb..8629cf3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "neotron-os" -version = "0.3.2" +version = "0.3.3" authors = ["Jonathan 'theJPster' Pallant ", "The Neotron Developers"] edition = "2018" description = "The Neotron Operating System" @@ -25,6 +25,7 @@ bench = false [lib] crate-type = ["rlib", "cdylib"] +required-features = ["native-log"] [profile.release] lto = true @@ -40,7 +41,8 @@ panic = "abort" neotron-common-bios = "0.8" pc-keyboard = "0.7" r0 = "1.0" -postcard = "0.5" +postcard = "1.0" serde = { version = "1.0", default-features = false } menu = "0.3" chrono = { version = "0.4", default-features = false } +embedded-sdmmc = { version = "0.5", default-features = false } diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..c405b6d --- /dev/null +++ b/flake.lock @@ -0,0 +1,127 @@ +{ + "nodes": { + "crane": { + "inputs": { + "flake-compat": "flake-compat", + "flake-utils": [ + "flake-utils" + ], + "nixpkgs": [ + "nixpkgs" + ], + "rust-overlay": [ + "rust-overlay" + ] + }, + "locked": { + "lastModified": 1684468982, + "narHash": "sha256-EoC1N5sFdmjuAP3UOkyQujSOT6EdcXTnRw8hPjJkEgc=", + "owner": "ipetkov", + "repo": "crane", + "rev": "99de890b6ef4b4aab031582125b6056b792a4a30", + "type": "github" + }, + "original": { + "owner": "ipetkov", + "repo": "crane", + "type": "github" + } + }, + "flake-compat": { + "flake": false, + "locked": { + "lastModified": 1673956053, + "narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1681202837, + "narHash": "sha256-H+Rh19JDwRtpVPAWp64F+rlEtxUWBAQW28eAi3SRSzg=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "cfacdce06f30d2b68473a46042957675eebb3401", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1684432087, + "narHash": "sha256-3zFTOY/3+kN9x9Zdq6ixLmgV4ZcEd1aafq41v/OVUek=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "7721e0d2c1845c24eafd5a016b9d349187c48097", + "type": "github" + }, + "original": { + "id": "nixpkgs", + "type": "indirect" + } + }, + "root": { + "inputs": { + "crane": "crane", + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs", + "rust-overlay": "rust-overlay" + } + }, + "rust-overlay": { + "inputs": { + "flake-utils": [ + "flake-utils" + ], + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1684462813, + "narHash": "sha256-YFphDnxzXtLLExXjiR9bUVF4isI/MKiC4HMboh2ZSOc=", + "owner": "oxalica", + "repo": "rust-overlay", + "rev": "e2ceeaa7f9334c5d732323b6fec363229da4f382", + "type": "github" + }, + "original": { + "owner": "oxalica", + "repo": "rust-overlay", + "type": "github" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..41b09bd --- /dev/null +++ b/flake.nix @@ -0,0 +1,88 @@ +{ + description = "Neotron OS"; + + inputs = { + flake-utils.url = "github:numtide/flake-utils"; + rust-overlay = { + url = "github:oxalica/rust-overlay"; + inputs = { + nixpkgs.follows = "nixpkgs"; + flake-utils.follows = "flake-utils"; + }; + }; + crane = { + url = "github:ipetkov/crane"; + inputs = { + nixpkgs.follows = "nixpkgs"; + flake-utils.follows = "flake-utils"; + rust-overlay.follows = "rust-overlay"; + }; + }; + }; + + outputs = { self, nixpkgs, flake-utils, rust-overlay, crane }: + let + # List of systems this flake.nix has been tested to work with + systems = [ "x86_64-linux" ]; + in + flake-utils.lib.eachSystem systems + (system: + let + pkgs = nixpkgs.legacyPackages.${system}.appendOverlays [ + rust-overlay.overlays.default + ]; + lib = pkgs.lib; + targets = [ + "thumbv6m-none-eabi" + "thumbv7m-none-eabi" + "thumbv7em-none-eabi" + ]; + toolchain = pkgs.rust-bin.stable.latest.default.override { inherit targets; }; + craneLib = (crane.mkLib pkgs).overrideToolchain toolchain; + + neotron-packages = builtins.listToAttrs (map + (tgt: + let arch = lib.head (builtins.split "-" tgt); + in { + name = "neotron-os-${arch}"; + value = craneLib.buildPackage { + pname = "neotron-os"; + nativeBuildInputs = [ + pkgs.gcc-arm-embedded + ]; + doCheck = false; + src = with pkgs.lib; + let keep = suffix: path: type: hasSuffix suffix path; + in + cleanSourceWith { + src = craneLib.path ./.; + filter = path: type: any id (map (f: f path type) [ + craneLib.filterCargoSources + (keep ".ld") + ]); + }; + cargoExtraArgs = "--target=${tgt}"; + installPhase = '' + runHook preInstall + mkdir -p $out/bin + '' + toString (map + (bin: '' + cp target/${tgt}/release/${bin} $out/bin/${tgt}-${bin}-libneotron_os.elf + arm-none-eabi-objcopy -O binary target/${tgt}/release/${bin} $out/bin/${tgt}-${bin}-libneotron_os.bin + '') [ "flash0002" "flash0802" "flash1002" ]) + '' + runHook postInstall + ''; + }; + } + ) + targets); + in + { + packages = neotron-packages; + + devShell = pkgs.mkShell { + inputsFrom = builtins.attrValues self.packages.${system}; + }; + } + ); +} diff --git a/src/commands/block.rs b/src/commands/block.rs index 87fab98..df7585d 100644 --- a/src/commands/block.rs +++ b/src/commands/block.rs @@ -26,7 +26,7 @@ pub static READ_ITEM: menu::Item = menu::Item { ], }, command: "readblk", - help: Some("List all the Block Devices"), + help: Some("Display one disk block, as hex"), }; /// Called when the "lsblk" command is executed. @@ -96,15 +96,10 @@ fn read_block(_menu: &menu::Menu, _item: &menu::Item, args: &[&str], _ bios::Result::Ok(_) => { // Carry on let mut count = 0; - for chunk in buffer.chunks(16) { + for chunk in buffer.chunks(32) { print!("{:03x}: ", count); for b in chunk { - print!("{:02x} ", *b); - } - print!(" "); - for b in chunk { - let c = char::from(*b); - print!("{}", if c.is_ascii_graphic() { c } else { '.' }); + print!("{:02x}", *b); } count += chunk.len(); println!(); diff --git a/src/commands/fs.rs b/src/commands/fs.rs new file mode 100644 index 0000000..fae0138 --- /dev/null +++ b/src/commands/fs.rs @@ -0,0 +1,200 @@ +//! File Systems related commands for Neotron OS + +use chrono::{Datelike, Timelike}; +use embedded_sdmmc::VolumeIdx; + +use crate::{bios, print, println, Ctx, API}; + +pub static DIR_ITEM: menu::Item = menu::Item { + item_type: menu::ItemType::Callback { + function: dir, + parameters: &[], + }, + command: "dir", + help: Some("Dir the root directory on block device 0"), +}; + +#[cfg(target_os = "none")] +pub static LOAD_ITEM: menu::Item = menu::Item { + item_type: menu::ItemType::Callback { + function: load, + parameters: &[menu::Parameter::Mandatory { + parameter_name: "file", + help: Some("The file to load"), + }], + }, + command: "load", + help: Some("Load a file into the application area"), +}; + +struct BiosBlock(); + +impl embedded_sdmmc::BlockDevice for BiosBlock { + type Error = bios::Error; + + fn read( + &self, + blocks: &mut [embedded_sdmmc::Block], + start_block_idx: embedded_sdmmc::BlockIdx, + _reason: &str, + ) -> Result<(), Self::Error> { + let api = API.get(); + let byte_slice = unsafe { + core::slice::from_raw_parts_mut( + blocks.as_mut_ptr() as *mut u8, + blocks.len() * embedded_sdmmc::Block::LEN, + ) + }; + match (api.block_read)( + 0, + bios::block_dev::BlockIdx(u64::from(start_block_idx.0)), + blocks.len() as u8, + bios::ApiBuffer::new(byte_slice), + ) { + bios::Result::Ok(_) => Ok(()), + bios::Result::Err(e) => Err(e), + } + } + + fn write( + &self, + blocks: &[embedded_sdmmc::Block], + start_block_idx: embedded_sdmmc::BlockIdx, + ) -> Result<(), Self::Error> { + let api = API.get(); + let byte_slice = unsafe { + core::slice::from_raw_parts( + blocks.as_ptr() as *const u8, + blocks.len() * embedded_sdmmc::Block::LEN, + ) + }; + match (api.block_write)( + 0, + bios::block_dev::BlockIdx(u64::from(start_block_idx.0)), + blocks.len() as u8, + bios::ApiByteSlice::new(byte_slice), + ) { + bios::Result::Ok(_) => Ok(()), + bios::Result::Err(e) => Err(e), + } + } + + fn num_blocks(&self) -> Result { + let api = API.get(); + match (api.block_dev_get_info)(0) { + bios::Option::Some(info) => Ok(embedded_sdmmc::BlockCount(info.num_blocks as u32)), + bios::Option::None => Err(bios::Error::InvalidDevice), + } + } +} + +struct BiosTime(); + +impl embedded_sdmmc::TimeSource for BiosTime { + fn get_timestamp(&self) -> embedded_sdmmc::Timestamp { + let time = API.get_time(); + embedded_sdmmc::Timestamp { + year_since_1970: (time.year() - 1970) as u8, + zero_indexed_month: time.month0() as u8, + zero_indexed_day: time.day0() as u8, + hours: time.hour() as u8, + minutes: time.minute() as u8, + seconds: time.second() as u8, + } + } +} + +/// Called when the "dir" command is executed. +fn dir(_menu: &menu::Menu, _item: &menu::Item, _args: &[&str], _ctx: &mut Ctx) { + fn work() -> Result<(), embedded_sdmmc::Error> { + println!("Listing files on Block Device 0, /"); + let bios_block = BiosBlock(); + let time = BiosTime(); + let mut mgr = embedded_sdmmc::VolumeManager::new(bios_block, time); + // Open the first partition + let volume = mgr.get_volume(VolumeIdx(0))?; + let root_dir = mgr.open_root_dir(&volume)?; + let mut total_bytes = 0u64; + let mut num_files = 0; + mgr.iterate_dir(&volume, &root_dir, |dir_entry| { + let padding = 8 - dir_entry.name.base_name().len(); + for b in dir_entry.name.base_name() { + let ch = *b as char; + print!("{}", if ch.is_ascii_graphic() { ch } else { '?' }); + } + for _ in 0..padding { + print!(" "); + } + print!(" "); + let padding = 3 - dir_entry.name.extension().len(); + for b in dir_entry.name.extension() { + let ch = *b as char; + print!("{}", if ch.is_ascii_graphic() { ch } else { '?' }); + } + for _ in 0..padding { + print!(" "); + } + if dir_entry.attributes.is_directory() { + print!(" "); + } else { + print!(" {:-13}", dir_entry.size,); + } + print!( + " {:02}/{:02}/{:04}", + dir_entry.mtime.zero_indexed_day + 1, + dir_entry.mtime.zero_indexed_month + 1, + u32::from(dir_entry.mtime.year_since_1970) + 1970 + ); + println!( + " {:02}:{:02}", + dir_entry.mtime.hours, dir_entry.mtime.minutes + ); + total_bytes += dir_entry.size as u64; + num_files += 1; + })?; + println!("{:-9} file(s) {:-13} bytes", num_files, total_bytes); + Ok(()) + } + + match work() { + Ok(_) => {} + Err(e) => { + println!("Error: {:?}", e); + } + } +} + +/// Called when the "load" command is executed. +#[cfg(target_os = "none")] +fn load(_menu: &menu::Menu, _item: &menu::Item, args: &[&str], _ctx: &mut Ctx) { + fn work(args: &[&str]) -> Result<(), embedded_sdmmc::Error> { + println!("Loading /{} from Block Device 0", args[0]); + let bios_block = BiosBlock(); + let time = BiosTime(); + let mut mgr = embedded_sdmmc::VolumeManager::new(bios_block, time); + // Open the first partition + let mut volume = mgr.get_volume(VolumeIdx(0))?; + let root_dir = mgr.open_root_dir(&volume)?; + let mut file = mgr.open_file_in_dir( + &mut volume, + &root_dir, + args[0], + embedded_sdmmc::Mode::ReadOnly, + )?; + let file_length = file.length(); + // Application space starts 4K into Cortex-M SRAM + const APPLICATION_START_ADDR: usize = 0x2000_1000; + let application_ram: &'static mut [u8] = unsafe { + core::slice::from_raw_parts_mut(APPLICATION_START_ADDR as *mut u8, file_length as usize) + }; + mgr.read(&mut volume, &mut file, application_ram)?; + Ok(()) + } + + match work(args) { + Ok(_) => {} + Err(e) => { + println!("Error: {:?}", e); + } + } +} diff --git a/src/commands/mod.rs b/src/commands/mod.rs index fe0989f..c316eb3 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -6,6 +6,7 @@ pub use super::Ctx; mod block; mod config; +mod fs; mod hardware; mod input; mod ram; @@ -19,11 +20,13 @@ pub static OS_MENU: menu::Menu = menu::Menu { &config::COMMAND_ITEM, &block::LSBLK_ITEM, &block::READ_ITEM, + &fs::DIR_ITEM, &hardware::LSHW_ITEM, &ram::HEXDUMP_ITEM, - &ram::LOAD_ITEM, #[cfg(target_os = "none")] &ram::RUN_ITEM, + #[cfg(target_os = "none")] + &fs::LOAD_ITEM, &screen::CLEAR_ITEM, &screen::BENCH_ITEM, &screen::FILL_ITEM, diff --git a/src/commands/ram.rs b/src/commands/ram.rs index 42580f8..5527bfd 100644 --- a/src/commands/ram.rs +++ b/src/commands/ram.rs @@ -20,24 +20,6 @@ pub static HEXDUMP_ITEM: menu::Item = menu::Item { help: Some("Dump the contents of RAM as hex"), }; -pub static LOAD_ITEM: menu::Item = menu::Item { - item_type: menu::ItemType::Callback { - function: load, - parameters: &[ - menu::Parameter::Mandatory { - parameter_name: "address", - help: Some("Start address"), - }, - menu::Parameter::Mandatory { - parameter_name: "hex", - help: Some("Bytes as hex string"), - }, - ], - }, - command: "load", - help: Some("Load hex bytes into RAM from stdin"), -}; - #[cfg(target_os = "none")] pub static RUN_ITEM: menu::Item = menu::Item { item_type: menu::ItemType::Callback { @@ -99,52 +81,16 @@ fn hexdump(_menu: &menu::Menu, _item: &menu::Item, args: &[&str], _ctx println!(); } -/// Called when the "load" command is executed. -fn load(_menu: &menu::Menu, _item: &menu::Item, args: &[&str], _ctx: &mut Ctx) { - let Some(address_str) = args.get(0) else { - println!("No address"); - return; - }; - let Ok(address) = parse_usize(address_str) else { - println!("Bad address"); - return; - }; - let Some(mut hex_str) = args.get(1).cloned() else { - println!("No hex"); - return; - }; - - let mut address = address as *mut u8; - let mut count = 0; - loop { - let Some(hex_byte) = hex_str.get(0..2) else { - println!("Bad hex from {:?}", hex_str); - return; - }; - hex_str = &hex_str[2..]; - let Ok(byte) = u8::from_str_radix(hex_byte, 16) else { - println!("Bad hex {:?}", hex_byte); - return; - }; - - unsafe { - address.write_volatile(byte); - address = address.offset(1); - } - count += 1; - - println!("Loaded {} bytes", count); - } -} - #[allow(unused)] #[repr(C)] pub struct Api { pub print: extern "C" fn(data: *const u8, len: usize), } +#[allow(unused)] static CALLBACK_TABLE: Api = Api { print: print_fn }; +#[allow(unused)] extern "C" fn print_fn(data: *const u8, len: usize) { let slice = unsafe { core::slice::from_raw_parts(data, len) }; if let Ok(s) = core::str::from_utf8(slice) { @@ -170,6 +116,7 @@ fn run(_menu: &menu::Menu, _item: &menu::Item, _args: &[&str], _ctx: & let code: extern "C" fn(*const Api) -> u32 = ::core::mem::transmute(start_ptr); code(&CALLBACK_TABLE) }; + println!(); if result != 0 { println!("Got error code {}", result); }