uprintln!
For the next exercise, we'll implement the uprint!
family of macros. Your goal is to make this
line of code work:
Which must send the string "The answer is 42"
through the serial interface.
How do we go about that? It's informative to look into the std
implementation of println!
.
Looks simple so far. We need the built-in format_args!
macro (it's implemented in the compiler so we
can't see what it actually does). We'll have to use that macro in the exact same way. What does this
_print
function do?
That looks complicated but the only part we are interested in is: w.write_fmt(args)
and
stdout().write_fmt(args)
. What print!
ultimately does is call the fmt::Write::write_fmt
method
with the output of format_args!
as its argument.
Luckily we don't have to implement the fmt::Write::write_fmt
method either because it's a default
method. We only have to implement the fmt::Write::write_str
method.
Let's do that.
This is what the macro side of the equation looks like. What's left to be done by you is provide the
implementation of the write_str
method.
Above we saw that Write
is in std::fmt
. We don't have access to std
but Write
is also
available in core::fmt
.
#![deny(unsafe_code)]
#![no_main]
#![no_std]
use core::fmt::{self, Write};
#[allow(unused_imports)]
use aux11::{entry, iprint, iprintln, usart1};
macro_rules! uprint {
($serial:expr, $($arg:tt)*) => {
$serial.write_fmt(format_args!($($arg)*)).ok()
};
}
macro_rules! uprintln {
($serial:expr, $fmt:expr) => {
uprint!($serial, concat!($fmt, "\n"))
};
($serial:expr, $fmt:expr, $($arg:tt)*) => {
uprint!($serial, concat!($fmt, "\n"), $($arg)*)
};
}
struct SerialPort {
usart1: &'static mut usart1::RegisterBlock,
}
impl fmt::Write for SerialPort {
fn write_str(&mut self, s: &str) -> fmt::Result {
// TODO implement this
// hint: this will look very similar to the previous program
Ok(())
}
}
#[entry]
fn main() -> ! {
let (usart1, _mono_timer, _itm) = aux11::init();
let mut serial = SerialPort { usart1 };
uprintln!(serial, "The answer is {}", 40 + 2);
loop {}
}