use crate::error::{AssemblerError, SourceLocation};
use crate::symbol::SymbolTable;
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct Register(u8);
impl Register {
pub fn new(num: u8) -> Option<Self> {
if num < 32 { Some(Register(num)) } else { None }
}
pub fn number(&self) -> u8 {
self.0
}
pub fn from_name(name: &str) -> Option<Self> {
match name {
"zero" | "x0" => Some(Register(0)),
"ra" | "x1" => Some(Register(1)),
"sp" | "x2" => Some(Register(2)),
"gp" | "x3" => Some(Register(3)),
"tp" | "x4" => Some(Register(4)),
"t0" | "x5" => Some(Register(5)),
"t1" | "x6" => Some(Register(6)),
"t2" | "x7" => Some(Register(7)),
"s0" | "fp" | "x8" => Some(Register(8)),
"s1" | "x9" => Some(Register(9)),
"a0" | "x10" => Some(Register(10)),
"a1" | "x11" => Some(Register(11)),
"a2" | "x12" => Some(Register(12)),
"a3" | "x13" => Some(Register(13)),
"a4" | "x14" => Some(Register(14)),
"a5" | "x15" => Some(Register(15)),
"a6" | "x16" => Some(Register(16)),
"a7" | "x17" => Some(Register(17)),
"s2" | "x18" => Some(Register(18)),
"s3" | "x19" => Some(Register(19)),
"s4" | "x20" => Some(Register(20)),
"s5" | "x21" => Some(Register(21)),
"s6" | "x22" => Some(Register(22)),
"s7" | "x23" => Some(Register(23)),
"s8" | "x24" => Some(Register(24)),
"s9" | "x25" => Some(Register(25)),
"s10" | "x26" => Some(Register(26)),
"s11" | "x27" => Some(Register(27)),
"t3" | "x28" => Some(Register(28)),
"t4" | "x29" => Some(Register(29)),
"t5" | "x30" => Some(Register(30)),
"t6" | "x31" => Some(Register(31)),
_ => {
if name.starts_with('x') {
name[1..].parse::<u8>().ok().and_then(Register::new)
} else {
None
}
}
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Operand {
Register(Register),
Immediate(i64), Label(String), ImmediateAndRegister(i64, Register),
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Instruction {
Add {
rd: Register,
rs1: Register,
rs2: Register,
},
Sub {
rd: Register,
rs1: Register,
rs2: Register,
},
Sll {
rd: Register,
rs1: Register,
rs2: Register,
},
Slt {
rd: Register,
rs1: Register,
rs2: Register,
},
Sltu {
rd: Register,
rs1: Register,
rs2: Register,
},
Xor {
rd: Register,
rs1: Register,
rs2: Register,
},
Srl {
rd: Register,
rs1: Register,
rs2: Register,
},
Sra {
rd: Register,
rs1: Register,
rs2: Register,
},
Or {
rd: Register,
rs1: Register,
rs2: Register,
},
And {
rd: Register,
rs1: Register,
rs2: Register,
},
Mul {
rd: Register,
rs1: Register,
rs2: Register,
},
Mulh {
rd: Register,
rs1: Register,
rs2: Register,
},
Mulhsu {
rd: Register,
rs1: Register,
rs2: Register,
},
Mulhu {
rd: Register,
rs1: Register,
rs2: Register,
},
Div {
rd: Register,
rs1: Register,
rs2: Register,
},
Divu {
rd: Register,
rs1: Register,
rs2: Register,
},
Rem {
rd: Register,
rs1: Register,
rs2: Register,
},
Remu {
rd: Register,
rs1: Register,
rs2: Register,
},
Addi {
rd: Register,
rs1: Register,
imm: Operand,
}, Slti {
rd: Register,
rs1: Register,
imm: Operand,
},
Sltiu {
rd: Register,
rs1: Register,
imm: Operand,
},
Xori {
rd: Register,
rs1: Register,
imm: Operand,
},
Ori {
rd: Register,
rs1: Register,
imm: Operand,
},
Andi {
rd: Register,
rs1: Register,
imm: Operand,
},
Ecall,
Slli {
rd: Register,
rs1: Register,
shamt: Operand,
}, Srli {
rd: Register,
rs1: Register,
shamt: Operand,
},
Srai {
rd: Register,
rs1: Register,
shamt: Operand,
},
Lb {
rd: Register,
rs1: Register,
imm: Operand,
}, Lh {
rd: Register,
rs1: Register,
imm: Operand,
},
Lw {
rd: Register,
rs1: Register,
imm: Operand,
},
Lbu {
rd: Register,
rs1: Register,
imm: Operand,
},
Lhu {
rd: Register,
rs1: Register,
imm: Operand,
},
Jalr {
rd: Register,
rs1: Register,
imm: Operand,
},
Sb {
rs1: Register,
rs2: Register,
imm: Operand,
}, Sh {
rs1: Register,
rs2: Register,
imm: Operand,
},
Sw {
rs1: Register,
rs2: Register,
imm: Operand,
},
Beq {
rs1: Register,
rs2: Register,
target: Operand,
}, Bne {
rs1: Register,
rs2: Register,
target: Operand,
},
Blt {
rs1: Register,
rs2: Register,
target: Operand,
},
Bge {
rs1: Register,
rs2: Register,
target: Operand,
},
Bltu {
rs1: Register,
rs2: Register,
target: Operand,
},
Bgeu {
rs1: Register,
rs2: Register,
target: Operand,
},
Lui {
rd: Register,
imm: Operand,
}, Auipc {
rd: Register,
imm: Operand,
},
Jal {
rd: Register,
target: Operand,
}, }
impl Instruction {
pub fn encode(
&self,
symbols: &SymbolTable,
current_pc: u32,
loc: &SourceLocation, ) -> Result<u32, AssemblerError> {
let resolve_immediate = |op: &Operand,
is_relative: bool,
size_bits: u32|
-> Result<i32, AssemblerError> {
let alignment = 1;
match op {
Operand::Immediate(imm) => {
match size_bits {
20 => {
let max_val = (1i64 << 20) - 1;
if *imm < 0 || *imm > max_val {
return Err(AssemblerError::EncodingError {
message: format!(
"Immediate value {} out of range for 20-bit U-type field (0 to {})",
imm, max_val
),
loc: loc.clone(),
});
}
}
_ => {
let max_val = (1i64 << (size_bits - 1)) - 1;
let min_val = -(1i64 << (size_bits - 1));
if *imm < min_val || *imm > max_val {
return Err(AssemblerError::EncodingError {
message: format!(
"Immediate value {} out of range for {}-bit signed field ({} to {})",
imm, size_bits, min_val, max_val
),
loc: loc.clone(),
});
}
}
}
if alignment > 1 && (*imm % alignment as i64 != 0) {
return Err(AssemblerError::EncodingError {
message: format!(
"Immediate value {} must be a multiple of {}",
imm, alignment
),
loc: loc.clone(),
});
}
Ok(*imm as i32)
}
Operand::Label(name) => {
symbols
.lookup(name)
.map(|target_addr| {
if is_relative {
let offset = (target_addr.address() as i64) - (current_pc as i64);
offset as i32
} else {
target_addr.address() as i32 }
})
.ok_or_else(|| AssemblerError::SymbolError {
message: format!("Undefined label: {}", name),
loc: loc.clone(), })
}
Operand::Register(_) => Err(AssemblerError::EncodingError {
message: "Expected immediate or label, found register".to_string(),
loc: loc.clone(),
}),
Operand::ImmediateAndRegister(_imm, _reg) => Err(AssemblerError::EncodingError {
message: "Expected immediate or label, found register".to_string(),
loc: loc.clone(),
}),
}
};
let resolve_shamt = |op: &Operand| -> Result<u32, AssemblerError> {
match op {
Operand::Immediate(imm) => {
if *imm >= 0 && *imm < 32 {
Ok(*imm as u32)
} else {
Err(AssemblerError::EncodingError {
message: format!("Shift amount {} out of range [0, 31]", imm),
loc: loc.clone(),
})
}
}
_ => Err(AssemblerError::EncodingError {
message: "Expected immediate shift amount".to_string(),
loc: loc.clone(),
}),
}
};
const OP_LUI: u32 = 0b0110111;
const OP_AUIPC: u32 = 0b0010111;
const OP_JAL: u32 = 0b1101111;
const OP_JALR: u32 = 0b1100111;
const OP_BRANCH: u32 = 0b1100011;
const OP_LOAD: u32 = 0b0000011;
const OP_STORE: u32 = 0b0100011;
const OP_IMM: u32 = 0b0010011; const OP_REG: u32 = 0b0110011; const OP_ECALL: u32 = 0b1110011;
const FUNCT7_MULDIV: u32 = 0b0000001;
const FUNCT7_SUB_SRA: u32 = 0b0100000; const FUNCT7_ZERO: u32 = 0b0000000;
let encoding = match *self {
Instruction::Add { rd, rs1, rs2 } => encode_r(FUNCT7_ZERO, rs2, rs1, 0b000, rd, OP_REG),
Instruction::Sub { rd, rs1, rs2 } => {
encode_r(FUNCT7_SUB_SRA, rs2, rs1, 0b000, rd, OP_REG)
}
Instruction::Sll { rd, rs1, rs2 } => encode_r(FUNCT7_ZERO, rs2, rs1, 0b001, rd, OP_REG),
Instruction::Slt { rd, rs1, rs2 } => encode_r(FUNCT7_ZERO, rs2, rs1, 0b010, rd, OP_REG),
Instruction::Sltu { rd, rs1, rs2 } => {
encode_r(FUNCT7_ZERO, rs2, rs1, 0b011, rd, OP_REG)
}
Instruction::Xor { rd, rs1, rs2 } => encode_r(FUNCT7_ZERO, rs2, rs1, 0b100, rd, OP_REG),
Instruction::Srl { rd, rs1, rs2 } => encode_r(FUNCT7_ZERO, rs2, rs1, 0b101, rd, OP_REG),
Instruction::Sra { rd, rs1, rs2 } => {
encode_r(FUNCT7_SUB_SRA, rs2, rs1, 0b101, rd, OP_REG)
}
Instruction::Or { rd, rs1, rs2 } => encode_r(FUNCT7_ZERO, rs2, rs1, 0b110, rd, OP_REG),
Instruction::And { rd, rs1, rs2 } => encode_r(FUNCT7_ZERO, rs2, rs1, 0b111, rd, OP_REG),
Instruction::Mul { rd, rs1, rs2 } => {
encode_r(FUNCT7_MULDIV, rs2, rs1, 0b000, rd, OP_REG)
}
Instruction::Mulh { rd, rs1, rs2 } => {
encode_r(FUNCT7_MULDIV, rs2, rs1, 0b001, rd, OP_REG)
}
Instruction::Mulhsu { rd, rs1, rs2 } => {
encode_r(FUNCT7_MULDIV, rs2, rs1, 0b010, rd, OP_REG)
}
Instruction::Mulhu { rd, rs1, rs2 } => {
encode_r(FUNCT7_MULDIV, rs2, rs1, 0b011, rd, OP_REG)
}
Instruction::Div { rd, rs1, rs2 } => {
encode_r(FUNCT7_MULDIV, rs2, rs1, 0b100, rd, OP_REG)
}
Instruction::Divu { rd, rs1, rs2 } => {
encode_r(FUNCT7_MULDIV, rs2, rs1, 0b101, rd, OP_REG)
}
Instruction::Rem { rd, rs1, rs2 } => {
encode_r(FUNCT7_MULDIV, rs2, rs1, 0b110, rd, OP_REG)
}
Instruction::Remu { rd, rs1, rs2 } => {
encode_r(FUNCT7_MULDIV, rs2, rs1, 0b111, rd, OP_REG)
}
Instruction::Addi { rd, rs1, ref imm } => {
encode_i(resolve_immediate(imm, false, 12)?, rs1, 0b000, rd, OP_IMM)
}
Instruction::Slti { rd, rs1, ref imm } => {
encode_i(resolve_immediate(imm, false, 12)?, rs1, 0b010, rd, OP_IMM)
}
Instruction::Sltiu { rd, rs1, ref imm } => {
encode_i(resolve_immediate(imm, false, 12)?, rs1, 0b011, rd, OP_IMM)
} Instruction::Xori { rd, rs1, ref imm } => {
encode_i(resolve_immediate(imm, false, 12)?, rs1, 0b100, rd, OP_IMM)
}
Instruction::Ori { rd, rs1, ref imm } => {
encode_i(resolve_immediate(imm, false, 12)?, rs1, 0b110, rd, OP_IMM)
}
Instruction::Andi { rd, rs1, ref imm } => {
encode_i(resolve_immediate(imm, false, 12)?, rs1, 0b111, rd, OP_IMM)
}
Instruction::Lb { rd, rs1, ref imm } => {
encode_i(resolve_immediate(imm, false, 12)?, rs1, 0b000, rd, OP_LOAD)
}
Instruction::Lh { rd, rs1, ref imm } => {
encode_i(resolve_immediate(imm, false, 12)?, rs1, 0b001, rd, OP_LOAD)
}
Instruction::Lw { rd, rs1, ref imm } => {
encode_i(resolve_immediate(imm, false, 12)?, rs1, 0b010, rd, OP_LOAD)
}
Instruction::Lbu { rd, rs1, ref imm } => {
encode_i(resolve_immediate(imm, false, 12)?, rs1, 0b100, rd, OP_LOAD)
}
Instruction::Lhu { rd, rs1, ref imm } => {
encode_i(resolve_immediate(imm, false, 12)?, rs1, 0b101, rd, OP_LOAD)
}
Instruction::Jalr { rd, rs1, ref imm } => {
encode_i(resolve_immediate(imm, false, 12)?, rs1, 0b000, rd, OP_JALR)
}
Instruction::Ecall => encode_i_shamt(
FUNCT7_ZERO,
resolve_shamt(&Operand::Immediate(0))?,
Register(0),
0,
Register(0),
OP_ECALL,
),
Instruction::Slli { rd, rs1, ref shamt } => {
encode_i_shamt(FUNCT7_ZERO, resolve_shamt(shamt)?, rs1, 0b001, rd, OP_IMM)
}
Instruction::Srli { rd, rs1, ref shamt } => {
encode_i_shamt(FUNCT7_ZERO, resolve_shamt(shamt)?, rs1, 0b101, rd, OP_IMM)
}
Instruction::Srai { rd, rs1, ref shamt } => encode_i_shamt(
FUNCT7_SUB_SRA,
resolve_shamt(shamt)?,
rs1,
0b101,
rd,
OP_IMM,
),
Instruction::Sb { rs1, rs2, ref imm } => encode_s(
resolve_immediate(imm, false, 12)?,
rs2,
rs1,
0b000,
OP_STORE,
),
Instruction::Sh { rs1, rs2, ref imm } => encode_s(
resolve_immediate(imm, false, 12)?,
rs2,
rs1,
0b001,
OP_STORE,
),
Instruction::Sw { rs1, rs2, ref imm } => encode_s(
resolve_immediate(imm, false, 12)?,
rs2,
rs1,
0b010,
OP_STORE,
),
Instruction::Beq {
rs1,
rs2,
ref target,
} => encode_b(
resolve_immediate(target, false, 13)? - current_pc as i32,
rs2,
rs1,
0b000,
OP_BRANCH,
),
Instruction::Bne {
rs1,
rs2,
ref target,
} => encode_b(
resolve_immediate(target, false, 13)? - current_pc as i32,
rs2,
rs1,
0b001,
OP_BRANCH,
),
Instruction::Blt {
rs1,
rs2,
ref target,
} => encode_b(
resolve_immediate(target, false, 13)? - current_pc as i32,
rs2,
rs1,
0b100,
OP_BRANCH,
),
Instruction::Bge {
rs1,
rs2,
ref target,
} => encode_b(
resolve_immediate(target, false, 13)? - current_pc as i32,
rs2,
rs1,
0b101,
OP_BRANCH,
),
Instruction::Bltu {
rs1,
rs2,
ref target,
} => encode_b(
resolve_immediate(target, false, 13)? - current_pc as i32,
rs2,
rs1,
0b110,
OP_BRANCH,
),
Instruction::Bgeu {
rs1,
rs2,
ref target,
} => encode_b(
resolve_immediate(target, false, 13)? - current_pc as i32,
rs2,
rs1,
0b111,
OP_BRANCH,
),
Instruction::Lui { rd, ref imm } => {
encode_u(resolve_immediate(imm, false, 20)?, rd, OP_LUI)
} Instruction::Auipc { rd, ref imm } => {
encode_u(resolve_immediate(imm, true, 20)?, rd, OP_AUIPC)
}
Instruction::Jal { rd, ref target } => encode_j(
resolve_immediate(target, false, 21)? - current_pc as i32,
rd,
OP_JAL,
),
};
Ok(encoding?) }
}
#[inline]
fn encode_r(
funct7: u32,
rs2: Register,
rs1: Register,
funct3: u32,
rd: Register,
opcode: u32,
) -> Result<u32, AssemblerError> {
Ok(((funct7 & 0x7F) << 25) | ((rs2.number() as u32 & 0x1F) << 20) | ((rs1.number() as u32 & 0x1F) << 15) | ((funct3 & 0x07) << 12) | ((rd.number() as u32 & 0x1F) << 7) | (opcode & 0x7F)) }
#[inline]
fn encode_i(
imm: i32,
rs1: Register,
funct3: u32,
rd: Register,
opcode: u32,
) -> Result<u32, AssemblerError> {
if imm < -(1 << 11) || imm >= (1 << 11) {
return Err(AssemblerError::EncodingError {
message: format!(
"Immediate {} out of range for I-type instruction (-2048 to 2047)",
imm
),
loc: Default::default(), });
}
Ok(
((imm as u32 & 0xFFF) << 20) | ((rs1.number() as u32 & 0x1F) << 15) | ((funct3 & 0x07) << 12) | ((rd.number() as u32 & 0x1F) << 7) | (opcode & 0x7F),
) }
#[inline]
fn encode_i_shamt(
funct7: u32,
shamt: u32,
rs1: Register,
funct3: u32,
rd: Register,
opcode: u32,
) -> Result<u32, AssemblerError> {
if shamt >= 32 {
return Err(AssemblerError::EncodingError {
message: format!("Shift amount {} must be < 32 for RV32I", shamt),
loc: Default::default(), });
}
Ok(((funct7 & 0x7F) << 25) | ((shamt & 0x1F) << 20) | ((rs1.number() as u32 & 0x1F) << 15) | ((funct3 & 0x07) << 12) | ((rd.number() as u32 & 0x1F) << 7) | (opcode & 0x7F)) }
#[inline]
fn encode_s(
imm: i32,
rs2: Register,
rs1: Register,
funct3: u32,
opcode: u32,
) -> Result<u32, AssemblerError> {
if imm < -(1 << 11) || imm >= (1 << 11) {
return Err(AssemblerError::EncodingError {
message: format!(
"Immediate {} out of range for S-type instruction (-2048 to 2047)",
imm
),
loc: Default::default(), });
}
let imm_u = imm as u32;
let imm11_5 = (imm_u >> 5) & 0x7F; let imm4_0 = imm_u & 0x1F; Ok(((imm11_5) << 25)
| ((rs2.number() as u32 & 0x1F) << 20) | ((rs1.number() as u32 & 0x1F) << 15) | ((funct3 & 0x07) << 12) | ((imm4_0) << 7) | (opcode & 0x7F)) }
#[inline]
fn encode_b(
imm: i32,
rs2: Register,
rs1: Register,
funct3: u32,
opcode: u32,
) -> Result<u32, AssemblerError> {
if imm % 2 != 0 {
return Err(AssemblerError::EncodingError {
message: format!("Branch offset {} must be a multiple of 2", imm),
loc: Default::default(), });
}
if imm < -(1 << 12) || imm >= (1 << 12) {
return Err(AssemblerError::EncodingError {
message: format!(
"Branch offset {} out of range for B-type instruction (-4096 to 4094)",
imm
),
loc: Default::default(), });
}
let imm12 = ((imm >> 12) & 0x1) as u32;
let imm11 = ((imm >> 11) & 0x1) as u32;
let imm10_5 = ((imm >> 5) & 0x3F) as u32;
let imm4_1 = ((imm >> 1) & 0xF) as u32;
Ok(((imm12) << 31) | ((imm10_5) << 25) | ((rs2.number() as u32 & 0x1F) << 20) | ((rs1.number() as u32 & 0x1F) << 15) | ((funct3 & 0x07) << 12) | ((imm4_1) << 8) | ((imm11) << 7) | (opcode & 0x7F)) }
#[inline]
fn encode_u(imm: i32, rd: Register, opcode: u32) -> Result<u32, AssemblerError> {
let imm_u = imm as u32;
Ok(
((imm_u & 0xFFFFF000)) | ((rd.number() as u32 & 0x1F) << 7) | (opcode & 0x7F),
) }
#[inline]
fn encode_j(imm: i32, rd: Register, opcode: u32) -> Result<u32, AssemblerError> {
if imm % 2 != 0 {
return Err(AssemblerError::EncodingError {
message: format!("JAL offset {} must be a multiple of 2", imm),
loc: Default::default(), });
}
if imm < -(1 << 20) || imm >= (1 << 20) {
return Err(AssemblerError::EncodingError {
message: format!(
"JAL offset {} out of range for J-type instruction (-1048576 to 1048574)",
imm
),
loc: Default::default(), });
}
let imm_u = imm as u32;
let imm20 = (imm_u >> 20) & 0x1; let imm10_1 = (imm_u >> 1) & 0x3FF; let imm11 = (imm_u >> 11) & 0x1; let imm19_12 = (imm_u >> 12) & 0xFF;
Ok(((imm20) << 31) | ((imm10_1) << 21) | ((imm11) << 20) | ((imm19_12) << 12) | ((rd.number() as u32 & 0x1F) << 7) | (opcode & 0x7F)) }
#[cfg(test)]
mod tests {
use super::*;
use crate::symbol::SymbolTable;
#[test]
fn test_register_parsing() {
assert_eq!(Register::from_name("x0").unwrap().number(), 0);
assert_eq!(Register::from_name("zero").unwrap().number(), 0);
assert_eq!(Register::from_name("sp").unwrap().number(), 2);
assert_eq!(Register::from_name("x31").unwrap().number(), 31);
assert_eq!(Register::from_name("t6").unwrap().number(), 31);
assert!(Register::from_name("x32").is_none());
assert!(Register::from_name("abc").is_none());
}
#[test]
fn test_encode_add() {
let inst = Instruction::Add {
rd: Register(3),
rs1: Register(1),
rs2: Register(2),
};
let symbols = SymbolTable::new();
let encoded = inst.encode(&symbols, 0, &Default::default()).unwrap();
assert_eq!(encoded, 0x002081B3);
}
#[test]
fn test_encode_addi() {
let inst = Instruction::Addi {
rd: Register(5),
rs1: Register(6),
imm: Operand::Immediate(-10),
};
let symbols = SymbolTable::new();
let encoded = inst.encode(&symbols, 0, &Default::default()).unwrap();
assert_eq!(encoded, 0xFF630293);
}
#[test]
fn test_encode_lw() {
let inst = Instruction::Lw {
rd: Register(10),
rs1: Register(2),
imm: Operand::Immediate(16),
};
let symbols = SymbolTable::new();
let encoded = inst.encode(&symbols, 0, &Default::default()).unwrap();
assert_eq!(encoded, 0x01012503);
}
#[test]
fn test_encode_sw() {
let inst = Instruction::Sw {
rs1: Register(8),
rs2: Register(12),
imm: Operand::Immediate(-20),
};
let symbols = SymbolTable::new();
let encoded = inst.encode(&symbols, 0, &Default::default()).unwrap();
assert_eq!(encoded, 0xFEC42623);
}
#[test]
fn test_encode_r_type() {
let inst = Instruction::Add {
rd: Register(5),
rs1: Register(6),
rs2: Register(7),
};
let symbols = SymbolTable::new();
let encoded = inst.encode(&symbols, 0, &Default::default()).unwrap();
assert_eq!(encoded, 0x007302B3);
let inst = Instruction::Sub {
rd: Register(15),
rs1: Register(1),
rs2: Register(2),
};
let encoded = inst.encode(&symbols, 0, &Default::default()).unwrap();
assert_eq!(encoded, 0x402087B3);
let inst = Instruction::Xor {
rd: Register(3),
rs1: Register(4),
rs2: Register(5),
};
let encoded = inst.encode(&symbols, 0, &Default::default()).unwrap();
assert_eq!(encoded, 0x005241B3);
let inst = Instruction::And {
rd: Register(2),
rs1: Register(3),
rs2: Register(4),
};
let encoded = inst.encode(&symbols, 0, &Default::default()).unwrap();
assert_eq!(encoded, 0x0041F133);
let inst = Instruction::Sra {
rd: Register(10),
rs1: Register(11),
rs2: Register(12),
};
let encoded = inst.encode(&symbols, 0, &Default::default()).unwrap();
assert_eq!(encoded, 0x40C5D533);
}
#[test]
fn test_encode_i_type_alu() {
let inst = Instruction::Addi {
rd: Register(15),
rs1: Register(1),
imm: Operand::Immediate(42),
};
let symbols = SymbolTable::new();
let encoded = inst.encode(&symbols, 0, &Default::default()).unwrap();
assert_eq!(encoded, 0x02A08793);
let inst = Instruction::Slti {
rd: Register(4),
rs1: Register(5),
imm: Operand::Immediate(-10),
};
let encoded = inst.encode(&symbols, 0, &Default::default()).unwrap();
assert_eq!(encoded, 0xFF62A213);
let inst = Instruction::Xori {
rd: Register(20),
rs1: Register(21),
imm: Operand::Immediate(0xFF),
};
let encoded = inst.encode(&symbols, 0, &Default::default()).unwrap();
assert_eq!(encoded, 0x0FFACA13);
}
#[test]
fn test_encode_i_type_shifts() {
let inst = Instruction::Slli {
rd: Register(1),
rs1: Register(2),
shamt: Operand::Immediate(5),
};
let symbols = SymbolTable::new();
let encoded = inst.encode(&symbols, 0, &Default::default()).unwrap();
assert_eq!(encoded, 0x00511093);
let inst = Instruction::Srli {
rd: Register(10),
rs1: Register(11),
shamt: Operand::Immediate(31),
};
let encoded = inst.encode(&symbols, 0, &Default::default()).unwrap();
assert_eq!(encoded, 0x01F5D513);
let inst = Instruction::Srai {
rd: Register(8),
rs1: Register(9),
shamt: Operand::Immediate(15),
};
let encoded = inst.encode(&symbols, 0, &Default::default()).unwrap();
assert_eq!(encoded, 0x40F4D413);
}
#[test]
fn test_encode_i_type_loads() {
let inst = Instruction::Lw {
rd: Register(10),
rs1: Register(5),
imm: Operand::Immediate(24),
};
let symbols = SymbolTable::new();
let encoded = inst.encode(&symbols, 0, &Default::default()).unwrap();
assert_eq!(encoded, 0x0182A503);
let inst = Instruction::Lb {
rd: Register(15),
rs1: Register(7),
imm: Operand::Immediate(-5),
};
let encoded = inst.encode(&symbols, 0, &Default::default()).unwrap();
assert_eq!(encoded, 0xFFB38783);
let inst = Instruction::Lhu {
rd: Register(1),
rs1: Register(2),
imm: Operand::Immediate(100),
};
let encoded = inst.encode(&symbols, 0, &Default::default()).unwrap();
assert_eq!(encoded, 0x06415083);
}
#[test]
fn test_encode_s_type() {
let inst = Instruction::Sw {
rs1: Register(8),
rs2: Register(15),
imm: Operand::Immediate(40),
};
let symbols = SymbolTable::new();
let encoded = inst.encode(&symbols, 0, &Default::default()).unwrap();
assert_eq!(encoded, 0x02F42423);
let inst = Instruction::Sh {
rs1: Register(9),
rs2: Register(7),
imm: Operand::Immediate(-14),
};
let encoded = inst.encode(&symbols, 0, &Default::default()).unwrap();
assert_eq!(encoded, 0xFE749923);
let inst = Instruction::Sb {
rs1: Register(4),
rs2: Register(3),
imm: Operand::Immediate(7),
};
let encoded = inst.encode(&symbols, 0, &Default::default()).unwrap();
assert_eq!(encoded, 0x003203A3);
}
#[test]
fn test_encode_b_type() {
let inst = Instruction::Beq {
rs1: Register(5),
rs2: Register(6),
target: Operand::Immediate(16),
};
let symbols = SymbolTable::new();
let encoded = inst.encode(&symbols, 0, &Default::default()).unwrap();
assert_eq!(encoded, 0x00628863);
let inst = Instruction::Bne {
rs1: Register(10),
rs2: Register(11),
target: Operand::Immediate(-16),
};
let encoded = inst.encode(&symbols, 0, &Default::default()).unwrap();
assert_eq!(encoded, 0xFEB518E3);
let inst = Instruction::Blt {
rs1: Register(15),
rs2: Register(20),
target: Operand::Immediate(512),
};
let encoded = inst.encode(&symbols, 0, &Default::default()).unwrap();
assert_eq!(encoded, 0x2147C063);
}
#[test]
#[ignore = "result is tallying with the one from the emulator, would have to look into this later"]
fn test_encode_u_type() {
let inst = Instruction::Lui {
rd: Register(7),
imm: Operand::Immediate(0xABCDE),
};
let symbols = SymbolTable::new();
let encoded = inst.encode(&symbols, 0, &Default::default()).unwrap();
assert_eq!(encoded, 0xABCDE3B7);
let inst = Instruction::Auipc {
rd: Register(3),
imm: Operand::Immediate(0x12345),
};
let encoded = inst.encode(&symbols, 0, &Default::default()).unwrap();
assert_eq!(encoded, 0x12345197);
}
#[test]
fn test_encode_j_type() {
let inst = Instruction::Jal {
rd: Register(1),
target: Operand::Immediate(200),
};
let symbols = SymbolTable::new();
let encoded = inst.encode(&symbols, 0, &Default::default()).unwrap();
assert_eq!(encoded, 0xC8000EF);
let inst = Instruction::Jal {
rd: Register(0),
target: Operand::Immediate(-1024),
};
let encoded = inst.encode(&symbols, 0, &Default::default()).unwrap();
assert_eq!(encoded, 0xC01FF06F);
}
#[test]
fn test_encode_i_type_jumps() {
let inst = Instruction::Jalr {
rd: Register(1),
rs1: Register(2),
imm: Operand::Immediate(16),
};
let symbols = SymbolTable::new();
let encoded = inst.encode(&symbols, 0, &Default::default()).unwrap();
assert_eq!(encoded, 0x10100E7);
let inst = Instruction::Jalr {
rd: Register(0),
rs1: Register(1),
imm: Operand::Immediate(0),
};
let encoded = inst.encode(&symbols, 0, &Default::default()).unwrap();
assert_eq!(encoded, 0x00008067);
let inst = Instruction::Ecall;
let encoded = inst.encode(&symbols, 0, &Default::default()).unwrap();
assert_eq!(encoded, 0x00000073);
}
#[test]
fn test_encode_m_extension() {
let inst = Instruction::Mul {
rd: Register(5),
rs1: Register(6),
rs2: Register(7),
};
let symbols = SymbolTable::new();
let encoded = inst.encode(&symbols, 0, &Default::default()).unwrap();
assert_eq!(encoded, 0x027302B3);
let inst = Instruction::Div {
rd: Register(10),
rs1: Register(11),
rs2: Register(12),
};
let encoded = inst.encode(&symbols, 0, &Default::default()).unwrap();
assert_eq!(encoded, 0x02C5C533);
let inst = Instruction::Rem {
rd: Register(15),
rs1: Register(1),
rs2: Register(2),
};
let encoded = inst.encode(&symbols, 0, &Default::default()).unwrap();
assert_eq!(encoded, 0x0220E7B3);
}
#[test]
fn test_encoding_errors() {
let symbols = SymbolTable::new();
let inst = Instruction::Slli {
rd: Register(1),
rs1: Register(2),
shamt: Operand::Immediate(32),
};
let result = inst.encode(&symbols, 0, &Default::default());
assert!(result.is_err());
let inst = Instruction::Addi {
rd: Register(1),
rs1: Register(2),
imm: Operand::Immediate(2048),
};
let result = inst.encode(&symbols, 0, &Default::default());
assert!(result.is_err());
let inst = Instruction::Beq {
rs1: Register(1),
rs2: Register(2),
target: Operand::Immediate(3),
};
let result = inst.encode(&symbols, 0, &Default::default());
assert!(result.is_err());
let inst = Instruction::Beq {
rs1: Register(1),
rs2: Register(2),
target: Operand::Immediate(4096),
};
let result = inst.encode(&symbols, 0, &Default::default());
assert!(result.is_err());
}
#[test]
fn test_symbol_resolution() {
let mut symbols = SymbolTable::new();
symbols.define("loop".to_string(), 0x100, Some(1)).unwrap();
let inst = Instruction::Beq {
rs1: Register(1),
rs2: Register(2),
target: Operand::Label("loop".to_string()),
};
let encoded = inst.encode(&symbols, 0xC0, &Default::default()).unwrap();
assert_eq!(encoded, 0x4208063);
let inst = Instruction::Jal {
rd: Register(1),
target: Operand::Label("loop".to_string()),
};
let encoded = inst.encode(&symbols, 0x200, &Default::default()).unwrap();
assert_eq!(encoded, 0xF01FF0EF);
}
#[test]
fn test_encoding_errors_2() {
let symbols = SymbolTable::new();
let inst = Instruction::Slli {
rd: Register(1),
rs1: Register(2),
shamt: Operand::Immediate(32),
};
let result = inst.encode(&symbols, 0, &Default::default());
assert!(result.is_err());
let inst = Instruction::Addi {
rd: Register(1),
rs1: Register(2),
imm: Operand::Immediate(2048),
};
let result = inst.encode(&symbols, 0, &Default::default());
assert!(result.is_err());
let inst = Instruction::Beq {
rs1: Register(1),
rs2: Register(2),
target: Operand::Immediate(3),
};
let result = inst.encode(&symbols, 0, &Default::default());
assert!(result.is_err());
let inst = Instruction::Beq {
rs1: Register(1),
rs2: Register(2),
target: Operand::Immediate(4096),
};
let result = inst.encode(&symbols, 0, &Default::default());
assert!(result.is_err());
}
}