//! bare4.rs
//! Simple bare metal application
//!
#![feature(used)]
#![feature(custom_attribute)] // needed for #[rustfmt_skip]
#![no_std]

extern crate cortex_m;
extern crate cortex_m_rt;

// Peripheral addresses as constants
#[rustfmt_skip]
mod address {
    pub const PERIPH_BASE: u32      = 0x40000000;
    pub const AHB1PERIPH_BASE: u32  = PERIPH_BASE + 0x00020000;
    pub const RCC_BASE: u32         = AHB1PERIPH_BASE + 0x3800;
    pub const RCC_AHB1ENR: u32      = RCC_BASE + 0x30;
    pub const GBPIA_BASE: u32       = AHB1PERIPH_BASE + 0x0000;
    pub const GPIOA_MODER: u32      = GBPIA_BASE + 0x00;
    pub const GPIOA_BSRR: u32       = GBPIA_BASE + 0x18;
}
use address::*;

// see the Reference Manual RM0368 (www.st.com/resource/en/reference_manual/dm00096844.pdf)
// rcc,     chapter 6
// gpio,    chapter 8

#[inline(always)]
fn read_u32(addr: u32) -> u32 {
    //unsafe { core::ptr::read_volatile(addr as *const _) }
    core::ptr::read_volatile(addr as *const _)
}

#[inline(always)]
fn write_u32(addr: u32, val: u32) {
    unsafe {
        core::ptr::write_volatile(addr as *mut _, val);
    }
}

fn wait(i: u32) {
    for _ in 0..i {
        cortex_m::asm::nop(); // no operation (cannot be optimized out)
    }
}

fn main() {
    // power on GPIOA
    let r = read_u32(RCC_AHB1ENR); // read
    write_u32(RCC_AHB1ENR, r | 1); // set enable

    // configure PA5 as output
    let r = read_u32(GPIOA_MODER) & !(0b11 << (5 * 2)); // read and mask
    write_u32(GPIOA_MODER, r | 0b01 << (5 * 2)); // set output mode

    // and alter the data output through the BSRR register
    // this is more efficient as the read register is not needed.

    loop {
        // set PA5 high
        write_u32(GPIOA_BSRR, 1 << 5); // set bit, output hight (turn on led)
        wait(10_000);

        // set PA5 low
        write_u32(GPIOA_BSRR, 1 << (5 + 16)); // clear bit, output low (turn off led)
        wait(10_000);
    }
}

// 1. build and run the application (debug build)
// did you enjoy the blinking?
// ** your answer here **
//  No, it is enoing.
//
// now lookup the data-sheets, and read each section referred,
// 6.3.11, 8.4.1, 8.4.7
//
// document each low level access *code* by the appropriate section in the
// data sheet
//
// commit your answers (bare4_1)
//
// 2. comment out line 30 and uncomment line 31 (essentially omitting the `unsafe`)
// what was the error message and explain why,
// ** your answer here **
//  call to unsafe function recvier unsafe function or blok
//
// digging a bit deeper, why do you think `read_volatile` is declared `unsafe`
// (https://doc.rust-lang.org/core/ptr/fn.read_volatile.html, for some food for thought )
// ** your answer here **
// it is reading a operation from a perifial. perifials ar critical resorses and writ operations
// kan cas race condition in combination with read.
//
// commit your answers (bare4_2)
//
// 3.
// volatile read/writes are explicit *volatile operations* in Rust, while in C they
// are declared at type level (i.e., access to varibles declared volatile amounts to
// volatile reads/and writes)
//
// both C and Rust (even more) allows code optimization to re-order operations, as long
// as data dependencies are preserved.
//
// why is important that ordering of volatile operations are ensured by the compiler?
// ** your answer here **
//  The order of the operations can efekt the out come of the program, race condition.
//
// give an example in the above code, where reordering might make things go horribly wrong
// (hint, accessing a peripheral not being powered...)
// ** your answer here **
//  in som cases wen seting an walue or aktivating a perifial is nessesary to do befor a read or
//  simelar.
//  seting gpioA ass activ befor reading. the compiler do not know if a perifial writ is to set a
//  setin or blink a led.
//
// without the non-reording proprety of `write_volatile/read_volatile` could that happen in theory
// (argue from the point of data dependencies)
// ** your answer here **
// If the compiler is left to it self wil it in som case changes the order of the read/write
// operation. if somthig can gow wrong it will. sow it is lickly that the program is broken if the
// is no forsd ordering
//
// commit your answers (bare4_3)

// As we are not using interrupts, we just register a dummy catch all handler
#[link_section = ".vector_table.interrupts"]
#[used]
static INTERRUPTS: [extern "C" fn(); 240] = [default_handler; 240];

extern "C" fn default_handler() {
    cortex_m::asm::bkpt();
}