//! bare1.rs
//!
//! Inspecting the generated assembly
//!
//! What it covers
//! - tracing over semihosting and ITM
//! - assembly calls and inline assembly
//! - more on arithmetics

#![no_main]
#![no_std]

extern crate panic_halt;

use cortex_m_rt::entry;
use cortex_m::{iprintln, Peripherals};
use cortex_m_semihosting::hprintln;

use core::num::Wrapping;

#[entry]
#[inline(never)]
fn main() -> ! {
    // Prepend by `x` by _ to avoid warning (never used).
    // The compiler is smart enough to figure out that
    // `x` is not used in any menaningful way.
    //let mut p = Peripherals::take().unwrap();
    //let stim = &mut p.ITM.stim[0];

    let mut _x = Wrapping(0u32);
    let one = Wrapping(1u32);
    loop {
        _x += one;
        cortex_m::asm::nop();
        //iprintln!(stim, "{}",_x);
        //hprintln!("{}", _x).unwrap();
        cortex_m::asm::bkpt();
        _x -= one;
    }
}

// 0. Setup
//    For this example we will use the `nightly` compiler
//    to get inline assembly.
//    (Inline assembly is currently not stabelized.)
//
//    > rustup override set nightly
//
//    In the `Corgo.toml` file, uncomment
//    # features = ["inline-asm"] # <- currently requires nightly compiler
//
//    You may need/want to install addititonal components also,
//    to that end look at the install section in the README.md.
//    If you change toolchain, exit and re-start `vscode`.
//
// 1. Build and run the application
//
//    > cargo build --example bare1
//    (or use the vscode build task)
//
//    Look at the `hello.rs` and `itm.rs` examples to setup the tracing.
//
//    When debugging the application it should get stuck in the
//    loop, (press pause/suspend to verify this).
//    what is the output in the ITM console
//
//    ** 1 **
//
//    What is the output in the semihosting (openocd) console
//    ** 1 **
//
//    Commit your answers (bare1_1)
//
// 2. Inspecting the generated assembly code
//    If in `vcsode` the gdb console in DEBUG CONSOLE
//
//    What is the output of:
//    (gdb) disassemble
//
//    ** Dump of assembler code for function main:
//    0x08000400 <+0>:	sub	sp, #16
//    0x08000402 <+2>:	movs	r0, #0
//    0x08000404 <+4>:	str	r0, [sp, #12]
//    0x08000406 <+6>:	b.n	0x8000408 <main+8>
// => 0x08000408 <+8>:	ldr	r0, [sp, #12]
//    0x0800040a <+10>:	adds	r1, r0, #1
//    0x0800040c <+12>:	mov	r2, r1
//    0x0800040e <+14>:	cmp	r1, r0
//    0x08000410 <+16>:	str	r2, [sp, #8]
//    0x08000412 <+18>:	bvs.n	0x800042c <main+44>
//    0x08000414 <+20>:	b.n	0x8000416 <main+22>
//    0x08000416 <+22>:	ldr	r0, [sp, #8]
//    0x08000418 <+24>:	str	r0, [sp, #12]
//    0x0800041a <+26>:	ldr	r1, [sp, #12]
//    0x0800041c <+28>:	subs	r2, r1, #1
//    0x0800041e <+30>:	cmp	r1, #1
//    0x08000420 <+32>:	str	r2, [sp, #4]
//    0x08000422 <+34>:	bvs.n	0x800043a <main+58>
//    0x08000424 <+36>:	b.n	0x8000426 <main+38>
//    0x08000426 <+38>:	ldr	r0, [sp, #4]
//    0x08000428 <+40>:	str	r0, [sp, #12]
//    0x0800042a <+42>:	b.n	0x8000408 <main+8>
//    0x0800042c <+44>:	movw	r0, #1964	; 0x7ac
//    0x08000430 <+48>:	movt	r0, #2048	; 0x800
//    0x08000434 <+52>:	bl	0x800045c <core::panicking::panic::h4c45e71f0f614f08>
//    0x08000438 <+56>:	udf	#254	; 0xfe
//    0x0800043a <+58>:	movw	r0, #2036	; 0x7f4
//    0x0800043e <+62>:	movt	r0, #2048	; 0x800
//    0x08000442 <+66>:	bl	0x800045c <core::panicking::panic::h4c45e71f0f614f08>
//    0x08000446 <+70>:	udf	#254	; 0xfe
// End of assembler dump.
//    **
//
//    Commit your answers (bare1_2)
//
// 3. Now remove the comment for `cortex_m::asm::nop()`.
//    Rebuild and debug, pause the program.
//
//    What is the output of:
//    (gdb) disassemble
//
//    ** 
// Dump of assembler code for function main:
//    0x08000404 <+0>:	sub	sp, #16
//    0x08000406 <+2>:	movs	r0, #0
//    0x08000408 <+4>:	str	r0, [sp, #12]
//    0x0800040a <+6>:	b.n	0x800040c <main+8>
//    0x0800040c <+8>:	ldr	r0, [sp, #12]
// => 0x0800040e <+10>:	adds	r1, r0, #1
//    0x08000410 <+12>:	mov	r2, r1
//    0x08000412 <+14>:	cmp	r1, r0
//    0x08000414 <+16>:	str	r2, [sp, #8]
//    0x08000416 <+18>:	bvs.n	0x8000436 <main+50>
//    0x08000418 <+20>:	b.n	0x800041a <main+22>
//    0x0800041a <+22>:	ldr	r0, [sp, #8]
//    0x0800041c <+24>:	str	r0, [sp, #12]
//    0x0800041e <+26>:	bl	0x8000400 <cortex_m::asm::nop::h0ac7b7a3f33f2667>
//    0x08000422 <+30>:	b.n	0x8000424 <main+32>
//    0x08000424 <+32>:	ldr	r0, [sp, #12]
//    0x08000426 <+34>:	subs	r1, r0, #1
//    0x08000428 <+36>:	cmp	r0, #1
//    0x0800042a <+38>:	str	r1, [sp, #4]
//    0x0800042c <+40>:	bvs.n	0x8000444 <main+64>
//    0x0800042e <+42>:	b.n	0x8000430 <main+44>
//    0x08000430 <+44>:	ldr	r0, [sp, #4]
//    0x08000432 <+46>:	str	r0, [sp, #12]
//    0x08000434 <+48>:	b.n	0x800040c <main+8>
//    0x08000436 <+50>:	movw	r0, #1980	; 0x7bc
//    0x0800043a <+54>:	movt	r0, #2048	; 0x800
//    0x0800043e <+58>:	bl	0x8000466 <core::panicking::panic::h4c45e71f0f614f08>
//    0x08000442 <+62>:	udf	#254	; 0xfe
//    0x08000444 <+64>:	movw	r0, #2052	; 0x804
//    0x08000448 <+68>:	movt	r0, #2048	; 0x800
//    0x0800044c <+72>:	bl	0x8000466 <core::panicking::panic::h4c45e71f0f614f08>
//    0x08000450 <+76>:	udf	#254	; 0xfe
// End of assembler dump.
//  **
//
//    Commit your answers (bare1_3)
//
// 4. Now remove the comment for `cortex_m::asm::bkpt()`
//    Rebuild and debug, let the program run until it halts.
//
//    What is the output of:
//    (gdb) disassemble
//
//    ** 
// Dump of assembler code for function main:
//    0x08000404 <+0>:	sub	sp, #16
//    0x08000406 <+2>:	movs	r0, #0
//    0x08000408 <+4>:	str	r0, [sp, #12]
//    0x0800040a <+6>:	b.n	0x800040c <main+8>
//    0x0800040c <+8>:	ldr	r0, [sp, #12]
//    0x0800040e <+10>:	adds	r1, r0, #1
//    0x08000410 <+12>:	mov	r2, r1
//    0x08000412 <+14>:	cmp	r1, r0
//    0x08000414 <+16>:	str	r2, [sp, #8]
//    0x08000416 <+18>:	bvs.n	0x800043a <main+54>
//    0x08000418 <+20>:	b.n	0x800041a <main+22>
//    0x0800041a <+22>:	ldr	r0, [sp, #8]
//    0x0800041c <+24>:	str	r0, [sp, #12]
//    0x0800041e <+26>:	bl	0x8000400 <cortex_m::asm::nop::h0ac7b7a3f33f2667>
//    0x08000422 <+30>:	b.n	0x8000424 <main+32>
// => 0x08000424 <+32>:	bkpt	0x0000
//    0x08000426 <+34>:	b.n	0x8000428 <main+36>
//    0x08000428 <+36>:	ldr	r0, [sp, #12]
//    0x0800042a <+38>:	subs	r1, r0, #1
//    0x0800042c <+40>:	cmp	r0, #1
//    0x0800042e <+42>:	str	r1, [sp, #4]
//    0x08000430 <+44>:	bvs.n	0x8000448 <main+68>
//    0x08000432 <+46>:	b.n	0x8000434 <main+48>
//    0x08000434 <+48>:	ldr	r0, [sp, #4]
//    0x08000436 <+50>:	str	r0, [sp, #12]
//    0x08000438 <+52>:	b.n	0x800040c <main+8>
//    0x0800043a <+54>:	movw	r0, #1980	; 0x7bc
//    0x0800043e <+58>:	movt	r0, #2048	; 0x800
//    0x08000442 <+62>:	bl	0x800046a <core::panicking::panic::h4c45e71f0f614f08>
//    0x08000446 <+66>:	udf	#254	; 0xfe
//    0x08000448 <+68>:	movw	r0, #2052	; 0x804
//    0x0800044c <+72>:	movt	r0, #2048	; 0x800
//    0x08000450 <+76>:	bl	0x800046a <core::panicking::panic::h4c45e71f0f614f08>
//    0x08000454 <+80>:	udf	#254	; 0xfe
// End of assembler dump.
// **
//
//    Commit your answers (bare1_4)
//
// 5. Release mode (optimized builds).
//    Rebuild `bare1.rs` in release (optimized mode).
//  
//    > cargo build --example bare1 --release
//    (or using the vscode build task)
//
//    Compare the generated assembly for the loop
//    between the dev (unoptimized) and release (optimized) build.
//
//    ** 
// Dump of assembler code for function main:
//    0x08000400 <+0>:	nop
// => 0x08000402 <+2>:	bkpt	0x0000
//    0x08000404 <+4>:	b.n	0x8000400 <main>
// End of assembler dump.
//  **
//
//    commit your answers (bare1_5)
//
//    Tips: The optimized build should have 3 instructions
//    while the debug (dev) build should have > 20 instructions
//    (both counting the inner loop only). The debug build
//    should have additional code that call panic if the additon
//    wraps (and in such case call panic).
//
//    Discussion:
//    In release (optimized) mode the addition is unchecked,
//    so there is a semantic difference here in between
//    the dev and release modes. This is motivited by:
//    1) efficiency, unchecked is faster
//    2) convenience, it would be inconvenient to explicitly use
//    wrapping arithmetics, and wrapping is what the programmer
//    typically would expect in any case. So the check
//    in dev/debug mode is just there for some extra safety
//    if your intention is NON-wrapping arithmetics.
//
// 6. *Optional
//    You can pass additional flags to the Rust `rustc` compiler.
//
//    `-Z force-overflow-checks=off`
//
//    Under this flag, code is never generated for oveflow checking.
//    You can enable this flag (uncomment the corresponding flag in
//    the `.cargo/config` file.)
//
//    What is now the disassembly of the loop (in debug mode):
//
//    ** 
// Dump of assembler code for function main:
//    0x08000404 <+0>:	sub	sp, #8
//    0x08000406 <+2>:	movs	r0, #0
//    0x08000408 <+4>:	str	r0, [sp, #4]
//    0x0800040a <+6>:	b.n	0x800040c <main+8>
//    0x0800040c <+8>:	ldr	r0, [sp, #4]
//    0x0800040e <+10>:	adds	r0, #1
//    0x08000410 <+12>:	str	r0, [sp, #4]
//    0x08000412 <+14>:	bl	0x8000400 <cortex_m::asm::nop::h5c0367e982e73891>
//    0x08000416 <+18>:	b.n	0x8000418 <main+20>
// => 0x08000418 <+20>:	bkpt	0x0000
//    0x0800041a <+22>:	b.n	0x800041c <main+24>
//    0x0800041c <+24>:	ldr	r0, [sp, #4]
//    0x0800041e <+26>:	subs	r0, #1
//    0x08000420 <+28>:	str	r0, [sp, #4]
//    0x08000422 <+30>:	b.n	0x800040c <main+8>
// End of assembler dump.
//  **
//
//    commit your answers (bare1_6)
//
//    Now restore the `.cargo/config` to its original state.
//
// 7. *Optional
//    There is another way to conveniently use wrapping arithmetics
//    without passing flags to the compiler.
//
//    https://doc.rust-lang.org/std/num/struct.Wrapping.html
//
//    Rewrite the code using this approach.
//
//    What is now the disassembly of the code in dev mode?
//
//    ** 
// Dump of assembler code for function main:
//    0x080004e4 <+0>:	sub	sp, #8
//    0x080004e6 <+2>:	movs	r0, #0
//    0x080004e8 <+4>:	str	r0, [sp, #0]
//    0x080004ea <+6>:	movs	r0, #1
//    0x080004ec <+8>:	str	r0, [sp, #4]
//    0x080004ee <+10>:	b.n	0x80004f0 <main+12>
//    0x080004f0 <+12>:	ldr	r1, [sp, #4]
//    0x080004f2 <+14>:	mov	r0, sp
//    0x080004f4 <+16>:	bl	0x8000490 <core::num::wrapping::_$LT$impl$u20$core..ops..arith..AddAssign$u20$for$u20$core..num..Wrapping$LT$u32$GT$$GT$::add_assign::hbb56dbef6bcfa964>
//    0x080004f8 <+20>:	b.n	0x80004fa <main+22>
//    0x080004fa <+22>:	bl	0x80004e0 <cortex_m::asm::nop::h0ac7b7a3f33f2667>
//    0x080004fe <+26>:	b.n	0x8000500 <main+28>
// => 0x08000500 <+28>:	bkpt	0x0000
//    0x08000502 <+30>:	b.n	0x8000504 <main+32>
//    0x08000504 <+32>:	ldr	r1, [sp, #4]
//    0x08000506 <+34>:	mov	r0, sp
//    0x08000508 <+36>:	bl	0x80004b8 <core::num::wrapping::_$LT$impl$u20$core..ops..arith..SubAssign$u20$for$u20$core..num..Wrapping$LT$u32$GT$$GT$::sub_assign::hcf06b8f89372a809>
//    0x0800050c <+40>:	b.n	0x800050e <main+42>
//    0x0800050e <+42>:	b.n	0x80004f0 <main+12>
// End of assembler dump.
//  **
//
//    What is now the disassembly of the code in release mode?
//
//    ** 
// Dump of assembler code for function main:
//    0x08000400 <+0>:	nop
// => 0x08000402 <+2>:	bkpt	0x0000
//    0x08000404 <+4>:	b.n	0x8000400 <main>
// End of assembler dump. 
//  **
//
//    commit your answers (bare1_7)
//
//    Final discussion:
//
//    Embedded code typically is performance sensitve, hence
//    it is important to understand how code is generated
//    to achieve efficient implementations.
//
//    Moreover, arithmetics are key to processing of data,
//    so its important that we are in control over the
//    computations. E.g. comupting checksums, hashes, cryptos etc.
//    all require precise control over wrapping vs. overflow behaviour.
//
//    If you write a library depending on wrapping arithmetics
//    do NOT rely on a compiler flag. (The end user might compile
//    it without this flag enabled, and thus get erronous results.)
//
//    NOTICE:
//    ------
//    You are now on a `nightly` release of the compiler for good and bad.
//    You can chose to switch back to the stable channel. If so you must
//    restore the `Cargo.toml` (comment out the `features = ["inline-asm"]`)
//
//    Pros and cons of nightly:
//    + Acccess to new Rust features (such as inline assembly)
//    - No guarantee these features will work, they might change semantics,
//      or even be revoked.
//
//    The compiler itself is the same, the stable release is just a snapchot
//    of the nightly (released each 6 week). It is the latest nightly
//    that passed some additional regression test, not a different compiler.
//    And of course, the stable has the experimental features disabled.
//
//    So its up to you to decide if you want to use the stable or nightly.