use crc::crc32::checksum_ieee;
use crate::system::{input::InputEvent, Display};
use super::{ServiceFn, InputFn, SetupFn, Context, Table};
pub struct ApplicationManager {
ram: Ram,
target_cs: [u8; 4],
target_cs_idx: usize,
service_fn: Option<ServiceFn>,
input_fn: Option<InputFn>,
status: Status,
os_table_ptr: &'static mut Table
}
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum Error {
Executing,
ChecksumFailed,
NoApplication,
InvalidServiceFn,
InvalidInputFn,
NoMemory
}
#[derive(Debug, Copy, Clone)]
pub struct Status {
pub is_loaded: bool,
pub is_running: bool,
pub ram_used: usize,
pub service_result: i32,
}
impl Default for Status {
fn default() -> Status {
Status {
is_loaded: false,
is_running: false,
service_result: -1,
ram_used: 0,
}
}
}
impl ApplicationManager {
pub fn new(ram: Ram, os_table_ptr: &'static mut Table) -> Self {
Self {
ram: ram,
target_cs: [0u8; 4],
target_cs_idx: 0,
service_fn: None,
input_fn: None,
status: Status::default(),
os_table_ptr,
}
}
pub fn write_ram_byte(&mut self, byte: u8) -> Result<(), Error> {
self.ram.write(byte)?;
Ok(())
}
pub fn write_checksum_byte(&mut self, byte: u8) -> Result<(), Error> {
if self.target_cs_idx > self.target_cs.len() {
Err(Error::NoMemory)
} else {
self.target_cs[self.target_cs_idx] = byte;
self.target_cs_idx += 1;
Ok(())
}
}
pub fn verify(&mut self) -> Result<(), Error> {
let ram_cs = self.ram.cs();
let digest = ApplicationManager::digest_from_bytes(&self.target_cs);
info!("Current Ram Digest: {}, stored ram Digest: {}", ram_cs, digest);
if digest == ram_cs {
self.status.is_loaded = true;
Ok(())
} else {
error!("Application checksum failed!");
Err(Error::ChecksumFailed)
}
}
fn digest_from_bytes(bytes: &[u8]) -> u32 {
assert_eq!(bytes.len(), 4);
let digest = ((u32::from(bytes[0])) << 24)
| ((u32::from(bytes[1])) << 16)
| ((u32::from(bytes[2])) << 8)
| (u32::from(bytes[3]));
digest
}
pub fn execute(&mut self) -> Result<(), Error> {
if !self.status.is_loaded {
return Err(Error::NoApplication);
}
let setup_ptr = Self::fn_ptr_from_slice(&self.ram.as_ref()[..4]);
let service_ptr = Self::fn_ptr_from_slice(&self.ram.as_ref()[4..8]);
let input_ptr = Self::fn_ptr_from_slice(&self.ram.as_ref()[8..12]);
let _result = unsafe {
let setup: SetupFn = ::core::mem::transmute(setup_ptr);
let service: ServiceFn = ::core::mem::transmute(service_ptr);
let input: InputFn = ::core::mem::transmute(input_ptr);
self.service_fn = Some(service);
self.input_fn = Some(input);
setup(self.os_table_ptr as *mut _)
};
self.status.is_running = true;
Ok(())
}
pub fn service(&mut self, display: &mut impl Display) -> Result<(), Error> {
if let Some(service_fn) = self.service_fn {
let fb = display.framebuffer();
let mut ctx = Context {
framebuffer: Some(fb)
};
self.status.service_result = unsafe { service_fn(&mut ctx) };
Ok(())
} else {
Err(Error::InvalidServiceFn)
}
}
pub fn service_input(&mut self, input: InputEvent) -> Result<(), Error> {
if let Some(input_fn) = self.input_fn {
let mut ctx = Context {
framebuffer: None,
};
let _ = unsafe { input_fn(&mut ctx, input) };
Ok(())
} else {
Err(Error::InvalidInputFn)
}
}
pub fn pause(&mut self) {
self.status.is_running = false;
}
pub fn kill(&mut self) -> Result<(), Error> {
self.ram.reset();
self.target_cs_idx = 0;
self.status.is_loaded = false;
self.status.is_running = false;
self.input_fn = None;
self.service_fn = None;
Ok(())
}
pub fn status(&self) -> Status {
self.status
}
fn fn_ptr_from_slice(bytes: &[u8]) -> *const () {
assert!(bytes.len() == 4);
let addr = ((u32::from(bytes[3])) << 24)
| ((u32::from(bytes[2])) << 16)
| ((u32::from(bytes[1])) << 8)
| (u32::from(bytes[0]));
addr as *const ()
}
pub fn program(&self) -> &[u8] {
self.ram.as_slice()
}
}
pub struct Ram {
ram: &'static mut [u8],
ram_idx: usize,
}
impl Ram {
pub fn new(ram: &'static mut [u8]) -> Self {
for byte in ram.iter_mut() {
*byte = 0u8;
}
Self {
ram,
ram_idx: 0,
}
}
pub fn write(&mut self, byte: u8) -> Result<(), Error> {
if self.ram_idx > self.ram.len() {
Err(Error::NoMemory)
} else {
self.ram[self.ram_idx] = byte;
self.ram_idx += 1;
Ok(())
}
}
pub fn cs(&self) -> u32 {
checksum_ieee(&self.ram[..self.ram_idx])
}
pub fn reset(&mut self) {
self.ram_idx = 0;
self.wipe();
}
fn wipe(&mut self) {
for i in 0..self.ram_idx {
self.ram[i] = 0u8;
}
}
pub fn as_ref(&self) -> &[u8] {
&self.ram
}
pub fn as_slice(&self) -> &[u8] {
&self.ram[..self.ram_idx]
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn checksum_parsing_works() {
assert_eq!(ApplicationManager::digest_from_bytes(&[35, 98, 167, 98]), 0x2362A762);
}
}