Implement LDA Operation
authorJacob Casper <dev@jacobcasper.com>
Tue, 16 Jan 2024 02:21:29 +0000 (20:21 -0600)
committerJacob Casper <dev@jacobcasper.com>
Tue, 16 Jan 2024 02:47:29 +0000 (20:47 -0600)
The LDA operation reads the value from a memory location given by the
instruction, and loads it to rA. Implementing this required fleshing out
the address calculations, fixing bugs in the `Word::as_field_spec()`
method, and fixing up visibilities of various structs. Pulling the
FieldSpec out of a Word being used as an Instruction was also necessary.

Work to have addressing respect the index offset is still necessary, but
we can update state with registers now!

This desperately needs convenience methods for testing so I can take
human readable opcodes and convert them to Words more easily.

src/instruction.rs [new file with mode: 0644]
src/machine.rs
src/main.rs

diff --git a/src/instruction.rs b/src/instruction.rs
new file mode 100644 (file)
index 0000000..dc3c61e
--- /dev/null
@@ -0,0 +1,32 @@
+use crate::*;
+
+#[derive(Debug)]
+pub struct Operator {
+    instruction: Word,
+    operator: fn(Word, &mut Machine) -> (),
+}
+
+impl Operator {
+    pub fn call(&self, machine: &mut Machine) -> () {
+        (self.operator)(self.instruction, machine)
+    }
+}
+
+fn lda(w: Word, m: &mut Machine) {
+    m.registers.a = m.memory[w.address() as usize].as_field_spec(w.field_spec());
+}
+
+fn noop(w: Word, m: &mut Machine) {}
+
+pub fn op_table(instruction: Word) -> Operator {
+    match instruction.bytes[4].value() {
+        8 => Operator {
+            instruction,
+            operator: lda,
+        },
+        _ => Operator {
+            instruction,
+            operator: noop,
+        },
+    }
+}
index 33bbcef..fdecd33 100644 (file)
@@ -6,7 +6,7 @@ pub struct MixBit {
 impl MixBit {
     // Clamps a u8 to a u6
     // 0b0011_1111 = 64
-    fn value(&self) -> u8 {
+    pub fn value(&self) -> u8 {
         return self.v & 0b0011_1111;
     }
 
@@ -31,29 +31,48 @@ pub struct Word {
     pub bytes: [MixBit; 5],
 }
 
+// Multiple return type for dealing with 8L+R fields from Words
+#[derive(Debug)]
+pub struct FieldSpec {
+    l: usize,
+    r: usize,
+}
+
 impl Word {
     fn new(sign: bool, bytes: [MixBit; 5]) -> Word {
         Word { sign, bytes }
     }
+
+    // The word is being used as an instruction.
+    // By definition, the 4th bit of the word is the field specification as 8L + R.
+    pub fn field_spec(&self) -> FieldSpec {
+        FieldSpec {
+            // divide by 8
+            l: (self.bytes[3].value() >> 3) as usize,
+            r: (self.bytes[3].value() % 8) as usize,
+        }
+    }
+
     // The allowable fields are those that are adjacent in a computer word, and they are
     // represented by (L:R) where L is the number of the left hand part and R is the number of the
     // right-hand part of the field.
-    pub fn field_specification(&self, l: usize, r: usize) -> Word {
-        let mut sign = false;
-        let mut l_clamp = l;
-        if l == 0 {
+    pub fn as_field_spec(&self, fs: FieldSpec) -> Word {
+        let mut sign = true;
+        let mut l_clamp = fs.l;
+        if fs.l == 0 {
             sign = self.sign;
         } else {
-            l_clamp = l - 1;
+            l_clamp = l_clamp - 1;
         }
-        let r_clamp = r % 5;
+        let r_clamp = (fs.r - 1) % 5;
         let mut bytes = [MixBit::default(); 5];
-        for n in l_clamp..r_clamp {
+        for n in l_clamp..=r_clamp {
             bytes[n] = self.bytes[n];
         }
         Word { sign, bytes }
     }
 
+    // TODO implement / respect index word (self.bytes[2] should be index)
     pub fn address(&self) -> i16 {
         let magnitude = ((self.bytes[0].value() as i16) << 6) | self.bytes[1].value() as i16;
         match self.sign {
@@ -66,20 +85,15 @@ impl Word {
 // There are nine registers in MIX.
 // We shall use a small letter "r", prefixed to the neame, to identify a MIX register.
 #[derive(Debug, Default)]
-struct Registers {
+pub struct Registers {
     // Accumulator
-    rA: Word,
+    pub a: Word,
     // Extension
-    rX: Word,
+    pub x: Word,
     // Index registers: Should only hold 2 bytes together with a sign.
-    rI1: Word,
-    rI2: Word,
-    rI3: Word,
-    rI4: Word,
-    rI5: Word,
-    rI6: Word,
+    pub i: [Word; 6],
     // Jump: holds two bytes; it behaves as if its sign is always +
-    rJ: Word,
+    pub j: Word,
 }
 
 // having three values: LESS, EQUAL, or GREATER
@@ -93,20 +107,20 @@ enum ComparisonIndicator {
 
 #[derive(Debug)]
 pub struct Machine {
-    registers: Registers,
+    pub registers: Registers,
     // Besides its registers, MIX contains:
     // an overflow toggle
     overflow: bool,
     // a comparison indicator
     comp: ComparisonIndicator,
     // memory: 4000 words of storage, each word with five bytes and a sign)
-    memory: [Word; 4000],
+    pub memory: [Word; 4000],
     // and input-output devices
     // TODO?
 }
 
 impl Machine {
-    fn default() -> Machine {
+    pub fn default() -> Machine {
         Machine {
             registers: Registers::default(),
             overflow: false,
index d63ec15..345f81e 100644 (file)
@@ -1,11 +1,33 @@
+mod instruction;
 mod machine;
+use crate::instruction::*;
 use crate::machine::*;
 
 fn main() {
-    let fs = (Word {
+    let mut m = Machine::default();
+    m.memory[0] = Word {
+        sign: false,
+        bytes: [
+            MixBit { v: 2 },
+            MixBit { v: 16 },
+            MixBit { v: 3 },
+            MixBit { v: 5 },
+            MixBit { v: 4 },
+        ],
+    };
+    let i = Word {
         sign: true,
-        bytes: [MixBit { v: 63 }; 5],
-    })
-    .field_specification(1, 2);
-    println!("{:?}", fs.address());
+        bytes: [
+            MixBit { v: 0 },
+            MixBit { v: 0 },
+            MixBit { v: 0 },
+            MixBit { v: 29 },
+            MixBit { v: 8 },
+        ],
+    };
+    println!("{:?}", i.field_spec());
+    let o = op_table(i);
+    o.call(&mut m);
+    println!("{:?}", m.registers.a);
+    println!("{:?}", m.memory[0]);
 }