Skip to content

Commit

Permalink
Implement ELF loading for uploader
Browse files Browse the repository at this point in the history
This gets the uploader functional enough so that the boot ROM advances into unimplemented instructions.
  • Loading branch information
kleinesfilmroellchen committed Jan 5, 2024
1 parent 32baae6 commit 9ad13f7
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 8 deletions.
11 changes: 10 additions & 1 deletion sapemu/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#![deny(missing_docs, unused, clippy::all, clippy::pedantic, clippy::nursery, rustdoc::all)]
#![feature(slice_as_chunks)]

use std::fs;
use std::path::PathBuf;
use std::time::Instant;

Expand All @@ -10,6 +11,7 @@ use log::{info, warn, LevelFilter};
use time::macros::format_description;

use crate::memory::Memory;
use crate::smp::upload::Uploader;
use crate::smp::{Smp, CPU_RATE};

pub mod dsp;
Expand All @@ -19,7 +21,7 @@ pub mod smp;
#[derive(Clone, Debug, Parser)]
#[command(version = spcasm::buildinfo::PKG_VERSION, about, long_about = None)]
struct CliArguments {
/// Input ELF to execute.
/// Input ELF to execute. This is always uploaded via a simulated CPU uploader at the moment.
input: PathBuf,
/// Verbosity level to use.
#[arg(long, short, action = clap::ArgAction::Count)]
Expand Down Expand Up @@ -52,10 +54,17 @@ fn main() {
let mut memory = Box::new(Memory::new());
let mut smp = Smp::new(&mut memory);

let mut uploader =
Uploader::from_elf(&object::read::elf::ElfFile32::parse(&*fs::read(arguments.input).unwrap()).unwrap())
.unwrap();

let start_time = Instant::now();
// FIXME: Don't run the uploader all the time.
for _ in 0 .. arguments.cycles {
uploader.perform_step(&mut smp.ports);
smp.tick(&mut memory);
}

let end_time = Instant::now();
let frequency = arguments.cycles as f64 / (end_time - start_time).as_secs_f64();
info!(
Expand Down
61 changes: 54 additions & 7 deletions sapemu/src/smp/upload.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@
//!
//! This package implements the main CPU side of the SPC700 data upload routine as defined in the boot ROM.
use std::error::Error;

use log::{debug, info};
use object::read::elf::ElfFile32;
use object::{Object, ObjectSegment};

use super::CpuIOPorts;

/// A single data block to be uploaded.
Expand All @@ -12,7 +18,7 @@ pub struct DataBlock {
data: [u8; 256],
/// Amount of data in the block.
#[allow(unused)]
length: u8,
length: u16,
}

impl DataBlock {
Expand All @@ -23,13 +29,19 @@ impl DataBlock {
None
} else {
let mut data = [0; 256];
let length = elements.len() as u8;
let length = elements.len() as u16;
for (i, element) in elements.into_iter().enumerate() {
data[i] = element;
}
Some(Self { address, data, length })
}
}

/// Create a full 256-element data block.
#[must_use]
pub const fn new_full(address: u16, elements: [u8; 256]) -> Self {
Self { address, data: elements, length: 256 }
}
}

/// Uploader responsible for implementing the upload routine.
Expand All @@ -50,7 +62,7 @@ enum UploaderState {
/// Uploader is waiting for signal value 0xBB in port 1.
WaitingForBB,
/// Uploader is waiting for acknowledge of 0xCC in port 0.
WaitingForAcknowledge,
WaitingForCC,
/// Uploader is done with uploading, all data has been sent.
Finished,
}
Expand All @@ -73,6 +85,36 @@ impl Uploader {
}
}

/// Load uploader data from an ELF file.
///
/// # Errors
/// Any ELF loading errors are passed on.
///
/// # Panics
/// All panics are programming bugs.
pub fn from_elf(file: &ElfFile32) -> Result<Self, Box<dyn Error>> {
let entry_point = file.raw_header().e_entry.get(file.endian()) as u16;
let mut this = Self::new().with_entry_point(entry_point);

for segment in file.segments() {
let start_address = segment.address() as u16;
let data = segment.data()?;
let (blocks, last_block) = data.as_chunks::<256>();
this = blocks.iter().enumerate().fold(this, |this, (i, block)| {
this.with_block(DataBlock::new_full(start_address + (i * 256) as u16, *block))
});
if !last_block.is_empty() {
this = this.with_block(
DataBlock::new(start_address + (blocks.len() * 256) as u16, last_block.iter().copied()).unwrap(),
);
}
}

info!("Loaded {} blocks from ELF file.", this.remaining_blocks.len());

Ok(this)
}

/// Specify a code entry point the uploader will send after all blocks have been transferred. This API should be
/// used as soon as possible, in particular before all data blocks are sent.
#[must_use]
Expand Down Expand Up @@ -101,18 +143,23 @@ impl Uploader {
match self.state {
UploaderState::WaitingForAA =>
if ports.read_from_smp::<0>() == 0xAA {
debug!("Read AA, waiting for BB...");
self.state = UploaderState::WaitingForBB;
},
UploaderState::WaitingForBB =>
if ports.read_from_smp::<1>() == 0xBB {
debug!("Read BB, sending CC and start address");
ports.write_to_smp::<1>(0x01);
self.write_address(ports);
ports.write_to_smp::<0>(0xCC);
self.state = UploaderState::WaitingForAcknowledge;
self.state = UploaderState::WaitingForCC;
},
UploaderState::WaitingForCC =>
if ports.read_from_smp::<0>() == 0xCC {
debug!("Read CC, starting first block transfer");
ports.write_to_smp::<0>(0x00);
todo!();
},
UploaderState::WaitingForAcknowledge => {
todo!()
},
UploaderState::Finished => {},
}
}
Expand Down

0 comments on commit 9ad13f7

Please sign in to comment.