 /***********************************************************************
* retro-b5500/emulator B5500DDPanel.js
************************************************************************
* Copyright (c) 2012, Nigel Williams and Paul Kimpel.
* Licensed under the MIT License, see http://www.opensource.org/licenses/mit-license.php
************************************************************************
* JavaScript object definition for the B5500 Distribution & Display panel
* utility constructors.
************************************************************************
* 2012-06-18  P.Kimpel
*   Original version, from thin air.
***********************************************************************/

/***********************************************************************
*  Panel Lamp                                                          *
***********************************************************************/
function B5500DDLamp(x, y) {
    /* Constructor for the lamp objects used within D&D. x & y are the
    coordinates of the lamp within its containing element */

    this.state = 0;                     // current lamp state, 0=off

    // visible DOM element
    this.element = document.createElement("div");
    this.element.className = "ddLamp";
    this.element.style.left = String(x) + "px";
    this.element.style.top = String(y) + "px";
}

/**************************************/

B5500DDLamp.onColor = "#FF9900";
B5500DDLamp.offColor = "#999999";

/**************************************/
B5500DDLamp.prototype.set = function(v) {
    /* Changes the visible state of the lamp according to the low-order
    bit of "v". */
    var newState = v & 1;

    if (this.state ^ newState) {         // the state has changed
        this.element.style.backgroundColor = (newState ? B5500DDLamp.onColor : B5500DDLamp.offColor);
        this.state = newState;
    }
}

/**************************************/
B5500DDLamp.prototype.flip = function() {
    /* Complements the visible state of the lamp */
    var newState = this.state ^ 1;

    this.element.style.backgroundColor = (newState ? B5500DDLamp.onColor : B5500DDLamp.offColor);
    this.state = newState;
}

/**************************************/
B5500DDLamp.prototype.setCaption = function(caption) {
    /* Establishes an optional caption for a single lamp */
    var e = document.createElement("div");

    e.className = "ddLampCaption";
    e.appendChild(document.createTextNode(caption));
    this.element.appendChild(e);
}


/***********************************************************************
*  Panel Register                                                      *
***********************************************************************/
function B5500DDRegister(bits, x, y, rows, caption) {
    /* Constructor for the register objects used within D&D:
        bits:   number of bits in register
        x:      horizontal coordinate of upper-left corner [hSpacing increments]
        y:      vertical coordinate of upper-left corner [vSpacing increments]
        rows:   number of rows used to display the bit lamps
    */
    var cols = Math.floor((bits+rows-1)/rows);
    var height = rows*B5500DDRegister.vSpacing;
    var width = cols*B5500DDRegister.hSpacing;
    var b;
    var cx = Math.floor((x-0.25)*B5500DDRegister.hSpacing);
    var cy = Math.floor((y-0.25)*B5500DDRegister.vSpacing);
    var lamp;

    this.bits = bits;                   // number of bits in the register
    this.left = cx;                     // horizontal offset relative to container
    this.top = cy;                      // vertical offset relative to container
    this.caption = caption || "";       // panel caption
    this.lastValue = 0;                 // prior register value
    this.lamps = new Array(bits+1);     // bit lamps

    // visible DOM element
    this.element = document.createElement("div");
    this.element.className = "ddRegister";
    this.element.style.left = String(cx) + "px";
    this.element.style.top = String(cy) + "px";
    this.element.style.width = String(width) + "px";
    this.element.style.height = String(height) + "px";

    cx = cols*B5500DDRegister.hSpacing + B5500DDRegister.hOffset;
    for (b=1; b<=bits; b++) {
        if ((b-1)%rows == 0) {
            cy = (rows-1)*B5500DDRegister.vSpacing + B5500DDRegister.vOffset;
            cx -= B5500DDRegister.hSpacing;
        } else {
            cy -= B5500DDRegister.vSpacing;
        }
        lamp = new B5500DDLamp(cx, cy);
        this.lamps[b] = lamp;
        this.element.appendChild(lamp.element);
    }

    this.captionDiv = document.createElement("div");
    this.captionDiv.className = "ddRegCaption";
    this.captionDiv.style.left = "2px";
    this.captionDiv.style.right = "2px";
    this.captionDiv.style.top = String(-B5500DDRegister.vOffset) + "px";
    if (caption) {
        lamp = document.createElement("span");
        lamp.className = "ddRegSpan";
        lamp.appendChild(document.createTextNode(caption));
        this.captionDiv.appendChild(lamp);
    }
    this.element.appendChild(this.captionDiv);

}

/**************************************/

B5500DDRegister.hSpacing = 24;          // horizontal lamp spacing, pixels
B5500DDRegister.hOffset = 5;            // horizontal lamp offset within container
B5500DDRegister.vSpacing = 24;          // vertical lamp spacing, pixels
B5500DDRegister.vOffset = 5;            // vertical lamp offset within container

/**************************************/
B5500DDRegister.prototype.xCoord = function(col) {
    /* Returns the horizontal lamp coordinate in "px" format */

    return String((col-1)*B5500DDRegister.hSpacing + B5500DDRegister.hOffset) + "px";
}

/**************************************/
B5500DDRegister.prototype.yCoord = function(row) {
    /* Returns the vertical lamp coordinate in "px" format */

    return String((row-1)*B5500DDRegister.vSpacing + B5500DDRegister.vOffset) + "px";
}

/**************************************/
B5500DDRegister.prototype.YYupdate = function(value) {
    /* Update the register lamps from the value of the parameter */
    var bitNr = 0;
    var low = (this.lastValue % 0x1000000) ^ (value % 0x1000000);
    var high = (Math.floor(this.lastValue / 0x1000000) % 0x1000000) ^ (Math.floor(value / 0x1000000) % 0x1000000);

    while (low) {
        bitNr++;
        if (low & 1) {
            this.lamps[bitNr].flip();
        }
        low >>>= 1;
    }
    bitNr = 23;
    while (high) {
        bitNr++;
        if (high & 1) {
            this.lamps[bitNr].flip();
        }
        high >>>= 1;
    }
    this.lastValue = value;
}

/**************************************/
B5500DDRegister.prototype.XXupdate = function(value) {
    /* Update the register lamps from the value of the parameter */
    var bitNr = 0;
    var bit;
    var mask = value % 0x1000000000000;

    while (mask) {
        bitNr++;
        bit = mask % 2;
        this.lamps[bitNr].set(bit);
        mask = (mask-bit)/2;
    }
}

/**************************************/
B5500DDRegister.prototype.update = function(value) {
    /* Update the register lamps from the value of the parameter */
    var bitNr = 0;
    var bit;
    var mask = value % 0x1000000000000;

    while (bitNr < this.bits) {
        bitNr++;
        bit = mask % 2;
        this.lamps[bitNr].element.style.backgroundColor = (bit ? B5500DDLamp.onColor : B5500DDLamp.offColor);
        mask = (mask-bit)/2;
    }
}
