Browse Source

Merge upstream cortex-m-semihosting.

Jonathan Perkin 3 years ago
parent
commit
4e8ad013fd
8 changed files with 431 additions and 127 deletions
  1. 101 5
      CHANGELOG.md
  2. 14 1
      Cargo.toml
  3. 18 2
      README.md
  4. 2 4
      src/debug.rs
  5. 51 0
      src/export.rs
  6. 18 34
      src/hio.rs
  7. 131 76
      src/lib.rs
  8. 96 5
      src/macros.rs

+ 101 - 5
CHANGELOG.md

@@ -5,6 +5,91 @@ This project adheres to [Semantic Versioning](http://semver.org/).
 
 
 ## [Unreleased]
 ## [Unreleased]
 
 
+## [v0.4.1] - 2020-10-20
+
+0.4.1 was yanked because the pre-built binaries contain conflicting symbols
+with a supported version of cortex-m.
+
+- Fix missing prebuilt binaries (#271)
+
+## [v0.4.0] - 2020-10-14
+
+v0.4.0 was yanked because it did not include the required pre-built binaries
+in the final crate.
+
+- Moved into cortex-m repository
+- Merge `HStdout` and `HStderr` into one type: `HostStream`
+- Support cortex-m v0.7
+- Semihosting macros no longer return a Result, instead errors are ignored.
+
+## [v0.3.7] - 2020-12-02
+
+- Replaces the yanked v0.3.6 by reverting #48, so the semihosting macros
+  continue to return a Result.
+
+## [v0.3.6] - 2020-12-01
+
+v0.3.6 was yanked because it incorrectly included #48, which was a breaking
+change.
+
+### Added
+
+- Update cortex-m dependency to support version 0.7.
+- Add `no-semihosting` feature to disable all semihosting calls.
+
+## [v0.3.5] - 2019-08-29
+
+### Added
+
+- Adds a feature to work around JLink quirks
+- Adds a dbg! macro using heprintln
+- Added thumbv8m.main support on stable
+
+### Fixed
+
+- Now Rust 2018 edition
+
+## [v0.3.4] - 2019-08-13
+
+### Fixed
+
+- Support for thumbv8 mainline hf target
+
+## [v0.3.3] - 2019-04-22
+
+### Added
+
+- Adds support for thumbv8 and cortex-m v0.6.0
+
+## [v0.3.2] - 2018-11-04
+
+### Added
+
+- Added a family of `hprint` macros for printing to the host standard output /
+  error via globally shared `HStdout` / `HStderr` handles .
+
+## [v0.3.1] - 2018-08-27
+
+### Changed
+
+- This crate no longer depends on `arm-none-eabi-gcc`.
+
+## [v0.3.0] - 2018-05-10
+
+### Changed
+
+- [breaking-change] `inline-asm` is no longer a default feature (i.e. a feature that's enabled by
+  default). The consequence is that this crate now compiles on 1.27 (beta) by default, and opting
+  into `inline-asm` requires nightly.
+
+## [v0.2.1] - 2018-04-25
+
+### Added
+
+- An opt-out "inline-asm" Cargo feature. When this feature is disabled semihosting is implemented
+  using an external assembly file instead of using the unstable inline assembly (`asm!`) feature
+  meaning that this crate can be compiled on stable.
+
 ## [v0.2.0] - 2017-07-07
 ## [v0.2.0] - 2017-07-07
 
 
 ### Added
 ### Added
@@ -56,8 +141,19 @@ This project adheres to [Semantic Versioning](http://semver.org/).
 
 
 - Initial release
 - Initial release
 
 
-[Unreleased]: https://github.com/japaric/cortex-m-semihosting/compare/v0.2.0...HEAD
-[v0.2.0]: https://github.com/japaric/cortex-m-semihosting/compare/v0.1.3...v0.2.0
-[v0.1.3]: https://github.com/japaric/cortex-m-semihosting/compare/v0.1.2...v0.1.3
-[v0.1.2]: https://github.com/japaric/cortex-m-semihosting/compare/v0.1.1...v0.1.2
-[v0.1.1]: https://github.com/japaric/cortex-m-semihosting/compare/v0.1.0...v0.1.1
+[Unreleased]: https://github.com/rust-embedded/cortex-m/compare/c-m-sh-v0.4.1...HEAD
+[v0.4.1]: https://github.com/rust-embedded/cortex-m/compare/c-m-sh-v0.4.0...c-m-sh-v0.4.1
+[v0.4.0]: https://github.com/rust-embedded/cortex-m/compare/c-m-sh-v0.3.5...c-m-sh-v0.4.0
+[v0.3.7]: https://github.com/rust-embedded/cortex-m-semihosting/compare/v0.3.6...v0.3.7
+[v0.3.6]: https://github.com/rust-embedded/cortex-m-semihosting/compare/v0.3.5...v0.3.6
+[v0.3.5]: https://github.com/rust-embedded/cortex-m-semihosting/compare/v0.3.4...v0.3.5
+[v0.3.4]: https://github.com/rust-embedded/cortex-m-semihosting/compare/v0.3.3...v0.3.4
+[v0.3.3]: https://github.com/rust-embedded/cortex-m-semihosting/compare/v0.3.2...v0.3.3
+[v0.3.2]: https://github.com/rust-embedded/cortex-m-semihosting/compare/v0.3.1...v0.3.2
+[v0.3.1]: https://github.com/rust-embedded/cortex-m-semihosting/compare/v0.3.0...v0.3.1
+[v0.3.0]: https://github.com/rust-embedded/cortex-m-semihosting/compare/v0.2.1...v0.3.0
+[v0.2.1]: https://github.com/rust-embedded/cortex-m-semihosting/compare/v0.2.0...v0.2.1
+[v0.2.0]: https://github.com/rust-embedded/cortex-m-semihosting/compare/v0.1.3...v0.2.0
+[v0.1.3]: https://github.com/rust-embedded/cortex-m-semihosting/compare/v0.1.2...v0.1.3
+[v0.1.2]: https://github.com/rust-embedded/cortex-m-semihosting/compare/v0.1.1...v0.1.2
+[v0.1.1]: https://github.com/rust-embedded/cortex-m-semihosting/compare/v0.1.0...v0.1.1

+ 14 - 1
Cargo.toml

@@ -1,11 +1,24 @@
 [package]
 [package]
-authors = ["Jorge Aparicio <japaricious@gmail.com>"]
+authors = [
+    "The Cortex-M Team <cortex-m@teams.rust-embedded.org>",
+    "Jorge Aparicio <japaricious@gmail.com>",
+]
 description = "Semihosting for RISCV processors"
 description = "Semihosting for RISCV processors"
 documentation = "https://docs.rs/riscv-semihosting"
 documentation = "https://docs.rs/riscv-semihosting"
 keywords = ["semihosting", "riscv"]
 keywords = ["semihosting", "riscv"]
+categories = ["no-std", "embedded"]
 license = "MIT OR Apache-2.0"
 license = "MIT OR Apache-2.0"
 name = "riscv-semihosting"
 name = "riscv-semihosting"
+readme = "README.md"
 repository = "https://github.com/riscv-rust/riscv-semihosting"
 repository = "https://github.com/riscv-rust/riscv-semihosting"
 version = "0.0.1"
 version = "0.0.1"
+edition = "2018"
+
+[features]
+default = ["inline-asm", "jlink-quirks"]
+inline-asm = []
+jlink-quirks = []
+no-semihosting = []
 
 
 [dependencies]
 [dependencies]
+riscv = "0.7.0"

+ 18 - 2
README.md

@@ -5,15 +5,22 @@
 
 
 > Semihosting for ARM Cortex-M processors
 > Semihosting for ARM Cortex-M processors
 
 
+This project is developed and maintained by the [Cortex-M team][team].
+
 ## [Documentation](https://docs.rs/cortex-m-semihosting)
 ## [Documentation](https://docs.rs/cortex-m-semihosting)
 
 
+# Minimum Supported Rust Version (MSRV)
+
+This crate is guaranteed to compile on stable Rust 1.33.0 and up. It *might*
+compile with older versions but that may change in any new patch release.
+
 ## License
 ## License
 
 
 Licensed under either of
 Licensed under either of
 
 
-- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or
+- Apache License, Version 2.0 ([LICENSE-APACHE](../LICENSE-APACHE) or
   http://www.apache.org/licenses/LICENSE-2.0)
   http://www.apache.org/licenses/LICENSE-2.0)
-- MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
+- MIT license ([LICENSE-MIT](../LICENSE-MIT) or http://opensource.org/licenses/MIT)
 
 
 at your option.
 at your option.
 
 
@@ -22,3 +29,12 @@ at your option.
 Unless you explicitly state otherwise, any contribution intentionally submitted
 Unless you explicitly state otherwise, any contribution intentionally submitted
 for inclusion in the work by you, as defined in the Apache-2.0 license, shall be
 for inclusion in the work by you, as defined in the Apache-2.0 license, shall be
 dual licensed as above, without any additional terms or conditions.
 dual licensed as above, without any additional terms or conditions.
+
+## Code of Conduct
+
+Contribution to this crate is organized under the terms of the [Rust Code of
+Conduct][CoC], the maintainer of this crate, the [Cortex-M team][team], promises
+to intervene to uphold that code of conduct.
+
+[CoC]: ../CODE_OF_CONDUCT.md
+[team]: https://github.com/rust-embedded/wg#the-cortex-m-team

+ 2 - 4
src/debug.rs

@@ -8,10 +8,8 @@
 //!
 //!
 //! Target program:
 //! Target program:
 //!
 //!
-//! ```
-//! #[macro_use]
-//! extern crate cortex_m_semihosting;
-//! use cortex_m_semihosting::debug::{self, EXIT_SUCCESS, EXIT_FAILURE};
+//! ```no_run
+//! use riscv_semihosting::debug::{self, EXIT_SUCCESS, EXIT_FAILURE};
 //!
 //!
 //! fn main() {
 //! fn main() {
 //!     if 2 == 2 {
 //!     if 2 == 2 {

+ 51 - 0
src/export.rs

@@ -0,0 +1,51 @@
+//! IMPLEMENTATION DETAILS USED BY MACROS
+
+use core::fmt::{self, Write};
+
+use riscv::interrupt;
+
+use crate::hio::{self, HostStream};
+
+static mut HSTDOUT: Option<HostStream> = None;
+
+pub fn hstdout_str(s: &str) {
+    let _result = interrupt::free(|_| unsafe {
+        if HSTDOUT.is_none() {
+            HSTDOUT = Some(hio::hstdout()?);
+        }
+
+        HSTDOUT.as_mut().unwrap().write_str(s).map_err(drop)
+    });
+}
+
+pub fn hstdout_fmt(args: fmt::Arguments) {
+    let _result = interrupt::free(|_| unsafe {
+        if HSTDOUT.is_none() {
+            HSTDOUT = Some(hio::hstdout()?);
+        }
+
+        HSTDOUT.as_mut().unwrap().write_fmt(args).map_err(drop)
+    });
+}
+
+static mut HSTDERR: Option<HostStream> = None;
+
+pub fn hstderr_str(s: &str) {
+    let _result = interrupt::free(|_| unsafe {
+        if HSTDERR.is_none() {
+            HSTDERR = Some(hio::hstderr()?);
+        }
+
+        HSTDERR.as_mut().unwrap().write_str(s).map_err(drop)
+    });
+}
+
+pub fn hstderr_fmt(args: fmt::Arguments) {
+    let _result = interrupt::free(|_| unsafe {
+        if HSTDERR.is_none() {
+            HSTDERR = Some(hio::hstderr()?);
+        }
+
+        HSTDERR.as_mut().unwrap().write_fmt(args).map_err(drop)
+    });
+}

+ 18 - 34
src/hio.rs

@@ -1,64 +1,46 @@
 //! Host I/O
 //! Host I/O
 
 
+use crate::nr;
 use core::{fmt, slice};
 use core::{fmt, slice};
-use nr;
 
 
-/// Host's standard error
-pub struct HStderr {
+/// A byte stream to the host (e.g., host's stdout or stderr).
+#[derive(Clone, Copy)]
+pub struct HostStream {
     fd: usize,
     fd: usize,
 }
 }
 
 
-impl HStderr {
+impl HostStream {
     /// Attempts to write an entire `buffer` into this sink
     /// Attempts to write an entire `buffer` into this sink
     pub fn write_all(&mut self, buffer: &[u8]) -> Result<(), ()> {
     pub fn write_all(&mut self, buffer: &[u8]) -> Result<(), ()> {
         write_all(self.fd, buffer)
         write_all(self.fd, buffer)
     }
     }
 }
 }
 
 
-impl fmt::Write for HStderr {
-    fn write_str(&mut self, s: &str) -> fmt::Result {
-        self.write_all(s.as_bytes()).map_err(|_| fmt::Error)
-    }
-}
-
-/// Host's standard output
-pub struct HStdout {
-    fd: usize,
-}
-
-impl HStdout {
-    /// Attempts to write an entire `buffer` into this sink
-    pub fn write_all(&mut self, buffer: &[u8]) -> Result<(), ()> {
-        write_all(self.fd, buffer)
-    }
-}
-
-impl fmt::Write for HStdout {
+impl fmt::Write for HostStream {
     fn write_str(&mut self, s: &str) -> fmt::Result {
     fn write_str(&mut self, s: &str) -> fmt::Result {
         self.write_all(s.as_bytes()).map_err(|_| fmt::Error)
         self.write_all(s.as_bytes()).map_err(|_| fmt::Error)
     }
     }
 }
 }
 
 
 /// Construct a new handle to the host's standard error.
 /// Construct a new handle to the host's standard error.
-pub fn hstderr() -> Result<HStderr, ()> {
+pub fn hstderr() -> Result<HostStream, ()> {
     // There is actually no stderr access in ARM Semihosting documentation. Use
     // There is actually no stderr access in ARM Semihosting documentation. Use
     // convention used in libgloss.
     // convention used in libgloss.
     // See: libgloss/arm/syscalls.c, line 139.
     // See: libgloss/arm/syscalls.c, line 139.
     // https://sourceware.org/git/gitweb.cgi?p=newlib-cygwin.git;a=blob;f=libgloss/arm/syscalls.c#l139
     // https://sourceware.org/git/gitweb.cgi?p=newlib-cygwin.git;a=blob;f=libgloss/arm/syscalls.c#l139
-    open(":tt\0", nr::open::W_APPEND).map(|fd| HStderr { fd })
+    open(":tt\0", nr::open::W_APPEND)
 }
 }
 
 
 /// Construct a new handle to the host's standard output.
 /// Construct a new handle to the host's standard output.
-pub fn hstdout() -> Result<HStdout, ()> {
-    open(":tt\0", nr::open::W_TRUNC).map(|fd| HStdout { fd })
+pub fn hstdout() -> Result<HostStream, ()> {
+    open(":tt\0", nr::open::W_TRUNC)
 }
 }
 
 
-fn open(name: &str, mode: usize) -> Result<usize, ()> {
+fn open(name: &str, mode: usize) -> Result<HostStream, ()> {
     let name = name.as_bytes();
     let name = name.as_bytes();
-    match unsafe { syscall!(OPEN, name.as_ptr(), mode, name.len() - 1) } as
-        isize {
+    match unsafe { syscall!(OPEN, name.as_ptr(), mode, name.len() - 1) } as isize {
         -1 => Err(()),
         -1 => Err(()),
-        fd => Ok(fd as usize),
+        fd => Ok(HostStream { fd: fd as usize }),
     }
     }
 }
 }
 
 
@@ -70,10 +52,12 @@ fn write_all(fd: usize, mut buffer: &[u8]) -> Result<(), ()> {
             // `n` bytes were not written
             // `n` bytes were not written
             n if n <= buffer.len() => {
             n if n <= buffer.len() => {
                 let offset = (buffer.len() - n) as isize;
                 let offset = (buffer.len() - n) as isize;
-                buffer = unsafe {
-                    slice::from_raw_parts(buffer.as_ptr().offset(offset), n)
-                }
+                buffer = unsafe { slice::from_raw_parts(buffer.as_ptr().offset(offset), n) }
             }
             }
+            #[cfg(feature = "jlink-quirks")]
+            // Error (-1) - should be an error but JLink can return -1, -2, -3,...
+            // For good measure, we allow up to negative 15.
+            n if n > 0xfffffff0 => return Ok(()),
             // Error
             // Error
             _ => return Err(()),
             _ => return Err(()),
         }
         }

+ 131 - 76
src/lib.rs

@@ -2,40 +2,46 @@
 //!
 //!
 //! # What is semihosting?
 //! # What is semihosting?
 //!
 //!
-//! "Semihosting is a mechanism that enables code running on a RISCV target to
-//!  communicate and use the Input/Output facilities on a host computer that is
-//!  running a debugger." - RISCV
+//! "Semihosting is a mechanism that enables code running on an ARM target to communicate and use
+//! the Input/Output facilities on a host computer that is running a debugger." - ARM
 //!
 //!
 //! # Interface
 //! # Interface
 //!
 //!
-//! Since semihosting operations are modeled as [system calls][sc], this crate
-//! exposes an untyped `syscall!` interface just like the [`sc`] crate does.
+//! This crate provides implementations of
+//! [`core::fmt::Write`](https://doc.rust-lang.org/core/fmt/trait.Write.html), so you can use it,
+//! in conjunction with
+//! [`core::format_args!`](https://doc.rust-lang.org/core/macro.format_args.html) or the [`write!` macro](https://doc.rust-lang.org/core/macro.write.html), for user-friendly construction and printing of formatted strings.
+//!
+//! Since semihosting operations are modeled as [system calls][sc], this crate exposes an untyped
+//! `syscall!` interface just like the [`sc`] crate does.
 //!
 //!
 //! [sc]: https://en.wikipedia.org/wiki/System_call
 //! [sc]: https://en.wikipedia.org/wiki/System_call
 //! [`sc`]: https://crates.io/crates/sc
 //! [`sc`]: https://crates.io/crates/sc
 //!
 //!
 //! # Forewarning
 //! # Forewarning
 //!
 //!
-//! Semihosting operations are *very* slow. Like, each WRITE operation can take
-//! hundreds of milliseconds.
+//! Semihosting operations are *very* slow. Like, each WRITE operation can take hundreds of
+//! milliseconds.
 //!
 //!
 //! # Example
 //! # Example
 //!
 //!
-//! This example will show how to print "Hello, world!" on the host.
+//! ## Using `hio::hstdout`
 //!
 //!
-//! Target program:
+//! This example will demonstrate how to print formatted strings.
 //!
 //!
-//! ```
-//! #[macro_use]
-//! extern crate riscv_semihosting;
+//! ```no_run
+//! use riscv_semihosting::hio;
+//! use core::fmt::Write;
 //!
 //!
-//! fn main() {
-//!     // File descriptor (on the host)
-//!     const STDOUT: usize = 1; // NOTE the host stdout may not always be fd 1
-//!     static MSG: &'static [u8] = b"Hello, world!\n";
+//! // This function will be called by the application
+//! fn print() -> Result<(), core::fmt::Error> {
+//!     let mut stdout = hio::hstdout().map_err(|_| core::fmt::Error)?;
+//!     let language = "Rust";
+//!     let ranking = 1;
 //!
 //!
-//!     // Signature: fn write(fd: usize, ptr: *const u8, len: usize) -> usize
-//!     let r = unsafe { syscall!(WRITE, STDOUT, MSG.as_ptr(), MSG.len()) };
+//!     write!(stdout, "{} on embedded is #{}!", language, ranking)?;
+//!
+//!     Ok(())
 //! }
 //! }
 //! ```
 //! ```
 //!
 //!
@@ -50,8 +56,8 @@
 //! # the command will block at this point
 //! # the command will block at this point
 //! ```
 //! ```
 //!
 //!
-//! The OpenOCD logs will be redirected to `/tmp/openocd.log`. You can view
-//! those logs in "real time" using `tail`
+//! The OpenOCD logs will be redirected to `/tmp/openocd.log`. You can view those logs in "real
+//! time" using `tail`
 //!
 //!
 //! ``` text
 //! ``` text
 //! $ tail -f /tmp/openocd.log
 //! $ tail -f /tmp/openocd.log
@@ -63,24 +69,23 @@
 //! Info : nrf51.cpu: hardware has 4 breakpoints, 2 watchpoints
 //! Info : nrf51.cpu: hardware has 4 breakpoints, 2 watchpoints
 //! ```
 //! ```
 //!
 //!
-//! Alternatively you could omit the `-l` flag from the `openocd` call, and the
-//! `tail -f` command but the OpenOCD output will have intermingled in it logs
-//! from its normal operation.
+//! Alternatively you could omit the `-l` flag from the `openocd` call, and the `tail -f` command
+//! but the OpenOCD output will have intermingled in it logs from its normal operation.
 //!
 //!
 //! Then, we run the program:
 //! Then, we run the program:
 //!
 //!
 //! ``` text
 //! ``` text
 //! $ arm-none-eabi-gdb hello-world
 //! $ arm-none-eabi-gdb hello-world
-//! # Connect to OpenOCD
+//! (gdb) # Connect to OpenOCD
 //! (gdb) target remote :3333
 //! (gdb) target remote :3333
 //!
 //!
-//! # Enable OpenOCD's semihosting support
+//! (gdb) # Enable OpenOCD's semihosting support
 //! (gdb) monitor arm semihosting enable
 //! (gdb) monitor arm semihosting enable
 //!
 //!
-//! # Flash the program
+//! (gdb) # Flash the program
 //! (gdb) load
 //! (gdb) load
 //!
 //!
-//! # Run the program
+//! (gdb) # Run the program
 //! (gdb) continue
 //! (gdb) continue
 //! ```
 //! ```
 //!
 //!
@@ -89,9 +94,82 @@
 //! ``` text
 //! ``` text
 //! # openocd -f $INTERFACE -f $TARGET -l /tmp/openocd.log
 //! # openocd -f $INTERFACE -f $TARGET -l /tmp/openocd.log
 //! (..)
 //! (..)
-//! Hello, world!
+//! Rust on embedded is #1!
+//! ```
+//! ## Using the syscall interface
+//!
+//! This example will show how to print "Hello, world!" on the host.
+//!
+//! Target program:
+//!
+//! ```no_run
+//! use riscv_semihosting::syscall;
+//!
+//! // This function will be called by the application
+//! fn print() {
+//!     // File descriptor (on the host)
+//!     const STDOUT: usize = 1; // NOTE the host stdout may not always be fd 1
+//!     static MSG: &'static [u8] = b"Hello, world!\n";
+//!
+//!     // Signature: fn write(fd: usize, ptr: *const u8, len: usize) -> usize
+//!     let r = unsafe { syscall!(WRITE, STDOUT, MSG.as_ptr(), MSG.len()) };
+//! }
+//! ```
+//! Output and monitoring proceed as in the above example.
+//!
+//! ## The `dbg!` macro
+//!
+//! Analogous to [`std::dbg`](https://doc.rust-lang.org/std/macro.dbg.html) the macro
+//! `dbg!` returns a given expression and prints it using `heprintln!` including context
+//! for quick and dirty debugging.
+//!
+//! Panics if `heprintln!` returns an error.
+//!
+//! Example:
+//!
+//! ```no_run
+//! const UUID: *mut u32 = 0x0009_FC70 as *mut u32;
+//! dbg!(UUID);
+//! let mut uuid: [u32; 4] = [0; 4];
+//! for i in 0..4 {
+//!     dbg!(i);
+//!     uuid[i] = unsafe { dbg!(UUID.offset(i as isize).read_volatile()) };
+//! }
+//! ```
+//! outputs
+//! ```text
+//! [examples/semihosting.rs:37] UUID = 0x0009fc70
+//! [examples/semihosting.rs:40] i = 0
+//! [examples/semihosting.rs:41] UUID.offset(i as isize).read_volatile() = 3370045464
+//! [examples/semihosting.rs:40] i = 1
+//! [examples/semihosting.rs:41] UUID.offset(i as isize).read_volatile() = 1426218275
+//! [examples/semihosting.rs:40] i = 2
+//! [examples/semihosting.rs:41] UUID.offset(i as isize).read_volatile() = 2422621116
+//! [examples/semihosting.rs:40] i = 3
+//! [examples/semihosting.rs:41] UUID.offset(i as isize).read_volatile() = 1044138593
 //! ```
 //! ```
 //!
 //!
+//! # Optional features
+//!
+//! ## `inline-asm`
+//!
+//! When this feature is enabled semihosting is implemented using inline assembly (`llvm_asm!`) and
+//! compiling this crate requires nightly.
+//!
+//! When this feature is disabled semihosting is implemented using FFI calls into an external
+//! assembly file and compiling this crate works on stable and beta.
+//!
+//! ## `jlink-quirks`
+//!
+//! When this feature is enabled, return values above `0xfffffff0` from semihosting operation
+//! `SYS_WRITE` (0x05) are interpreted as if the entire buffer had been written. The current
+//! latest version 6.48b of J-Link exhibits such behaviour, causing a panic if this feature
+//! is not enabled.
+//!
+//! ## `no-semihosting`
+//!
+//! When this feature is enabled, the underlying system calls are patched out.
+//!
 //! # Reference
 //! # Reference
 //!
 //!
 //! For documentation about the semihosting operations, check:
 //! For documentation about the semihosting operations, check:
@@ -101,69 +179,46 @@
 //!
 //!
 //! [pdf]: http://infocenter.arm.com/help/topic/com.arm.doc.dui0471e/DUI0471E_developing_for_arm_processors.pdf
 //! [pdf]: http://infocenter.arm.com/help/topic/com.arm.doc.dui0471e/DUI0471E_developing_for_arm_processors.pdf
 
 
+#![cfg_attr(feature = "inline-asm", feature(llvm_asm))]
 #![deny(missing_docs)]
 #![deny(missing_docs)]
-#![deny(warnings)]
-#![feature(asm)]
 #![no_std]
 #![no_std]
 
 
 #[macro_use]
 #[macro_use]
 mod macros;
 mod macros;
 
 
 pub mod debug;
 pub mod debug;
+#[doc(hidden)]
+pub mod export;
 pub mod hio;
 pub mod hio;
 pub mod nr;
 pub mod nr;
 
 
-/// The hint that differentiates the semihosting call.
-// WARNING: This variable is hardcoded in the asm! Don't forget to update
-// if it changes.
-pub const RISCV_SEMIHOSTING_CALL_NUMBER: usize = 7;
-
-/// Performs a semihosting operation, takes a pointer to an argument block
-#[inline]
-#[cfg(target_arch = "riscv")]
-pub unsafe fn syscall<T>(mut nr: usize, arg: &T) -> usize {
-    // .option push
-    // .option norvc
-    asm!(r"
-      slli x0, x0, 0x1f
-      ebreak
-      srai x0, x0, 0x7
-    "
-         : "+{x10}"(nr)
-         : "{x11}"(arg)
-         : "memory"
-         : "volatile");
-    // .option pop
-    nr
+#[cfg(not(feature = "inline-asm"))]
+extern "C" {
+    fn __sh_syscall(nr: usize, arg: usize) -> usize;
 }
 }
 
 
 /// Performs a semihosting operation, takes a pointer to an argument block
 /// Performs a semihosting operation, takes a pointer to an argument block
-#[cfg(not(target_arch = "riscv"))]
-pub unsafe fn syscall<T>(_nr: usize, _arg: &T) -> usize {
-    0
+#[inline(always)]
+pub unsafe fn syscall<T>(nr: usize, arg: &T) -> usize {
+    syscall1(nr, arg as *const T as usize)
 }
 }
 
 
 /// Performs a semihosting operation, takes one integer as an argument
 /// Performs a semihosting operation, takes one integer as an argument
-#[inline]
-#[cfg(target_arch = "riscv")]
-pub unsafe fn syscall1(mut nr: usize, arg: usize) -> usize {
-    // .option push
-    // .option norvc
-    asm!(r"
-      slli x0, x0, 0x1f
-      ebreak
-      srai x0, x0, 0x7
-    "
-         : "+{x10}"(nr)
-         : "{x11}"(arg)
-         : "memory"
-         : "volatile");
-    // .option pop
-    nr
-}
-
-/// Performs a semihosting operation, takes one integer as an argument
-#[cfg(not(target_arch = "riscv"))]
+#[inline(always)]
 pub unsafe fn syscall1(_nr: usize, _arg: usize) -> usize {
 pub unsafe fn syscall1(_nr: usize, _arg: usize) -> usize {
-    0
+    match () {
+        #[cfg(all(feature = "inline-asm", not(feature = "no-semihosting")))]
+        () => {
+            let mut nr = _nr;
+            llvm_asm!("
+                slli x0, x0, 0x1f
+                ebreak
+                srai x0, x0, 0x7
+            " : "+{a0}"(nr) : "{a1}"(_arg) :: "volatile");
+            nr
+        }
+
+        #[cfg(feature = "no-semihosting")]
+        () => 0,
+    }
 }
 }

+ 96 - 5
src/macros.rs

@@ -11,19 +11,110 @@ macro_rules! syscall {
         $crate::syscall($crate::nr::$nr, &[$a1 as usize, $a2 as usize])
         $crate::syscall($crate::nr::$nr, &[$a1 as usize, $a2 as usize])
     };
     };
     ($nr:ident, $a1:expr, $a2:expr, $a3:expr) => {
     ($nr:ident, $a1:expr, $a2:expr, $a3:expr) => {
-        $crate::syscall($crate::nr::$nr, &[$a1 as usize, $a2 as usize,
-                                           $a3 as usize])
+        $crate::syscall($crate::nr::$nr, &[$a1 as usize, $a2 as usize, $a3 as usize])
     };
     };
     ($nr:ident, $a1:expr, $a2:expr, $a3:expr, $a4:expr) => {
     ($nr:ident, $a1:expr, $a2:expr, $a3:expr, $a4:expr) => {
-        $crate::syscall($crate::nr::$nr, &[$a1 as usize, $a2 as usize,
-                                           $a3 as usize, $a4 as usize])
+        $crate::syscall(
+            $crate::nr::$nr,
+            &[$a1 as usize, $a2 as usize, $a3 as usize, $a4 as usize],
+        )
     };
     };
 }
 }
 
 
-/// Macro version of `syscall1`
+/// Macro version of `syscall1`.
 #[macro_export]
 #[macro_export]
 macro_rules! syscall1 {
 macro_rules! syscall1 {
     ($nr:ident, $a1:expr) => {
     ($nr:ident, $a1:expr) => {
         $crate::syscall1($crate::nr::$nr, $a1 as usize)
         $crate::syscall1($crate::nr::$nr, $a1 as usize)
     };
     };
 }
 }
+
+/// Macro for printing to the HOST standard output.
+///
+/// This is similar to the `print!` macro in the standard library. Both will panic on any failure to
+/// print.
+#[macro_export]
+macro_rules! hprint {
+    ($s:expr) => {
+        $crate::export::hstdout_str($s)
+    };
+    ($($tt:tt)*) => {
+        $crate::export::hstdout_fmt(format_args!($($tt)*))
+    };
+}
+
+/// Macro for printing to the HOST standard output, with a newline.
+///
+/// This is similar to the `println!` macro in the standard library. Both will panic on any failure to
+/// print.
+#[macro_export]
+macro_rules! hprintln {
+    () => {
+        $crate::export::hstdout_str("\n")
+    };
+    ($s:expr) => {
+        $crate::export::hstdout_str(concat!($s, "\n"))
+    };
+    ($s:expr, $($tt:tt)*) => {
+        $crate::export::hstdout_fmt(format_args!(concat!($s, "\n"), $($tt)*))
+    };
+}
+
+/// Macro for printing to the HOST standard error.
+///
+/// This is similar to the `eprint!` macro in the standard library. Both will panic on any failure
+/// to print.
+#[macro_export]
+macro_rules! heprint {
+    ($s:expr) => {
+        $crate::export::hstderr_str($s)
+    };
+    ($($tt:tt)*) => {
+        $crate::export::hstderr_fmt(format_args!($($tt)*))
+    };
+}
+
+/// Macro for printing to the HOST standard error, with a newline.
+///
+/// This is similar to the `eprintln!` macro in the standard library. Both will panic on any failure
+/// to print.
+#[macro_export]
+macro_rules! heprintln {
+    () => {
+        $crate::export::hstderr_str("\n")
+    };
+    ($s:expr) => {
+        $crate::export::hstderr_str(concat!($s, "\n"))
+    };
+    ($s:expr, $($tt:tt)*) => {
+        $crate::export::hstderr_fmt(format_args!(concat!($s, "\n"), $($tt)*))
+    };
+}
+
+/// Macro that prints and returns the value of a given expression for quick and
+/// dirty debugging.
+///
+/// Works exactly like `dbg!` in the standard library, replacing `eprintln!`
+/// with `heprintln!`.
+#[macro_export]
+macro_rules! dbg {
+    () => {
+        $crate::heprintln!("[{}:{}]", file!(), line!());
+    };
+    ($val:expr) => {
+        // Use of `match` here is intentional because it affects the lifetimes
+        // of temporaries - https://stackoverflow.com/a/48732525/1063961
+        match $val {
+            tmp => {
+                $crate::heprintln!("[{}:{}] {} = {:#?}",
+                    file!(), line!(), stringify!($val), &tmp);
+                tmp
+            }
+        }
+    };
+    // Trailing comma with single argument is ignored
+    ($val:expr,) => { $crate::dbg!($val) };
+    ($($val:expr),+ $(,)?) => {
+        ($($crate::dbg!($val)),+,)
+    };
+}