virmin/insn.rs
1use std::cmp;
2use num::BigUint;
3use crate::domain::Countable;
4use crate::domain::{Bits,Bytes};
5use crate::machine::Width;
6use crate::machine::MicroCode;
7
8// ================================================================
9// Format
10// ================================================================
11
12/// Defines a format for a class of related instructions. For
13/// example, some instructions might not take any operands. Others
14/// might take, say, three register operands, etc. The following
15/// illustrates:
16///
17/// ```text
18/// +-+-+-+-+-+-+-+-+
19/// |7 5| 2|1 0|
20/// +-+-+-+-+-+-+-+-+
21/// | #2 | #1 |Op |
22/// +-+-+-+-+-+-+-+-+
23/// ```
24/// Here, we see one possible layout for an instruction class which
25/// includes two three-bit operands, and a two bit opcode. This means
26/// we can have at most four instructions in this class, and each
27/// operand can take on eight distinct values.
28#[derive(PartialEq)]
29pub struct Format {
30 /// Determines the overall width (in bytes) of an instruction in
31 /// this class. Generally speaking, virtual machines normally
32 /// have all instructions of the same width (e.g. 32bits) and, in
33 /// such case, `width` will be the same for all formats. However,
34 /// it is possible that a virtual machine has different sized
35 /// instructions (e.g. 16bit instructions, and 32bit "double"
36 /// instructions).
37 width: Bytes,
38 /// Human-readable label for this format
39 label: String,
40 /// Determine the number of distinct instructions in this class.
41 opcode : Bits,
42 /// Determine the number and size of operands for all instructions
43 /// in this class.
44 operands: Vec<Bits>
45}
46
47impl Format {
48 pub fn new(width:Bytes, label: &str, opcode: Bits, operands: &[Bits]) -> Format {
49 let r = Format{width,label:label.to_string(),opcode,operands:operands.to_vec()};
50 // Sanity check there is enough space
51 assert!(width.count() >= r.count());
52 //
53 r
54 }
55}
56
57impl Countable for Format {
58 fn count(&self) -> BigUint {
59 let mut count = BigUint::from(self.opcode.count());
60 //
61 for op in &self.operands {
62 count = count * op.count();
63 }
64 //
65 count
66 }
67}
68
69// =====================================================
70// Abstract Microcode
71// =====================================================
72
73/// Represents an abstract microcode instruction. This is
74/// (effectively) a template for constructing a concrete microcode
75/// instruction from a concrete instantiation of an instruction
76/// (i.e. where all operands have known values).
77pub enum AbstractMicroCode {
78 /// X := Y (w bits)
79 Copy(Operand,Operand,Width),
80 /// pc := I
81 Goto(Operand),
82 /// pc := pc + I
83 Jump(Operand),
84 /// X := i
85 Load(Operand,u64,Width)
86}
87
88impl AbstractMicroCode {
89 /// Determine how many operands this microcode requires. This is
90 /// necessary to sanity check that, for a given instruction
91 /// format, this microcode instruction makes sense.
92 pub fn arity(&self) -> usize {
93 match &self {
94 AbstractMicroCode::Copy(x,y,w) => {
95 cmp::max(x.arity(),y.arity())
96 }
97 AbstractMicroCode::Load(x,i,w) => {
98 x.arity()
99 }
100 _ => {
101 todo!("implement more instructions")
102 }
103 }
104 }
105 /// Given a set of concrete operands, reduce this abstract
106 /// microcode instruction into a concrete microcode instruction.
107 pub fn to_microcode(&self, operands: &[usize]) -> MicroCode {
108 match &self {
109 AbstractMicroCode::Copy(x,y,w) => {
110 let l = x.as_usize(operands);
111 let r = y.as_usize(operands);
112 MicroCode::Copy(l,r,*w)
113 }
114 AbstractMicroCode::Load(x,i,w) => {
115 let l = x.as_usize(operands);
116 MicroCode::Load(l,*i,*w)
117 }
118 _ => {
119 todo!("implement more instructions")
120 }
121 }
122 }
123}
124
125/// Represents an arbitrary expression over one or more instruction
126/// operands. For each instruction instantiation, an operand
127/// expression can be evaluated to a constant.
128pub enum Operand {
129 /// A constant value which can be used in various ways. For
130 /// example, it can be used to identify a fixed location in the
131 /// underlying machine; or, it could be used as part of a more
132 /// complex operand expression.
133 Const(usize),
134 /// An operand value read from the instantiated instruction.
135 Var(usize)
136}
137
138impl Operand {
139 /// Determine the arity of an operand expression. That is, how
140 /// many operands are needed for it to evaluate.
141 pub fn arity(&self) -> usize {
142 match &self {
143 Operand::Const(i) => {
144 0
145 }
146 Operand::Var(v) => {
147 v + 1
148 }
149 }
150 }
151
152 pub fn as_usize(&self, operands: &[usize]) -> usize {
153 match &self {
154 Operand::Const(i) => {
155 *i
156 }
157 Operand::Var(v) => {
158 operands[*v]
159 }
160 }
161 }
162}
163
164// =====================================================
165// Instruction
166// =====================================================
167
168pub struct Instruction<'a> {
169 /// Mnemonic for referring to the instruction. Every instruction
170 /// should have a unique mnemonic.
171 mnemonic: &'a str,
172 /// Format associated with this instruction.
173 format: &'a Format,
174 /// Machine semantics associated with instruction.
175 semantic: &'a [AbstractMicroCode]
176}
177
178impl<'a> Instruction<'a> {
179 pub fn new(mnemonic: &'a str, format: &'a Format, semantic: &'a [AbstractMicroCode]) -> Self {
180 for code in semantic {
181 assert!(code.arity() <= format.operands.len());
182 }
183 Instruction{mnemonic,format,semantic}
184 }
185
186 pub fn to_microcode(&self, operands: &[usize]) -> Vec<MicroCode> {
187 let mut microcode = Vec::new();
188 for c in self.semantic {
189 microcode.push(c.to_microcode(operands));
190 }
191 microcode
192 }
193}
194
195// =====================================================
196// Instruction Set
197// =====================================================
198
199/// A collection of instructions.
200pub struct InstructionSet<'a> {
201 insns : &'a [Instruction<'a>]
202}
203
204impl<'a> InstructionSet<'a> {
205 pub fn new(insns : &'a [Instruction<'a>]) -> Self {
206 InstructionSet{insns}
207 }
208}
209