use core::{arch::global_asm, ptr};
use aarch32_cpu::asm::{dsb, isb};
use crate::cpu::cache::{self, CacheTarget};
pub mod competition;
global_asm!(include_str!("./sdk_trampoline.s"), options(raw));
unsafe extern "C" {
fn v5gdb_sdk_trampoline_arm();
static v5gdb_sdk_trampoline_arm_end: u32;
fn v5gdb_sdk_trampoline_thumb();
static v5gdb_sdk_trampoline_thumb_end: u32;
}
pub unsafe fn redirect_function(target: *mut (), destination: *const ()) {
const THUMB_BIT: usize = 0b1;
let is_thumb = (target as usize) & THUMB_BIT != 0;
let (trampoline_fn, trampoline_end) = if is_thumb {
(
v5gdb_sdk_trampoline_thumb as unsafe extern "C" fn(),
&raw const v5gdb_sdk_trampoline_thumb_end,
)
} else {
(
v5gdb_sdk_trampoline_arm as unsafe extern "C" fn(),
&raw const v5gdb_sdk_trampoline_arm_end,
)
};
let trampoline_src = ((trampoline_fn as usize) & !THUMB_BIT) as *const u16;
let write_addr = ((target as usize) & !THUMB_BIT) as *mut u16;
let code_len_bytes = (trampoline_end as usize) - (trampoline_src as usize);
let code_len = code_len_bytes / size_of::<u16>();
let destination_slot = unsafe { write_addr.add(code_len) };
unsafe {
ptr::copy_nonoverlapping(trampoline_src, write_addr, code_len);
ptr::write_unaligned(destination_slot.cast::<u32>(), destination as u32);
}
dsb();
isb();
cache::sync_instruction(CacheTarget::Address(write_addr as u32));
cache::sync_instruction(CacheTarget::Address(destination_slot as u32));
}
macro_rules! jumptable {
($offset:literal, $ty:ty) => {{
const JUMPTABLE_BASE: u32 = 0x037fc000;
let ptr = (JUMPTABLE_BASE + $offset) as *const $ty;
*ptr
}};
}
pub(crate) use jumptable;
pub fn stop_all_motors() {
use vex_sdk::{V5_MAX_DEVICE_PORTS, vexDeviceGetByIndex, vexDeviceMotorVoltageSet};
for port_num in 0..V5_MAX_DEVICE_PORTS {
unsafe {
let device = vexDeviceGetByIndex(port_num as u32);
if device.is_null() {
continue;
}
vexDeviceMotorVoltageSet(device, 0);
}
}
}
pub mod serial {
use core::cmp;
use derive_more::From;
pub const OUT_BUF_SIZE: usize = 2048;
#[derive(Debug, Clone, Copy, PartialEq, Eq, From)]
#[repr(transparent)]
pub struct Channel(pub u32);
impl Channel {
pub const USER: Self = Self(1);
}
pub fn write_byte(channel: Channel, byte: u8) -> bool {
let sys_write_char = unsafe { jumptable!(0x898, extern "C" fn(u32, u8) -> i32) };
sys_write_char(channel.0, byte) != 0
}
pub unsafe fn write_buf(channel: Channel, buf: *const u8, len: usize) -> Result<usize, ()> {
let sys_write_buf =
unsafe { jumptable!(0x89c, unsafe extern "C" fn(u32, *const u8, u32) -> i32) };
let written = unsafe { sys_write_buf(channel.0, buf, len as u32) };
if written == -1 {
Err(())
} else {
Ok(written as usize)
}
}
pub fn write(channel: Channel, slice: &[u8]) -> Result<usize, ()> {
unsafe { write_buf(channel, slice.as_ptr(), slice.len()) }
}
pub fn write_all(channel: Channel, mut slice: &[u8]) -> Result<(), ()> {
loop {
let written = write(channel, slice)?;
slice = &slice[cmp::min(written, slice.len())..];
if slice.is_empty() {
break;
}
unsafe {
vex_sdk::vexTasksRun();
}
}
Ok(())
}
pub fn read_byte(channel: Channel) -> Option<u8> {
let sys_read = unsafe { jumptable!(0x8a0, extern "C" fn(u32) -> i32) };
match sys_read(channel.0) {
-1 => None,
byte => Some(byte as u8),
}
}
pub fn peek_byte(channel: Channel) -> Option<u8> {
let sys_peek = unsafe { jumptable!(0x8a4, extern "C" fn(u32) -> i32) };
match sys_peek(channel.0) {
-1 => None,
byte => Some(byte as u8),
}
}
pub fn write_buf_capacity(channel: Channel) -> Option<usize> {
let sys_write_free = unsafe { jumptable!(0x8ac, extern "C" fn(u32) -> i32) };
match sys_write_free(channel.0) {
-1 => None,
len => Some(len as usize),
}
}
}