This document provides a comprehensive guide on how to execute assembly code using the EnthusiASM framework. Follow the steps below to get started.
Once the architecture is implemented, you can use the core library to assemble and execute assembly code. The steps to follow are:
parser.assembler.cpu and memory for the architecture.load_program function.cpu’s step function.I’m using the code from the example described in the Implementing a New Architecture document.
Imagine that we want to run this code:
addi r0, r0, 5
addi r1, r0, 10
add r0, r0, r1
First of all, we need to run the parser to convert the assembly code into an intermediate representation.
const PROGRAM: &str = r#"
addi r0, r0, 5
addi r1, r0, 10
add r0, r0, r1
"#;
fn main() {
let parser = SimpleParser;
let mut lines = vec![];
for (lineno, line) in PROGRAM.lines().enumerate() {
let parsed = parser.parse(line, lineno).expect("program parse error");
for instruction in parsed.tokens {
lines.push(instruction);
}
}
}
Then we need to assemble the parsed instructions into machine code.
fn main() {
// ... previous code ...
let program = SimpleAssembler.assemble(lines).expect("failed to assemble");
}
Next, we set up the CPU and memory for our architecture.
In our case, the default constructor is sufficient to also set up the memory.
fn main() {
// ... previous code ...
let mut cpu = SimpleCPU::default();
}
enthusiasm-core comes with a very simple function to load a program into memory, called load_program.
/// Loads a [`Program`](crate::prelude::Program) into the [`Memory`](crate::prelude::Memory)
/// of the given [`CPU`](crate::prelude::Cpu) starting at the specified base address.
///
/// It also sets the program counter to the program's entry point.
pub fn load_program<C, M, A, R>(cpu: &mut C, base: A, program: &Program<A>) -> EnthusiasmResult<()>
where
C: Cpu<Memory=M, Registers=R>,
M: Memory<Address=A>,
A: Into<u64> + TryFrom<u64> + Copy + std::fmt::Debug + std::fmt::Display + Eq + PartialEq,
R: Registers<Address=A>,
{
let memory = cpu.memory_mut();
for (i, byte) in program.bytes.iter().enumerate() {
let addr =
A::try_from(base.into() + i as u64).map_err(|_| EnthusiasmError::AddressOverflow)?;
memory.write8(addr, *byte)?;
}
// set program counter to entry point
let entry_point = A::try_from(base.into() + program.entry_point.into())
.map_err(|_| EnthusiasmError::AddressOverflow)?;
cpu.registers_mut().set_program_counter(entry_point);
Ok(())
}
What it does is to take the assembled program and a base address, and it writes the program bytes into memory starting from that address. It also sets the CPU’s program counter to the entry point of the program + base address.
Once loaded, the CPU is ready to execute the program.
fn main() {
// ... previous code ...
load_program(&mut cpu, 0u64, &program).expect("failed to load program");
}
Finally, we can execute the program by stepping through the instructions.
fn main() {
// ... previous code ...
for _ in 0..3 {
cpu.step().expect("failed to execute instruction");
}
println!("Register r0: {}", cpu.registers().get(0));
println!("Register r1: {}", cpu.registers().get(1));
}
In this case we are executing three instructions, that’s why we loop three times calling cpu.step().
In real scenarios, you would typically have a more complex loop that continues executing until a certain condition is met, such as reaching a specific instruction, an error, or a halt condition.
In particular, the general condition should be to run until EnthusiasmError::Cpu(CpuError::Halted)) is returned from
the step function.
Whenever you need to read the value of a register, you can use the registers() method of the CPU to get a reference to
the
registers, and then use the read method to read the value of a specific register
Similarly, to read a value from memory, you can use the memory() method of the CPU to get a reference to the memory,
and
then use the appropriate read method (e.g., read8, read16, read32, etc.) to read the value at a specific address.
By following the steps outlined in this guide, you can successfully execute assembly code using the EnthusiASM framework.