assembler: enhance error handling and tokenization logic

This commit is contained in:
2025-06-26 17:00:14 +01:00
parent 40f8b1d57b
commit ed4fcc8495
11 changed files with 514 additions and 98 deletions
+2 -1
View File
@@ -1,9 +1,10 @@
use crate::{instructions::encode::Encode, prelude::*};
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
pub enum Interrupt {
Software(u8),
Breakpoint,
#[default]
HardFault,
}
+14 -9
View File
@@ -1,4 +1,5 @@
//! Various types of arguments that instructions can take, alongside encoding and decoding logic.
//! Various types of arguments that instructions can take, alongside encoding and decoding
//! logic.
use crate::{
instructions::{RegisterParseError, encode::Encode},
@@ -35,18 +36,20 @@ impl std::fmt::Display for ArgsDecodeError {
impl std::error::Error for ArgsDecodeError {}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
/// Used by instructions with 2 registers and an immediate argument.
pub struct ITypeArgs {
pub immediate: u16,
pub r1: Register,
/// May not actually be used by some instructions taking an immediate e.g. LUI. This is solved by making the constructor take Options.
/// May not actually be used by some instructions taking an immediate e.g. LUI. This
/// is solved by making the constructor take Options.
pub r2: Register,
}
impl ITypeArgs {
#[must_use]
/// Creates a new [`ITypeArgs`]. If r1 or r2 is unset, they will be replaced with [`Register::NoReg`].
/// Creates a new [`ITypeArgs`]. If r1 or r2 is unset, they will be replaced with
/// [`Register::NoReg`].
pub fn new(immediate: u16, r1: Option<Register>, r2: Option<Register>) -> Self {
let r1 = r1.unwrap_or_default();
let r2 = r2.unwrap_or_default();
@@ -56,8 +59,8 @@ impl ITypeArgs {
}
impl Encode for ITypeArgs {
/// Encodes an I-type instruction from its fields. These must have some unused high-order
/// bits set to 0 else the bit shifting logic gets fucked.
/// Encodes an I-type instruction from its fields. These must have some unused
/// high-order bits set to 0 else the bit shifting logic gets fucked.
fn encode(self, opcode: u8) -> u32 {
let opcode = u32::from(opcode);
let r1 = self.r1 as u32;
@@ -84,7 +87,7 @@ impl TryFrom<u32> for ITypeArgs {
}
/// Used by instructions not using immediates (besides 5 bit shift values).
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct RTypeArgs {
pub sr1: Register,
pub sr2: Register,
@@ -95,7 +98,8 @@ pub struct RTypeArgs {
impl RTypeArgs {
#[must_use]
/// Creates a new [`RTypeArgs`]. If any registers are unset, they will be replaced with [`Register::NoReg`]. If `shamt` is unset, it will be set to 0.
/// Creates a new [`RTypeArgs`]. If any registers are unset, they will be replaced
/// with [`Register::NoReg`]. If `shamt` is unset, it will be set to 0.
pub fn new(
sr1: Option<Register>,
sr2: Option<Register>,
@@ -122,7 +126,8 @@ impl Encode for RTypeArgs {
///
/// # Arguments
///
/// - `shamt`: The amount to shift value (used only in shift instructions, otherwise 0).
/// - `shamt`: The amount to shift value (used only in shift instructions, otherwise
/// 0).
fn encode(self, opcode: u8) -> u32 {
let opcode = u32::from(opcode);
let sr1 = self.sr1 as u32;