1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241
//! Representations of Falcon instruction operands.
use std::fmt;
use num_traits::{PrimInt, Signed, Unsigned};
fn display_signed_hex<I>(immediate: &I, f: &mut fmt::Formatter<'_>) -> fmt::Result
where
I: fmt::LowerHex + PrimInt + Signed,
{
let sign = if immediate.is_negative() { "-" } else { "" };
write!(f, "{}{:#x}", sign, immediate.abs())
}
fn display_unsigned_hex<I>(immediate: &I, f: &mut fmt::Formatter<'_>) -> fmt::Result
where
I: fmt::LowerHex + PrimInt + Unsigned,
{
write!(f, "{:#x}", immediate)
}
/// A Falcon CPU register.
///
/// The Falcon utilizes separate register files for 16 general-purpose registers
/// and another 16 special-purpose registers. This structure stores information
/// on the type of register and the register index itself.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Register(pub RegisterKind, pub usize);
impl fmt::Display for Register {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.0 {
RegisterKind::Gpr => write!(f, "$r{}", self.1),
RegisterKind::Spr => write!(f, "${}", get_spr_name(self.1)),
}
}
}
/// Types of CPU registers supported by the Falcon.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum RegisterKind {
/// A general-purpose CPU register.
Gpr,
/// A special-purpose CPU register.
Spr,
}
/// Gets the qualified name of a special-purpose register based on the given
/// register index.
#[rustfmt::skip]
pub fn get_spr_name(value: usize) -> &'static str {
[
/* 0x0 */ "iv0",
/* 0x1 */ "iv1",
/* 0x2 */ "iv2",
/* 0x3 */ "ev",
/* 0x4 */ "sp",
/* 0x5 */ "pc",
/* 0x6 */ "imb",
/* 0x7 */ "dmb",
/* 0x8 */ "csw",
/* 0x9 */ "ccr",
/* 0xA */ "sec",
/* 0xB */ "ctx",
/* 0xC */ "exci",
/* 0xD */ "sec1",
/* 0xE */ "imb1",
/* 0xF */ "dmb1",
][value]
}
/// Gets the qualified name of a flag bit in the `$flags` register based on the
/// given bit index.
#[rustfmt::skip]
pub fn get_flag_name(value: usize) -> Option<&'static str> {
[
/* 0x00 */ Some("p0"),
/* 0x01 */ Some("p1"),
/* 0x02 */ Some("p2"),
/* 0x03 */ Some("p3"),
/* 0x04 */ Some("p4"),
/* 0x05 */ Some("p5"),
/* 0x06 */ Some("p6"),
/* 0x07 */ Some("p7"),
/* 0x08 */ Some("c"),
/* 0x09 */ Some("o"),
/* 0x0A */ Some("s"),
/* 0x0B */ Some("z"),
/* 0x0C */ None,
/* 0x0D */ None,
/* 0x0E */ None,
/* 0x0F */ None,
/* 0x10 */ Some("ie0"),
/* 0x11 */ Some("ie1"),
/* 0x12 */ Some("ie2"),
/* 0x13 */ None,
/* 0x14 */ Some("is0"),
/* 0x15 */ Some("is1"),
/* 0x16 */ Some("is2"),
/* 0x17 */ None,
/* 0x18 */ Some("ea"),
/* 0x19 */ None,
/* 0x1A */ None,
/* 0x1B */ None,
/* 0x1C */ None,
/* 0x1D */ None,
/* 0x1E */ None,
/* 0x1F */ None,
][value]
}
/// The used Falcon memory spaces in SRAM.
///
/// Falcon follows the Harvard computer model by utilizing separate memory spaces
/// for code and data. The *IMEM (instruction memory)* utilizes primitive paging
/// and contains the code that is executed.
///
/// The *DMEM (data memory)* on the other hand stores local variables and the stack.
/// Unaligned access to it leads to data corruption.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum MemorySpace {
/// The Falcon I/O space.
///
/// Since IMEM cannot be accessed from any opcodes, we don't need to cover it here.
Io,
/// The DMEM segment.
DMem,
}
impl fmt::Display for MemorySpace {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let fmt = match self {
MemorySpace::Io => "I",
MemorySpace::DMem => "D",
};
write!(f, "{}", fmt)
}
}
/// A direct memory access to an address in a [`MemorySpace`].
///
/// Some instructions directly access an address in a specific memory space. An
/// address can be described in the following formats:
///
/// - Access through a single register holding the address: `[$reg]`
/// - Access through two registers for base address and scaled offset: `[$reg1 + $reg2 * scale]`
/// - Access through a register for base address and an immediate for offset: `[$reg + imm]`
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum MemoryAccess {
/// A form where the memory address is derived from a single register: `[$reg]`
Reg {
/// The memory space which should be accessed.
space: MemorySpace,
/// The register which holds the address to access.
base: Register,
},
/// A form where the memory address is derived from two registers: `[$reg1 + $reg2 * scale]`
RegReg {
/// The memory space which should be accessed.
space: MemorySpace,
/// The register which holds the memory base address.
base: Register,
/// The register that holds the offset to the base address.
offset: Register,
/// A constant scale for the offset value. Determined by the instruction
/// form.
scale: u8,
},
/// A form where the memory address is derived from a register and an immediate: `[$reg + imm]`
RegImm {
/// The memory space which should be accessed.
space: MemorySpace,
/// The register which holds the memory base address.
base: Register,
/// An offset of the base address stored in an immediate.
offset: u32,
},
}
impl fmt::Display for MemoryAccess {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
MemoryAccess::Reg { space, base } => write!(f, "{}[{}]", space, base),
MemoryAccess::RegReg {
space,
base,
offset,
scale,
} => match scale {
0 => write!(f, "{}[{}]", space, base),
1 => write!(f, "{}[{} + {}]", space, base, offset),
_ => write!(f, "{}[{} + {} * {}]", space, base, offset, scale),
},
MemoryAccess::RegImm {
space,
base,
offset,
} => match offset {
0 => write!(f, "{}[{}]", space, base),
_ => write!(f, "{}[{} + {:#02x}]", space, base, offset),
},
}
}
}
/// An operand of a Falcon assembly [`crate::Instruction`].
///
/// Operands usually denote CPU registers, immediates, and memory addressing for
/// the instruction to operate on. A `Vec` of instruction operands can be obtained
/// for every instruction individually through [`crate::Instruction::operands`]. It is
/// at the user's responsibility to correctly interpret and process the operands.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Operand {
/// A Falcon CPU register operand.
Register(Register),
/// An immediate operand that represents a specific bit of the `$csw` register.
Flag(u8),
/// A signed immediate.
Immediate(i32),
/// An unsigned immediate.
UnsignedImmediate(u32),
/// An immediate operand that denotes a bitfield starting from the lower bit index
/// and covering a specific amount of bits.
Bitfield(u32, u32),
/// A direct access to a memory space at a given address.
Memory(MemoryAccess),
}
impl fmt::Display for Operand {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Operand::Register(reg) => write!(f, "{}", reg),
Operand::Flag(flag) => write!(f, "${}", get_flag_name(*flag as usize).unwrap_or("unk")),
Operand::Immediate(val) => display_signed_hex(val, f),
Operand::UnsignedImmediate(val) => display_unsigned_hex(val, f),
Operand::Bitfield(i, n) => write!(f, "{}:{}", i, i + n),
Operand::Memory(mem) => write!(f, "{}", mem),
}
}
}