1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
//! This crate provides a working entrypoint for the VEX V5 Brain.
//!
//! # Usage
//!
//! Your entrypoint function should be an async function that takes a single argument of type [`Peripherals`](vexide_devices::peripherals::Peripherals).
//! It can return any type implementing [`Termination`](vexide_core::program::Termination).
//! ```rust
//! #[vexide::main]
//! async fn main(peripherals: Peripherals) { ... }
//! ```

#![no_std]
#![feature(asm_experimental_arch)]
#![allow(clippy::needless_doctest_main)]

use vexide_core::print;

extern "C" {
    // These symbols don't have real types so this is a little bit of a hack
    static mut __bss_start: u32;
    static mut __bss_end: u32;
}

#[repr(C, packed)]
/// The cold header is a structure that is placed at the beginning of cold memory and tells VexOS details abuot the program.
pub struct ColdHeader {
    /// The magic number for the cold header. This should always be "XVX5".
    pub magic: [u8; 4],
    /// The program type. PROS sets this to 0.
    pub program_type: u32,
    /// The owner of the program. PROS sets this to 2.
    pub owner: u32,
    /// Padding before the options.
    pub padding: [u32; 2],
    /// A bitfield of program options that change the behavior of some jumptable functions.
    pub options: u32,
}
impl ColdHeader {
    /// Creates a new cold header with the correct magic number and options.
    pub const fn new(program_type: u32, owner: u32, options: u32) -> Self {
        Self {
            magic: *b"XVX5",
            program_type,
            owner,
            padding: [0; 2],
            options,
        }
    }
}

extern "Rust" {
    fn main();
}

/// Sets up the user stack, zeroes the BSS section, and calls the user code.
/// This function is designed to be used as an entrypoint for programs on the VEX V5 Brain.
///
/// # Safety
///
/// This function MUST only be called once and should only be called at the very start of program initialization.
/// Calling this function more than one time will seriously mess up both your stack and your heap.
pub unsafe fn program_entry() {
    #[cfg(target_arch = "arm")]
    unsafe {
        use core::arch::asm;
        asm!(
            "
            // Load the user stack
            ldr sp, =__stack_start
            "
        );
    }

    // Clear the BSS section
    #[cfg(target_arch = "arm")]
    unsafe {
        use core::ptr::addr_of_mut;
        let mut bss_start = addr_of_mut!(__bss_start);
        while bss_start < addr_of_mut!(__bss_end) {
            core::ptr::write_volatile(bss_start, 0);
            bss_start = bss_start.offset(1);
        }
    }
    // vexPrivateApiDisable
    // (unsafe { *(0x37fc020 as *const extern "C" fn(u32)) })(COLD_HEADER.options);

    unsafe {
        // Initialize the heap allocator
        // This cfg is mostly just to make the language server happy. All of this code is near impossible to run in the WASM sim.
        #[cfg(target_arch = "arm")]
        vexide_core::allocator::vexos::init_heap();
        // Print the banner
        #[cfg(not(feature = "no-banner"))]
        print!(
            "
\x1B[1;38;5;196m=%%%%%#-  \x1B[38;5;254m-#%%%%-\x1B[1;38;5;196m  :*%%%%%+.
\x1B[38;5;208m  -#%%%%#-  \x1B[38;5;254m:%-\x1B[1;38;5;208m  -*%%%%#
\x1B[38;5;226m    *%%%%#=   -#%%%%%+
\x1B[38;5;226m      *%%%%%+#%%%%%%%#=
\x1B[38;5;34m        *%%%%%%%*-+%%%%%+
\x1B[38;5;27m          +%%%*:   .+###%#
\x1B[38;5;93m           .%:\x1B[0m
vexide startup successful!
Running user code...
"
        );
        // Run vexos background processing at a regular 2ms interval.
        // This is necessary for serial and devices to work properly.
        vexide_async::task::spawn(async {
            loop {
                vex_sdk::vexTasksRun();
                vexide_async::time::sleep(::core::time::Duration::from_millis(2)).await;
            }
        })
        .detach();
        // Call the user code
        main();
        // Exit the program
        vexide_core::program::exit();
    }
}