1
0

Refactor instruction layout to allow for more type codes

This commit is contained in:
Jeff 2025-02-07 20:52:08 -05:00
parent 3d48558b94
commit ac11ad5674
3 changed files with 61 additions and 50 deletions

View File

@ -1,17 +1,17 @@
//! The Dust instruction set. //! The Dust instruction set.
//! //!
//! Each instruction is 64 bits and uses up to seven distinct fields. //! Each instruction is 64 bits and uses up to nine distinct fields.
//! //!
//! # Layout //! # Layout
//! //!
//! Bits | Description //! Bits | Description
//! ----- | ----------- //! ----- | -----------
//! 0-6 | Operation //! 0-4 | Operation
//! 7 | Flag indicating if the B field is a constant //! 5-8 | B field type
//! 8 | Flag indicating if the C field is a constant //! 9 | Flag indicating if the B field is a constant
//! 9 | D field (boolean) //! 10-13 | C field type
//! 10-12 | B field type //! 14 | Flag indicating if the C field is a constant
//! 13-15 | C field type //! 15 | D field (boolean)
//! 16-31 | A field (unsigned 16-bit integer) //! 16-31 | A field (unsigned 16-bit integer)
//! 32-47 | B field (unsigned 16-bit integer) //! 32-47 | B field (unsigned 16-bit integer)
//! 48-63 | C field (unsigned 16-bit integer) //! 48-63 | C field (unsigned 16-bit integer)
@ -40,20 +40,20 @@
//! assert_eq!(move_1, move_2); //! assert_eq!(move_1, move_2);
//! ``` //! ```
//! //!
//! Use the [`Argument`][] type when creating instructions. In addition to being easy to read and //! Use the [`Operand`][] type when creating instructions. In addition to being easy to read and
//! write, this ensures that the instruction has the correct flags to represent the arguments. //! write, this ensures that the instruction has the correct flags to represent the operands.
//! //!
//! ``` //! ```
//! # use dust_lang::instruction::{Instruction, Add, Argument}; //! # use dust_lang::instruction::{Instruction, Add, Operand};
//! let add_1 = Instruction::add( //! let add_1 = Instruction::add(
//! 0, //! 0,
//! Argument::Register(1), //! Operand::Register(1),
//! Argument::Constant(2) //! Operand::Constant(2)
//! ); //! );
//! let add_2 = Instruction::from(Add { //! let add_2 = Instruction::from(Add {
//! destination: 0, //! destination: 0,
//! left: Argument::Register(1), //! left: Operand::Register(1),
//! right: Argument::Constant(2), //! right: Operand::Constant(2),
//! }); //! });
//! //!
//! assert_eq!(add_1, add_2); //! assert_eq!(add_1, add_2);
@ -64,28 +64,27 @@
//! To read an instruction, check its operation with [`Instruction::operation`], then convert the //! To read an instruction, check its operation with [`Instruction::operation`], then convert the
//! instruction to the struct that corresponds to that operation. Like the example above, this //! instruction to the struct that corresponds to that operation. Like the example above, this
//! removes the burden of dealing with the options directly and automatically casts the A, B, C and //! removes the burden of dealing with the options directly and automatically casts the A, B, C and
//! D fields as `u16`, `bool` or `Argument` values. //! D fields as `u16`, `bool` or `Operand` values.
//! //!
//! ``` //! ```
//! # use dust_lang::instruction::{Instruction, Add, Argument, Operation}; //! # use dust_lang::instruction::{Instruction, Add, Operand, Operation};
//! # let mystery_instruction = Instruction::add( //! # let mystery_instruction = Instruction::add(
//! # 1, //! # 1,
//! # Argument::Register(1), //! # Operand::Register(1),
//! # Argument::Constant(2) //! # Operand::Constant(2)
//! # ); //! # );
//! // Let's read an instruction and see if it performs addition-assignment, //! // Let's read an instruction and see if it performs addition-assignment,
//! // like in one of the following examples: //! // like in one of the following examples:
//! // - `a += 2` //! // - `a += 2`
//! // - `a = a + 2` //! // - `a = a + 2`
//! // - `a = 2 + a`
//! //!
//! let operation = mystery_instruction.operation(); //! let operation = mystery_instruction.operation();
//! let is_add_assign = match operation { //! let is_add_assign = match operation {
//! Operation::Add => { //! Operation::Add => {
//! let Add { destination, left, right } = Add::from(&mystery_instruction); //! let Add { destination, left, right } = Add::from(&mystery_instruction);
//! //!
//! left == Argument::Register(destination) //! left == Operand::Register(destination)
//! || right == Argument::Register(destination); //! || right == Operand::Register(destination)
//! //!
//! } //! }
//! _ => false, //! _ => false,
@ -165,12 +164,12 @@ pub struct InstructionFields {
impl InstructionFields { impl InstructionFields {
pub fn build(self) -> Instruction { pub fn build(self) -> Instruction {
let bits = ((self.operation.0 as u64) << 57) let bits = ((self.operation.0 as u64) << 59)
| ((self.b_is_constant as u64) << 56) | ((self.b_type.0 as u64) << 54)
| ((self.c_is_constant as u64) << 55) | ((self.b_is_constant as u64) << 53)
| ((self.d_field as u64) << 54) | ((self.c_type.0 as u64) << 49)
| ((self.b_type.0 as u64) << 51) | ((self.c_is_constant as u64) << 48)
| ((self.c_type.0 as u64) << 48) | ((self.d_field as u64) << 47)
| ((self.a_field as u64) << 32) | ((self.a_field as u64) << 32)
| ((self.b_field as u64) << 16) | ((self.b_field as u64) << 16)
| (self.c_field as u64); | (self.c_field as u64);
@ -219,45 +218,57 @@ pub struct Instruction(u64);
impl Instruction { impl Instruction {
pub fn operation(&self) -> Operation { pub fn operation(&self) -> Operation {
let first_7_bits = (self.0 >> 57) as u8; let first_5_bits = (self.0 >> 59) as u8;
Operation(first_7_bits) Operation(first_5_bits)
}
pub fn b_is_constant(&self) -> bool {
(self.0 >> 56) & 1 != 0
}
pub fn c_is_constant(&self) -> bool {
(self.0 >> 55) & 1 != 0
}
pub fn d_field(&self) -> bool {
(self.0 >> 54) & 1 != 0
} }
pub fn b_type(&self) -> TypeCode { pub fn b_type(&self) -> TypeCode {
let byte = ((self.0 >> 51) & 0b111) as u8; let bits_5_to_8 = (self.0 >> 54) & 0b1111;
TypeCode(byte) TypeCode(bits_5_to_8 as u8)
}
pub fn b_is_constant(&self) -> bool {
let bit_9 = (self.0 >> 53) & 1;
bit_9 != 0
} }
pub fn c_type(&self) -> TypeCode { pub fn c_type(&self) -> TypeCode {
let byte = ((self.0 >> 48) & 0b111) as u8; let bits_10_to_13 = (self.0 >> 49) & 0b1111;
TypeCode(byte) TypeCode(bits_10_to_13 as u8)
}
pub fn c_is_constant(&self) -> bool {
let bit_14 = (self.0 >> 48) & 1;
bit_14 != 0
}
pub fn d_field(&self) -> bool {
let bit_15 = (self.0 >> 47) & 1;
bit_15 != 0
} }
pub fn a_field(&self) -> u16 { pub fn a_field(&self) -> u16 {
((self.0 >> 32) & 0xFFFF) as u16 let bits_16_to_31 = (self.0 >> 32) & 0xFFFF;
bits_16_to_31 as u16
} }
pub fn b_field(&self) -> u16 { pub fn b_field(&self) -> u16 {
((self.0 >> 16) & 0xFFFF) as u16 let bits_32_to_47 = (self.0 >> 16) & 0xFFFF;
bits_32_to_47 as u16
} }
pub fn c_field(&self) -> u16 { pub fn c_field(&self) -> u16 {
(self.0 & 0xFFFF) as u16 let bits_48_to_63 = self.0 & 0xFFFF;
bits_48_to_63 as u16
} }
pub fn set_a_field(&mut self, bits: u16) { pub fn set_a_field(&mut self, bits: u16) {
@ -707,7 +718,7 @@ mod tests {
Operand::Register(2, TypeCode::CHARACTER), Operand::Register(2, TypeCode::CHARACTER),
); );
assert_eq!(instruction.operation(), Operation::ADD); assert_eq!(Operation::ADD, instruction.operation());
} }
#[test] #[test]

View File

@ -1,7 +1,6 @@
use std::fmt::{self, Display, Formatter}; use std::fmt::{self, Display, Formatter};
use crate::{ use crate::{
Type,
instruction::TypeCode, instruction::TypeCode,
vm::{Pointer, Thread}, vm::{Pointer, Thread},
}; };

View File

@ -89,6 +89,7 @@ pub enum Register<T> {
} }
impl<T> Register<T> { impl<T> Register<T> {
#[allow(unused_assignments)]
pub fn close(mut self) { pub fn close(mut self) {
if let Self::Value(value) = self { if let Self::Value(value) = self {
self = Self::Closed(value); self = Self::Closed(value);