Split `Syscalls` into `Syscalls` and `RawSyscalls`.

`RawSyscalls` contains everything I previously called the "low-level API". `Syscalls` contains everything I previously called the "high-level API". A generic impl implements `Syscalls` for every type that implements `RawSyscalls`.
diff --git a/core/platform/src/lib.rs b/core/platform/src/lib.rs
index e746d32..699d3da 100644
--- a/core/platform/src/lib.rs
+++ b/core/platform/src/lib.rs
@@ -10,10 +10,10 @@
 
 mod allows;
 mod error_code;
-mod syscall_types;
+mod raw_syscalls;
 mod syscalls;
 
 pub use allows::{AllowReadable, Allowed};
 pub use error_code::ErrorCode;
-pub use syscall_types::{OneArgMemop, ReturnType, YieldType, ZeroArgMemop};
+pub use raw_syscalls::{OneArgMemop, RawSyscalls, YieldType, ZeroArgMemop};
 pub use syscalls::Syscalls;
diff --git a/core/platform/src/raw_syscalls.rs b/core/platform/src/raw_syscalls.rs
new file mode 100644
index 0000000..b3cfba4
--- /dev/null
+++ b/core/platform/src/raw_syscalls.rs
@@ -0,0 +1,190 @@
+// TODO: Implement `libtock_runtime` and `libtock_unittest`, which are
+// referenced in the comment on `RawSyscalls`.
+
+/// `RawSyscalls` allows a fake Tock kernel to be injected into components for
+/// unit testing. It is implemented by `libtock_runtime::TockSyscalls` and
+/// `libtock_unittest::FakeSyscalls`. Components should not use `RawSyscalls`
+/// directly; instead, use the `Syscalls` trait, which provides higher-level
+/// interfaces to the system calls.
+
+// RawSyscalls is designed to minimize the amount of handwritten assembly code
+// needed without generating unnecessary instructions. There are a few major
+// factors affecting its design:
+//     1. Most system calls only clobber r0-r4, while yield has a far longer
+//        clobber list. As such, yield must have its own assembly
+//        implementation.
+//     2. The compiler is unable to optimize away unused arguments. For example,
+//        memop's "get process RAM start address" operation only needs r0 set,
+//        while memop's "break" operation needs both r0 and r1 set. If our
+//        inline assembly calls "get process RAM start address" but sets both r0
+//        and r1, the compiler doesn't know that r1 will be
+//        ignored so setting that register will not be optimized away. Therefore
+//        we want to set the minimum number of argument registers possible.
+//     3. The cost of specifying unused return registers is only that of
+//        unnecessarily marking a register as clobbered. Explanation: After
+//        inlining, an unused register is marked as "changed by the assembly"
+//        but can immediately be re-used by the compiler, which is the same as a
+//        clobbered register. System calls should generally be
+//        inlined -- and even if they aren't, the unused return values will
+//        probably be passed in caller-saved registers (this is true for the C
+//        ABI, so probably true for the Rust ABI), which are treated as
+//        clobbered regardless.
+//
+// Currently, yield takes exactly one argument, to specify what yield type to
+// do. Therefore we only need one raw yield call.
+//
+// Subscribe, command, read-write allow, and read-only allow all take four
+// argument types. Even when calling command IDs that have unused arguments, we
+// still need to clear the argument registers so as to avoid passing
+// confidential data to capsules (this is in line with Tock's threat model). As
+// such, four_arg_syscall() is used for all subscribe, command, read-only allow,
+// and read-write allow system calls.
+//
+// Memop takes 1 or 2 arguments (operation and an optional argument). Because it
+// is part of the core kernel, it is okay for us to leave arbitrary data in the
+// argument register for operations where the argument register is unused
+// (again, in line with Tock's threat model). Memop returns up to 2 return
+// arguments, so we don't need to mark r2 and r3 as clobbered. As such, we need
+// two raw memop calls: one for operations without an argument and one for
+// operations with an argument.
+//
+// Because the variables passed in and out of raw system calls represent
+// register values, they are of type usize. In cases where it doesn't make sense
+// to pass a pointer-sized value, libtock_unittest::FakeSyscalls may panic if a
+// too-large value is passed.
+pub trait RawSyscalls {
+    // raw_yield should:
+    //     1. Call syscall class 0
+    //     2. Use register r0 for input and output as an inlateout register,
+    //        passing in r0_in and returning its value.
+    //     3. Mark all caller-saved registers as lateout clobbers.
+    //     4. NOT provide any of the following options:
+    //            pure             (yield has side effects)
+    //            nomem            (a callback can read + write globals)
+    //            readonly         (a callback can write globals)
+    //            preserves_flags  (a callback can change flags)
+    //            noreturn         (yield is expected to return)
+    //            nostack          (a callback needs the stack)
+    //
+    // Design note: This is safe because the yield types that currently exist
+    // are safe. If an unsafe yield type is added, we will need to make
+    // raw_yield unsafe. Although raw_yield shouldn't be called by code outside
+    // this crate, it can be, so that is a backwards-incompatible change. We
+    // pass YieldType rather than a usize because if we used usize directly then
+    // this API becomes unsound if the kernel adds support for an unsafe yield
+    // type (or even one that takes one more argument).
+    fn raw_yield(r0_in: YieldType) -> usize;
+
+    // four_arg_syscall is used to invoke the subscribe, command, read-write
+    // allow, and read-only allow system calls.
+    //
+    // four_arg_syscall's inline assembly should have the following properties:
+    //     1. Calls the syscall class specified by class
+    //     2. Passes r0-r3 in the corresponding registers as inlateout
+    //        registers. Returns r0-r3 in order.
+    //     3. Does not mark any registers as clobbered.
+    //     4. Has all of the following options:
+    //            preserves_flags  (these system calls do not touch flags)
+    //            nostack          (these system calls do not touch the stack)
+    //     5. Does NOT have any of the following options:
+    //            pure      (these system calls have side effects)
+    //            nomem     (the compiler needs to write to globals before allow)
+    //            readonly  (rw allow can modify memory)
+    //            noreturn  (all these system calls are expected to return)
+    //
+    /// # Safety
+    /// `four_arg_syscall` must NOT be used to invoke yield. Otherwise, it has
+    /// the same safety invariants as the underlying system call, which varies
+    /// depending on the system call class.
+    unsafe fn four_arg_syscall(
+        r0: usize,
+        r1: usize,
+        r2: usize,
+        r3: usize,
+        class: u8,
+    ) -> (usize, usize, usize, usize);
+
+    // zero_arg_memop is used to invoke memop operations that do not accept an
+    // argument register. Because there are no memop commands that set r2 or r3,
+    // this only needs to return r0 and r1.
+    //
+    // Memop commands may panic in the unit test environment, as not all memop
+    // calls can be sensibly implemented in that environment.
+    //
+    // zero_arg_memop's inline assembly should have the following properties:
+    //     1. Calls syscall class 5
+    //     2. Specifies r0 as an inlateout register, and r1 as a lateout
+    //        register.
+    //     3. Does not mark any registers as clobbered.
+    //     4. Has all of the following options:
+    //            preserves_flags
+    //            nostack
+    //            nomem            (it is okay for the compiler to cache globals
+    //                              across memop calls)
+    //     5. Does NOT have any of the following options:
+    //            pure      (two invocations of the same memop can return
+    //                       different values)
+    //            readonly  (incompatible with nomem)
+    //            noreturn
+    //
+    // Design note: like raw_yield, this is safe because memops that currently
+    // exist are safe. zero_arg_memop takes a ZeroArgMemop rather than a usize
+    // so that if the kernel adds an unsafe memop this API doesn't become
+    // unsound.
+    fn zero_arg_memop(r0_in: ZeroArgMemop) -> (usize, usize);
+
+    // one_arg_memop is used to invoke memop operations that take an argument.
+    // Because there are no memop operations that set r2 or r3, this only needs
+    // to return r0 and r1.
+    //
+    // one_arg_memop's inline assembly should:
+    //     1. Call syscall class 5
+    //     2. Specify r0 and r1 as inlateout registers, and return (r0, r1)
+    //     3. Not mark any registers as clobbered.
+    //     4. Have all of the following options:
+    //            preserves_flags
+    //            nostack
+    //            nomem            (the compiler can cache globals across memop
+    //                              calls)
+    //     5. Does NOT have any of the following options:
+    //            pure      Two invocations of sbrk can return different values
+    //            readonly  Incompatible with nomem
+    //            noreturn
+    //
+    // Design note: like raw_yield, this is safe because memops that currently
+    // exist are safe. zero_arg_memop takes a ZeroArgMemop rather than a usize
+    // so that if the kernel adds an unsafe memop this API doesn't become
+    // unsound.
+    fn one_arg_memop(r0_in: OneArgMemop, r1: usize) -> (usize, usize);
+}
+
+#[non_exhaustive]
+#[repr(usize)]
+pub enum OneArgMemop {
+    Brk = 0,
+    Sbrk = 1,
+    FlashRegionStart = 8,
+    FlashRegionEnd = 9,
+    SpecifyStackTop = 10,
+    SpecifyHeapStart = 11,
+}
+
+// TODO: When the numeric values (0 and 1) are assigned to the yield types,
+// specify those values here.
+#[non_exhaustive]
+#[repr(usize)]
+pub enum YieldType {
+    Wait,
+    NoWait,
+}
+
+#[non_exhaustive]
+#[repr(usize)]
+pub enum ZeroArgMemop {
+    MemoryStart = 2,
+    MemoryEnd = 3,
+    FlashStart = 4,
+    FlashEnd = 5,
+    GrantStart = 6,
+    FlashRegions = 7,
+}
diff --git a/core/platform/src/syscall_types.rs b/core/platform/src/syscall_types.rs
deleted file mode 100644
index d44df9d..0000000
--- a/core/platform/src/syscall_types.rs
+++ /dev/null
@@ -1,46 +0,0 @@
-// Contains various types used by the `Syscalls` trait. These are in a separate
-// file from `Syscalls` to keep the size of syscalls.rs reasonable.
-
-#[non_exhaustive]
-#[repr(usize)]
-pub enum OneArgMemop {
-    Brk = 0,
-    Sbrk = 1,
-    FlashRegionStart = 8,
-    FlashRegionEnd = 9,
-    SpecifyStackTop = 10,
-    SpecifyHeapStart = 11,
-}
-
-pub enum ReturnType {
-    Failure = 0,
-    FailureWithU32 = 1,
-    FailureWith2U32 = 2,
-    FailureWithU64 = 3,
-    Success = 128,
-    SuccessWithU32 = 129,
-    SuccessWith2U32 = 130,
-    SuccessWithU64 = 131,
-    SuccessWith3U32 = 132,
-    SuccessWithU32AndU64 = 133,
-}
-
-// TODO: When the numeric values (0 and 1) are assigned to the yield types,
-// specify those values here.
-#[non_exhaustive]
-#[repr(usize)]
-pub enum YieldType {
-    Wait,
-    NoWait,
-}
-
-#[non_exhaustive]
-#[repr(usize)]
-pub enum ZeroArgMemop {
-    MemoryStart = 2,
-    MemoryEnd = 3,
-    FlashStart = 4,
-    FlashEnd = 5,
-    GrantStart = 6,
-    FlashRegions = 7,
-}
diff --git a/core/platform/src/syscalls.rs b/core/platform/src/syscalls.rs
index c9e7f98..fc3207e 100644
--- a/core/platform/src/syscalls.rs
+++ b/core/platform/src/syscalls.rs
@@ -1,199 +1,55 @@
 // TODO: Implement `libtock_runtime` and `libtock_unittest`, which are
 // referenced in the comment on `Syscalls`.
 
-use crate::syscall_types::{OneArgMemop, ReturnType, YieldType, ZeroArgMemop};
+use crate::raw_syscalls::{RawSyscalls, YieldType};
 
-/// `Syscalls` serves two purposes:
-///     High level: It provides safe abstractions over Tock's raw system calls.
-///     Low level:  It allows a fake Tock kernel to be injected into components
-///                 for unit testing.
-///
-/// To serve these use cases, `Syscalls` has two API layers (called the
-/// high-level API and the low-level API). Components needing access to Tock's
-/// system calls should use the high-level API, which is safe and has nice
-/// abstractions. The low-level API is implemented by
-/// `libtock_runtime::TockSyscalls` and `libtock_unittest::FakeSyscalls`, and
-/// provides the raw userspace<->kernel interface.
+/// `Syscalls` provides safe abstractions over Tock's system calls. It is
+/// implemented for `libtock_runtime::TockSyscalls` and
+/// `libtock_unittest::FakeSyscalls` (by way of `RawSyscalls`).
 pub trait Syscalls {
-    // -------------------------------------------------------------------------
-    // High-level API
-    // -------------------------------------------------------------------------
-
     /// Puts the process to sleep until a callback becomes pending, invokes the
     /// callback, then returns.
-    fn yield_wait() {
-        Self::raw_yield(YieldType::Wait);
-    }
+    fn yield_wait();
 
     /// Runs the next pending callback, if a callback is pending. Unlike
     /// `yield_wait`, `yield_no_wait` returns immediately if no callback is
     /// pending. Returns true if a callback was executed, false otherwise.
+    fn yield_no_wait() -> bool;
+
+    // TODO: Add a subscribe interface.
+
+    // TODO: Add a command interface.
+
+    // TODO: Add a read-write allow interface.
+
+    // TODO: Add a read-only allow interface.
+
+    // TODO: Add memop() methods.
+}
+
+impl<S: RawSyscalls> Syscalls for S {
+    fn yield_wait() {
+        Self::raw_yield(YieldType::Wait);
+    }
+
     fn yield_no_wait() -> bool {
         Self::raw_yield(YieldType::NoWait) != ReturnType::Failure as usize
     }
+}
 
-    // TODO: Implement a subscribe interface.
-
-    // TODO: Implement a command interface.
-
-    // TODO: Implement a read-write allow interface.
-
-    // TODO: Implement a read-only allow interface.
-
-    // TODO: Implement memop() methods.
-
-    // -------------------------------------------------------------------------
-    // Low-level API
-    // -------------------------------------------------------------------------
-
-    // This API is designed to minimize the amount of handwritten assembly code
-    // needed without generating unnecessary instructions. There are a few major
-    // factors affecting its design:
-    //     1. Most system calls only clobber r0-r4, while yield has a far longer
-    //        clobber list. As such, yield must have its own assembly
-    //        implementation.
-    //     2. The compiler is unable to optimize away unused arguments. For
-    //        example, memop's "get process RAM start address" operation only
-    //        needs r0 set, while memop's "break" operation needs both r0 and r1
-    //        set. If our inline assembly calls "get process RAM start address"
-    //        but sets both r0 and r1, the compiler doesn't know that r1 will be
-    //        ignored so setting that register will not be optimized away.
-    //        Therefore we want to set the minimum number of argument registers
-    //        possible.
-    //     3. The cost of specifying unused return registers is only that of
-    //        unnecessarily marking a register as clobbered. Explanation: After
-    //        inlining, an unused register is marked as "changed by the
-    //        assembly" but can immediately be re-used by the compiler, which is
-    //        the same as a clobbered register. System calls should generally be
-    //        inlined -- and even if they aren't, the unused return values will
-    //        probably be passed in caller-saved registers (this is true for the
-    //        C ABI, so probably true for the Rust ABI), which are treated as
-    //        clobbered regardless.
-    //
-    // Currently, yield takes exactly one argument, to specify what yield type
-    // to do. Therefore we only need one raw yield call.
-    //
-    // Subscribe, command, read-write allow, and read-only allow all take four
-    // argument types. Even when calling command IDs that have unused arguments,
-    // we still need to clear the argument registers so as to avoid passing
-    // confidential data to capsules (this is in line with Tock's threat model).
-    // As such, four_arg_syscall() is used for all subscribe, command, read-only
-    // allow, and read-write allow system calls.
-    //
-    // Memop takes 1 or 2 arguments (operation and an optional argument), and
-    // being part of the core kernel it is okay for us to leave arbitrary data
-    // in the argument register if the argument is unused (again, in line with
-    // Tock's threat model). Memop returns up to 2 return arguments, so we don't
-    // need to mark r2 and r3 as clobbered. As such, we need two raw memop
-    // calls: one for operations without an argument and one for operations with
-    // an argument.
-    //
-    // Because the variables passed in and out of raw system calls represent
-    // register values, they are of type usize. In cases where it doesn't make
-    // sense to pass a pointer-sized value, libtock_unittest::FakeSyscalls is
-    // free to panic if a too-large value is passed.
-
-    // raw_yield should:
-    //     1. Call syscall class 0
-    //     2. Use register r0 for input and output as an inlateout register,
-    //        passing in r0_in and returning its value.
-    //     3. Mark all caller-saved registers as lateout clobbers.
-    //     4. NOT provide any of the following options:
-    //            pure             (yield has side effects)
-    //            nomem            (a callback can read + write globals)
-    //            readonly         (a callback can write globals)
-    //            preserves_flags  (a callback can change flags)
-    //            noreturn         (yield is expected to return)
-    //            nostack          (a callback needs the stack)
-    //
-    // Design note: This is safe because the yield types that currently exist
-    // are safe. If an unsafe yield type is added, we will need to make
-    // raw_yield unsafe. Although raw_yield shouldn't be called by code outside
-    // this crate, it can be, so that is a backwards-incompatible change. We
-    // pass YieldType rather than a usize because if we used usize directly then
-    // this API becomes unsound if the kernel adds support for an unsafe yield
-    // type (or even one that takes one more argument).
-    fn raw_yield(r0_in: YieldType) -> usize;
-
-    // four_arg_syscall is used to invoke subscribe, command, read-write allow,
-    // and read-only allow system calls.
-    //
-    // four_arg_syscall's inline assembly should have the following properties:
-    //     1. Calls the syscall class specified by class
-    //     2. Passes r0-r3 in the corresponding registers as inlateout
-    //        registers. Returns r0-r3 in order.
-    //     3. Does not mark any registers as clobbered.
-    //     4. Has all of the following options:
-    //            preserves_flags  (these system calls do not touch flags)
-    //            nostack          (these system calls do not touch the stack)
-    //     5. Does NOT have any of the following options:
-    //            pure      (these system calls have side effects)
-    //            nomem     (the compiler needs to write to globals before allow)
-    //            readonly  (rw allow can modify memory)
-    //            noreturn  (all these system calls are expected to return)
-    //
-    /// # Safety
-    /// A four_arg_syscall must NOT be used to invoke yield. Otherwise, it is
-    /// exactly as safe as the underlying system call, which varies depending on
-    /// the system call class.
-    unsafe fn four_arg_syscall(
-        r0: usize,
-        r1: usize,
-        r2: usize,
-        r3: usize,
-        class: u8,
-    ) -> (usize, usize, usize, usize);
-
-    // zero_arg_memop is used to invoke memop operations that do not accept an
-    // argument register. Because the are no memop commands that set r2 or r3,
-    // this only needs to return r0 and r1.
-    //
-    // Many memop commands are not expected to work in the unit test
-    // environment. If called, those commands may panic.
-    //
-    // zero_arg_memop's inline assembly should have the following properties:
-    //     1. Calls syscall class 5
-    //     2. Specifies r0 as an inlateout register, and r1 as a lateout
-    //        register.
-    //     3. Does not mark any registers as clobbered.
-    //     4. Has all of the following options:
-    //            preserves_flags
-    //            nostack
-    //            nomem            (it is okay for the compiler to cache globals
-    //                              across memop calls)
-    //     5. Does NOT have any of the following options:
-    //            pure      (two invocations of the same memop can return
-    //                       different values)
-    //            readonly  (incompatible with nomem)
-    //            noreturn
-    //
-    // Design note: like raw_yield, this is safe because memops that currently
-    // exist are safe. zero_arg_memop takes a ZeroArgMemop rather than a usize
-    // so that if the kernel adds an unsafe memop this API doesn't become
-    // unsound.
-    fn zero_arg_memop(r0_in: ZeroArgMemop) -> (usize, usize);
-
-    // one_arg_memop is used to invoke memop operations that take an argument.
-    // Because there are no memop operations that set r2 or r3, this only needs
-    // to return r0 and r1.
-    //
-    // one_arg_memop's inline assembly should:
-    //     1. Call syscall class 5
-    //     2. Specify r0 and r1 as inlateout registers, and return (r0, r1)
-    //     3. Not mark any registers as clobbered.
-    //     4. Have all of the following options:
-    //            preserves_flags
-    //            nostack
-    //            nomem            (the compiler can cache globals across memop
-    //                              calls)
-    //     5. Does NOT have any of the following options:
-    //            pure      Two invocations of sbrk can return different values
-    //            readonly  Incompatible with nomem
-    //            noreturn
-    //
-    // Design note: like raw_yield, this is safe because memops that currently
-    // exist are safe. zero_arg_memop takes a ZeroArgMemop rather than a usize
-    // so that if the kernel adds an unsafe memop this API doesn't become
-    // unsound.
-    fn one_arg_memop(r0_in: OneArgMemop, r1: usize) -> (usize, usize);
+// Note: variants are commented out because if they aren't commented out I get a
+// "variant is never constructed" error. When we figure out an error handling
+// design, this type is likely to move into an error handling-related module, at
+// which point we will uncomment the other variants.
+enum ReturnType {
+    Failure = 0,
+    //FailureWithU32 = 1,
+    //FailureWith2U32 = 2,
+    //FailureWithU64 = 3,
+    //Success = 128,
+    //SuccessWithU32 = 129,
+    //SuccessWith2U32 = 130,
+    //SuccessWithU64 = 131,
+    //SuccessWith3U32 = 132,
+    //SuccessWithU32AndU64 = 133,
 }