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
//! # asm, the module that provides structures that represent assembly instructions
use alloc::{
    collections::BTreeMap,
    string::{String, ToString},
};

use core::fmt;
use spin::Mutex;

/// This is the stack size that is used if the assembly file
/// does not specify one.
pub const DEFAULT_STACK_SIZE: usize = 256;

/// This specifies the number of predefined registers.
/// This is VERY important to get right, if this is too small,
/// user defined registers will overwrite the Accumulator,
/// StackPointer, and other predefined registers.
pub const PREDEFINED_REGISTERS: usize = 2;

lazy_static! {
    /// This tracks the current address where the next register will be allocated
    pub(crate) static ref REGISTER_POINTER: Mutex<usize> = Mutex::new(PREDEFINED_REGISTERS);

    /// This tracks the named registers
    pub(crate) static ref NAMED_REGISTERS: Mutex<BTreeMap<String, Register>> = Mutex::new(BTreeMap::new());
}

/// The Register enum represents a register in an assembly program (obviously)
#[derive(Clone, Debug, PartialEq, PartialOrd)]
pub enum Register {
    /// The register that points to where the next item on the stack
    /// will be stored to
    StackPointer,

    /// The register that stores temporary values in several instructions
    Accumulator,

    /// A register defined by the user. Each register's address and size is
    /// known statically.
    Named {
        /// the name of the user defined register
        name: String,
        /// the number of cells that the register occupies
        size: usize,
        /// the address of the register
        addr: usize,
    },
}

impl Register {
    /// Get a user defined register by its name.
    /// The register MUST be previously defined.
    pub fn named(name: impl fmt::Display) -> Option<Self> {
        let registers = NAMED_REGISTERS.lock();
        if let Some(r) = registers.get(&name.to_string()) {
            Some(r.clone())
        } else {
            None
        }
    }

    /// Define a Register with a given name and size. This will
    /// create a Register in the NAMED_REGISTERS map with the
    /// value of REGISTER_POINTER as the Register's address.
    pub fn define(name: impl fmt::Display, size: usize) -> Self {
        // The pointer to where this register will be stored
        let mut rptr = REGISTER_POINTER.lock();
        // The map of defined registers
        let mut registers = NAMED_REGISTERS.lock();

        // This register
        let result = Self::Named {
            name: name.to_string(),
            size,
            addr: *rptr,
        };

        // Increment the register pointer so that the next
        // defined register will not overwrite this register
        *rptr += size;

        // Insert this register into the map
        registers.insert(name.to_string(), result.clone());

        // Return this register
        result
    }

    /// Get the address where this register is stored. This is
    /// used in many instructions, most notably the `refer` instruction.
    pub fn get_addr(&self) -> usize {
        match self {
            Self::Named { addr, .. } => *addr,
            Self::Accumulator => 0,
            Self::StackPointer => 1
        }
    }

    /// Get the number of cells this register occupies. Both the
    /// StackPointer and the Accumulator are one cell each
    pub fn get_size(&self) -> usize {
        match self {
            Self::Named { size, .. } => *size,
            _ => 1,
        }
    }
}

/// The Literal enum represents a literal in an assembly program (duh).
/// A literal can either be a double precision float, or a character literal
#[derive(Clone, Debug, PartialEq, PartialOrd)]
pub enum Literal {
    /// A character literal
    Character(char),

    /// A double precision float literal
    Number(f64),
}

impl Literal {
    /// Create a character literal from a character
    pub fn ch(ch: char) -> Self {
        Self::Character(ch)
    }

    /// Create a number literal from a double precision float
    pub fn num(n: f64) -> Self {
        Self::Number(n)
    }

    /// Get the value of this literal as a double precision float
    pub fn get(&self) -> f64 {
        match self {
            // a character cannot directly cast as a float
            Self::Character(ch) => *ch as i32 as f64,
            Self::Number(n) => *n,
        }
    }
}

/// This enum represents a single instruction that can be assembled.
/// The majority of the work to implement Target for a target
/// programming language is defining how to convert an instruction
/// to a properly formatted string for the target.
#[derive(Clone, Debug, PartialEq, PartialOrd)]
pub enum Instruct {
    /// The `refer` instruction takes a register and pushes a
    /// pointer to that register onto the stack. The motivation
    /// for only allowing registers to be referenced is very
    /// simple to explain. Values on the stack are meant to be
    /// temporary. Literals pretty much exclusively exist on the
    /// stack. Allowing references to literals promotes bad practices.
    Refer(Register),

    /// The `deref_ld` instruction takes no arguments. This instruction
    /// pops an address off the stack and pushes the value stored at the
    /// cell the address points to.
    ///
    /// Essentially, this derefences the pointer as a double precision float pointer.
    DerefLoad,

    /// The `deref_st` instruction takes no arguments. This instruction
    /// pops an address cell and a value cell off the stack. The value cell is stored
    /// as a double precision float at the location the pointer points to.
    ///
    /// This is equivalent to `*(double*)address = value`
    DerefStore,

    /// The `ld` instruction takes a register as an argument. This instruction
    /// loads the value stored in the register and pushes it onto the stack.
    Load(Register),

    /// The `st` instruction takes a register as an argument. This instruction
    /// pops a value off of the stack and stores it in the register.
    ///
    /// This is equivalent to `register = value`
    Store(Register),

    /// The `push` instruction takes either a literal as an argument. This literal
    /// can either be a character or a double precision float. This instruction pushes
    /// the literal onto the stack.
    Push(Literal),

    /// The `pop` instruction pops a value off of the stack and stores it in the ACC register.
    ///
    /// This is equivalent to `ACC = value`
    Pop,

    /// The `alloc` instruction takes a register as an argument, and pops a `size` value
    /// off of the stack. Then, `alloc` stores a pointer `size` number of consecutive free cells.
    ///
    /// This is equivalent to `register = (double*)malloc(size)`
    Alloc(Register),

    /// The `free` instruction takes a register as an argument, and pops a `size` value
    /// off of the stack. Then, `free` frees `size` number of values at the location the
    /// register points to.
    Free(Register),

    /// The `dup` instruction takes no argument, and simply duplicates the top item on
    /// the stack.
    Duplicate,

    /// The `add` instruction pops two cells off the stack and pushes their sum
    Add,

    /// The `sub` instruction pops two cells off the stack and pushes the first minus the second
    Subtract,

    /// The `mul` instruction pops two cells off the stack and pushes their product
    Multiply,

    /// The `div` instruction pops two cells off the stack and pushes the first divided by the second
    Divide,

    /// The `outc` instruction pops a cell off the stack and prints `cell % 256` as a character to STDOUT
    OutputChar,

    /// The `outn` instruction pops a cell off the stack and prints the cell as a number to STDOUT
    OutputNumber,

    /// The `inc` instruction pushes a character from STDIN onto the stack
    InputChar,

    /// The `inn` instruction pushes a double precision float from STDIN onto the stack
    InputNumber,

    /// The `cmp` instruction compares two cells on the stack.
    /// If the first popped value is less than the second, -1 is pushed.
    /// If the first popped value is equal to the second, 0 is pushed.
    /// If the first popped value is greater than to the second, 1 is pushed.
    Compare,

    /// The `loop` instruction the start of a loop. At the start of each iteration, a test value is popped from the stack. While the value is not zero, the loop continues. Else, the loop jumps to the matching `endloop`
    WhileNotZero,

    /// The `endloop` instruction marks the end of a loop
    EndWhile,
}