Source: CPU/cpu_devices.js

/**
 * Some utility devices used throughout the CPU. Each device is a subclass of
 * AbsractDevice, which is defined in src/representation/device.js.
 * */

import { WORD_SIZE } from "../constants.js";
import { IOBus } from "../representation/IOBus.js";
import * as gates from "../TriArithmetic/gates.js";
import { AbsractDevice } from "../representation/device.js";
import { Adder } from "./adder.js";
import { Tri } from "../representation/tri.js";

/**
 * Takes in two arrays of Tri objects and a carry in VALUE and computes the
 * result and carry out value.
 *
 * @property {Tri} cIn - the carry in VALUE
 * @property {Adder[]} TriAdders - the array of adders for each tri in a bus
 * @property {Tri} cOut - the carry out VALUE
 * @extends AbsractDevice
 */
export class WordAdder extends AbsractDevice {
  //A full adder device for 2 input IO busses with carry in and carry out
  //Create an array of tri adders
  triAdders = [];

  /**
   * Create a new WordAdder device with the specified input busses and carry
   * in locations.
   *
   * @param {IOBus} busIn1
   * @param {IOBus} busIn2
   * @param {Tri} cIn
   * @constructor
   */
  constructor(busIn1, busIn2, cIn) {
    //extend AbstractDevice functionality
    super();
    // Wire up the 2 input busses as device inputs and the carry in
    this.addInput(busIn1);
    this.addInput(busIn2);
    this.cIn = cIn;
    // Wire the internal tri adders with carry propogation
    for (let i = 0; i < WORD_SIZE; i++) {
      let carryProp = this.cIn;
      if (i != 0) {
        carryProp = this.triAdders[i - 1].cOut;
      }
      this.triAdders[i] = new Adder(
        this.inputs[0].data[i],
        this.inputs[1].data[i],
        carryProp,
        this.output.data[i],
        new Tri(),
      );
    }
    // Each cOut should be stored in the next adders cIn
    this.cOut = this.triAdders[WORD_SIZE - 1].cOut;
  }

  /**
   * Compute the result of the addition of the two input busses with carries
   */
  compute() {
    // Compute the output of each tri adder as wired in the constructor
    for (let i = 0; i < WORD_SIZE; i++) {
      this.triAdders[i].compute();
    }
  }
}

/**
 * Takes in two arrays of Tri objects and a carry in VALUE and computes the
 * result and carry out value of addition or substraction depending on the
 * setting of the control signal.
 *
 * @property {Tri} cIn - the carry in VALUE
 * @property {WordAdder} adder - the internal adder device
 * @property {Tri} control - the control signal for addition or subtraction
 * @property {Tri} carry - the carry out VALUE
 * @extends AbsractDevice
 */
export class AddSub extends AbsractDevice {
  /**
   * Create a new AddSub device with the specified input busses, carry in
   *
   * @param {Tri} control
   * @param {IOBus} wordIn1
   * @param {IOBus} wordIn2
   * @param {Tri} cIn
   * @constructor
   */
  constructor(control, wordIn1, wordIn2, cIn) {
    super();
    // Wire up the carry in, inputs, and internal bus adder
    this.cIn = cIn;
    this.inputs = [wordIn1, wordIn2];
    this.adder = new WordAdder(this.inputs[0], this.inputs[1], this.cIn);
    // Has one control signal to select operation
    this.control = control;
    this.adder.val1 = this.inputs[0];
    // Create a bus for the second input to the adder which will be
    // written by the value of a multiply gate
    this.adder.val2 = new IOBus();
    this.adder.cIn = this.cIn;
    //Overwrite the output with the adder's output
    this.output = this.adder.output;
    this.carry = this.adder.cOut;
  }

  /**
   * Compute the result of the addition or subtraction of the two input busses
   * with carries
   */
  compute() {
    //Extend the control to a WORD_SIZE bus
    let temp = new IOBus();
    gates.wordMap((x) => this.control.state, temp.data);
    //Supply the second input selected by the control signal to the adder
    this.adder.val2.writeBus(
      gates.wordMap2(gates.MUL, temp.data, this.inputs[1].data),
    );
    this.adder.compute();
  }
}

//Muxes

/**
 * A Three to one mux device. Takes in three inputs and a control signal and
 * selects the output based on the control signal.
 *
 * @property {*} out1 - the first input
 * @property {*} out2 - the second input
 * @property {*} out3 - the third input
 * @property {*} out - the output
 * @property {Tri} sig - the control signal
 */
export class ThreeOne {
  out1;
  out2;
  out3;
  out;
  sig;

  /**
   * Set the output signal of the mux based on the state of the control
   * signal.
   */
  select() {
    if (this.sig.state == -1) {
      this.out = this.out1;
    } else if (this.sig.state == 0) {
      this.out = this.out2;
    } else {
      this.out = this.out3;
    }
  }
}

/**
 * A Nine to one mux device. Takes in nine inputs and two control signals and
 * outputs the selected input based on the control signals.
 *
 * @property {*} outs - the nine inputs
 * @property {*} out - the output
 * @property {Tri[]} sigs - the two control signals
 * @property {Map} keyMap - a map of the control signal states to the output
 */
export class NineTwo {
  keyMap = new Map();
  outs;
  sigs;
  out;
  constructor() {
    this.keyMap.set("-1,-1", 0);
    this.keyMap.set("-1,0", 1);
    this.keyMap.set("-1,1", 2);
    this.keyMap.set("0,-1", 3);
    this.keyMap.set("0,0", 4);
    this.keyMap.set("0,1", 5);
    this.keyMap.set("1,-1", 6);
    this.keyMap.set("1,0", 7);
    this.keyMap.set("1,1", 8);
  }

  /**
   * Set the output signal of the mux based on the state of the control
   * signals.
   */
  select() {
    let sigCode =
      this.sigs[0].state.toString() + "," + this.sigs[1].state.toString();
    this.out = this.outs[this.keyMap.get(sigCode)];
  }
}