Source: machine/bullGamma.js

const assert = require('../tools/assert');

InstructionsParser = require("../assembly/hexParser").InstructionsParser;
Memory = require("./innerComponents/memory").Memory;
Group = require("./innerComponents/group").Group;
Octad = require("./innerComponents/octad").Octad;
CmpMemory = require("./innerComponents/cmpMemory").CmpMemory;
Series3 = require("./innerComponents/series3").Series3;
Serie = require("./innerComponents/serie").Serie;
MagneticDrum = require("./magneticDrum/magneticDrum").MagneticDrum;

const MEMORY_MODE = require("./constants").MEMORY_MODE;

const NB_BANAL_MEMORIES = require("./constants").NB_BANAL_MEMORIES;
const NB_GROUPS = require("./constants").NB_GROUPS;
const NB_SERIES = require("./constants").NB_SERIES;
const NB_GENERAL_SERIES = require("./constants").NB_GENERAL_SERIES;
const NB_OCTADS_PER_GROUP = require("./constants").NB_OCTADS_PER_GROUP;
const NB_COMMUTED_OCTADS = require("./constants").NB_COMMUTED_OCTADS;
const NB_MEMORIES_PER_OCTAD = require("./constants").NB_MEMORIES_PER_OCTAD;
NB_MEMORIES_PER_HALF_OCTAD = require('./constants').NB_MEMORIES_PER_HALF_OCTAD;

/**
 * Central class meant to represent the whole machine
 */
class BullGamma {

  /**
   * Constructs a new instance of BullGamma
   */
  constructor() {
    // Memories
    this._generalMemories = new Array(NB_BANAL_MEMORIES);
    for (let i = 1; i < NB_BANAL_MEMORIES; ++i) {
      this._generalMemories[i] = new Memory(i + 1, this);
    }
    // M0 == M1
    this._generalMemories[0] = this._generalMemories[1];

    this.series3 = new Series3(NB_GENERAL_SERIES, this)
    this.ioGroup = new Group(NB_GENERAL_SERIES, this)

    // Series and groups
    this.series = new Array(NB_SERIES);
    this.groups = new Array(NB_GROUPS);
    for (let i = 0; i < NB_GENERAL_SERIES; ++i) {
      this.groups[i] = new Group(i, this);
      this.series[i] = new Serie(i, this, this.groups[i]);
    }
    this.series[NB_GENERAL_SERIES] = this.series3;
    this.groups[NB_GENERAL_SERIES] = this.ioGroup;

    this.currentOctad = this.groups[0].octads[0];

    // Other
    this.magneticDrum = new MagneticDrum(this);
    this.connectedMachines = [];
    this._memoryMode = MEMORY_MODE.DECIMAL;
    this.ms1 = 0;
    this.md = 0;
    this.mc = new CmpMemory();
    this.nl = 0; // line number
    this.ns = 3; // series number
    this.rnl1 = 0;
    this.rnl2 = 0;
    this.parser = new InstructionsParser(this);
  }

  /**
   * Given an ID, return the corresponding serie
   * @param id the serie to return, should be between 0 and 3 included
   */
  getSerie(id) {
    assert(id >= 0 && id < NB_SERIES, "id should not be negative or superior to " + NB_SERIES - 1);
    return this.series[id];
  }

  /**
   * Given an ID, return the corresponding group
   * @param id the group to return, should be between 0 and 3 included
   */
  getGroup(id) {
    assert(id >= 0 && id < NB_GROUPS, "id should not be negative or superior to " + NB_GROUPS - 1);
    return this.groups[id];
  }

  /**
   * Given an ID, return the corresponding octad
   * @param id the octad to return, should be between 0 and 7 included
   */
  getOctad(id) {
    assert(id >= 0, "octad id should be positive");
    assert(id < NB_COMMUTED_OCTADS, "octad id should be inferior to " + NB_COMMUTED_OCTADS);
    return this.groups[Math.floor(id/NB_OCTADS_PER_GROUP)].octads[id % NB_OCTADS_PER_GROUP];
  }

  /**
   * Changes the current octad the Bull Gamma is working with
   * @param id id of the desired octad
   */
  setCommutedOctad(id) {
    this.currentOctad = this.getOctad(id);
  }

  /**
   * @param id the memory to be returned, if superior to 7, then the memory is selected from the octad
   * @param octadId if given, the memory will be selected from this octad, else from the current octad
   * @returns {*} the memory with the desired id
   */
  getMemory(id, octadId) {
    assert(id >= 0, "memory id should not be negative");
    assert(
      id < NB_BANAL_MEMORIES + NB_MEMORIES_PER_OCTAD,
      "memory id should be inferior to " + NB_BANAL_MEMORIES + NB_MEMORIES_PER_OCTAD
    );

    if (id < NB_BANAL_MEMORIES) {
      return this._generalMemories[id];
    } else {
      if (octadId !== undefined && octadId !== null) {
        return this.getOctad(octadId).getMemory([id - NB_BANAL_MEMORIES]);
      } else {
        return this.currentOctad.getMemory([id - NB_BANAL_MEMORIES]);
      }
    }
  }

  /**
   * @returns {*} the current memory mode for the machine, either MEMORY_MODE.BINARY or MEMORY_MODE.DECIMAL
   */
  getMemoryMode() {
    return this._memoryMode;
  }

  /**
   * set the value for the machine's memory mode
   * @param newMode the new value, either MEMORY_MODE.BINARY or MEMORY_MODE.DECIMAL
   */
  setMemoryMode(newMode) {
    assert.equal(newMode === MEMORY_MODE.BINARY || newMode === MEMORY_MODE.DECIMAL, true, "invalid memory mode");
    this._memoryMode = newMode;
  }

  /**
   * Compute the next line to be executed if no jump
   * @returns next line
   */
  nextLine() {
    return (this.nl + 1) % (this.getSerie(this.ns).nbInst);
  }

  /**
   * Executes the coming instruction if the current Series
   */
  executeNextInstruction() {
    let old_cp = this.nl;
    this.nl = this.nextLine();

    // execute instruction
    this.getSerie(this.ns).getInstruction(old_cp).execute();
  }

  getExtractors() {
    let extractors = [];
    for (let i=3*NB_MEMORIES_PER_HALF_OCTAD; i<4*NB_MEMORIES_PER_HALF_OCTAD; i++) {
      extractors.push(this.ioGroup.getWord(i));
    }
    return extractors;
  }

  getIntroductors() {
    let introductors = [];
    for (let i=1*NB_MEMORIES_PER_HALF_OCTAD; i<2*NB_MEMORIES_PER_HALF_OCTAD; i++) {
      introductors.push(this.ioGroup.getWord(i));
    }
    return introductors;
  }

  connectMachine(machine) {
    this.connectedMachines.push(machine);
    machine.setBullGamma(this);
  }

}

module.exports.BullGamma = BullGamma;