1
0
mirror of https://github.com/pkimpel/retro-220.git synced 2026-01-13 15:18:24 +00:00
pkimpel.retro-220/webUI/B220PanelUtil.js
Paul Kimpel 8b6199afd8 Commit retro-220 version 0.02:
1. Finish implementation of Cardatron input and output units.
2. Minor corrections to BAC-Assembler script and BALGOL-Generator transcription.
3. Apply band-select "punches" in column 1 of WINTER.PI.card assembler deck.
4. Implement striping on Console register displays to highlight the opcode (62) fields.
5. Implement List-Cards utility to read cards from CR1 and list them on LP2.
2017-05-24 09:09:08 -07:00

909 lines
34 KiB
JavaScript

/***********************************************************************
* retro-220/webUI B220PanelUtil.js
************************************************************************
* Copyright (c) 2017, Paul Kimpel.
* Licensed under the MIT License, see
* http://www.opensource.org/licenses/mit-license.php
************************************************************************
* JavaScript object definition for the Burroughs 220 Emulator Control
* Panel utility constructors:
* NeonLamp
* NeonLampBox
* ColoredLamp
* ToggleSwitch
* ThreeWaySwitch
* OrganSwitch
* BlackControlKnob
* PanelRegister
************************************************************************
* 2017-01-01 P.Kimpel
* Original version, from retro-205 D205PanelUtil.js.
***********************************************************************/
/***********************************************************************
* Panel Neon Lamp *
***********************************************************************/
function NeonLamp(parent, x, y, id) {
/* Constructor for the neon lamp objects used within panels. x & y are the
coordinates of the lamp within its containing element; id is the DOM id */
this.state = 0; // current lamp state, 0=off
this.topCaptionDiv = null; // optional top caption element
this.bottomCaptionDiv = null; // optional bottom caption element
// visible DOM element
this.element = document.createElement("div");
this.element.id = id;
this.element.className = NeonLamp.lampClass;
if (x !== null) {
this.element.style.left = x.toString() + "px";
}
if (y !== null) {
this.element.style.top = y.toString() + "px";
}
if (parent) {
parent.appendChild(this.element);
}
}
/**************************************/
NeonLamp.topCaptionClass = "neonLampTopCaption";
NeonLamp.bottomCaptionClass = "neonLampBottomCaption";
NeonLamp.lampClass = "neonLamp";
NeonLamp.litClass = "neonLamp neonLit";
NeonLamp.lampLevels = 6;
NeonLamp.levelClass = [ // css class names for the lamp levels
NeonLamp.lampClass,
NeonLamp.litClass + "1",
NeonLamp.litClass + "2",
NeonLamp.litClass + "3",
NeonLamp.litClass + "4",
NeonLamp.litClass + "5",
NeonLamp.litClass];
/**************************************/
NeonLamp.prototype.addEventListener = function addEventListener(eventName, handler, useCapture) {
/* Sets an event handler whenever the image element is clicked */
this.element.addEventListener(eventName, handler, useCapture);
};
/**************************************/
NeonLamp.prototype.set = function set(state) {
/* Changes the visible state of the lamp according to the value of "state", 0-1 */
var newState = Math.max(Math.min(Math.round(state*NeonLamp.lampLevels + 0.4999), NeonLamp.lampLevels), 0);
if (this.state ^ newState) { // the state has changed
this.state = newState;
this.element.className = NeonLamp.levelClass[newState];
}
};
/**************************************/
NeonLamp.prototype.flip = function flip() {
/* Complements the visible state of the lamp */
this.set(1.0 - this.state/NeonLamp.lampLevels);
};
/**************************************/
NeonLamp.prototype.setCaption = function setCaption(caption, atBottom) {
/* Establishes an optional caption at the top or bottom of a single lamp.
Returns the caption element */
var e = (atBottom ? this.bottomCaptionDiv : this.topCaptionDiv);
if (e) {
e.textContent = caption;
} else {
e = document.createElement("div");
if (atBottom) {
this.bottomCaptionDiv = e;
e.className = NeonLamp.bottomCaptionClass;
} else {
this.topCaptionDiv = e;
e.className = NeonLamp.topCaptionClass;
}
e.appendChild(document.createTextNode(caption));
this.element.appendChild(e);
}
return e;
};
/***********************************************************************
* Panel Neon Lamp Box *
***********************************************************************/
function NeonLampBox(parent, x, y, id, caption) {
/* Constructor for the neon lamp-in-a-box objects used within panels.
x & y are the coordinates of the box within its containing element;
id is the DOM id */
// visible DOM element
this.element = document.createElement("div");
this.element.id = id;
this.element.className = NeonLampBox.lampBoxClass;
if (x !== null) {
this.element.style.left = x.toString() + "px";
}
if (y !== null) {
this.element.style.top = y.toString() + "px";
}
this.lamp = new NeonLamp(this.element, 3, 3, id + "_Lamp");
this.button = document.createElement("div");
this.button.id = id + "_LampBtn";
this.button.className = NeonLampBox.lampButtonClass;
this.button.textContent = caption;
this.element.appendChild(this.button);
if (parent) {
parent.appendChild(this.element);
}
}
/**************************************/
NeonLampBox.lampBoxClass = "lampBox";
NeonLampBox.lampButtonClass = "lampButton";
/**************************************/
NeonLampBox.prototype.addEventListener = function addEventListener(eventName, handler, useCapture) {
/* Sets an event handler whenever the image element is clicked */
this.element.addEventListener(eventName, handler, useCapture);
};
/**************************************/
NeonLampBox.prototype.set = function set(state) {
/* Changes the visible state of the lamp according to the value of "state", 0-1 */
this.lamp.set(state);
};
/**************************************/
NeonLampBox.prototype.flip = function flip() {
/* Complements the visible state of the lamp */
this.lamp.flip();
};
/**************************************/
NeonLampBox.prototype.setCaption = function setCaption(caption) {
/* Establishes an optional caption on the button for a single lamp */
this.button.textContent = caption;
};
/***********************************************************************
* Panel Colored Lamp *
***********************************************************************/
function ColoredLamp(parent, x, y, id, offClass, onClass) {
/* Constructor for the colored lamp objects used within panels. x & y are
the coordinates of the lamp within its containing element; id is the DOM id */
this.state = 0; // current lamp state, 0=off
this.topCaptionDiv = null; // optional top caption element
this.bottomCaptionDiv = null; // optional bottom caption element
this.lampClass = offClass; // css styling for an "off" lamp
this.litClass = // css styling for an "on" lamp
offClass + " " + onClass;
this.levelClass = [ // css class names for the lamp levels
offClass,
this.litClass + "1",
this.litClass + "2",
this.litClass + "3",
this.litClass + "4",
this.litClass + "5",
this.litClass];
// visible DOM element
this.element = document.createElement("div");
this.element.id = id;
this.element.className = offClass;
if (x !== null) {
this.element.style.left = x.toString() + "px";
}
if (y !== null) {
this.element.style.top = y.toString() + "px";
}
if (parent) {
parent.appendChild(this.element);
}
}
/**************************************/
ColoredLamp.lampLevels = 6;
ColoredLamp.topCaptionClass = "coloredLampTopCaption";
ColoredLamp.bottomCaptionClass = "coloredLampBottomCaption";
/**************************************/
ColoredLamp.prototype.addEventListener = function addEventListener(eventName, handler, useCapture) {
/* Sets an event handler whenever the image element is clicked */
this.element.addEventListener(eventName, handler, useCapture);
};
/**************************************/
ColoredLamp.prototype.set = function set(state) {
/* Changes the visible state of the lamp according to the value of "state", 0-1 */
var newState = Math.max(Math.min(Math.round(state*ColoredLamp.lampLevels + 0.4999), ColoredLamp.lampLevels), 0);
if (this.state != newState) { // the state has changed
this.state = newState;
this.element.className = this.levelClass[newState];
}
};
/**************************************/
ColoredLamp.prototype.flip = function flip() {
/* Complements the visible state of the lamp */
this.set(ColoredLamp.lampLevels - this.state);
};
/**************************************/
ColoredLamp.prototype.setCaption = function setCaption(caption, atBottom) {
/* Establishes an optional caption at the top or bottom of a single lamp.
Returns the caption element */
var e = (atBottom ? this.bottomCaptionDiv : this.topCaptionDiv);
if (e) {
e.textContent = caption;
} else {
e = document.createElement("div");
if (atBottom) {
this.bottomCaptionDiv = e;
e.className = ColoredLamp.bottomCaptionClass;
} else {
this.topCaptionDiv = e;
e.className = ColoredLamp.topCaptionClass;
}
e.appendChild(document.createTextNode(caption));
this.element.appendChild(e);
}
return e;
};
/***********************************************************************
* Panel Toggle Switch *
***********************************************************************/
function ToggleSwitch(parent, x, y, id, offImage, onImage) {
/* Constructor for the toggle switch objects used within panels. x & y are
the coordinates of the switch within its containing element; id is the DOM id */
this.state = 0; // current switch state, 0=off
this.topCaptionDiv = null; // optional top caption element
this.bottomCaptionDiv = null; // optional bottom caption element
this.offImage = offImage; // image used for the off state
this.onImage = onImage; // image used for the on state
// visible DOM element
this.element = document.createElement("img");
this.element.id = id;
this.element.src = offImage;
if (x !== null) {
this.element.style.left = x.toString() + "px";
}
if (y !== null) {
this.element.style.top = y.toString() + "px";
}
if (parent) {
parent.appendChild(this.element);
}
}
/**************************************/
ToggleSwitch.topCaptionClass = "toggleSwitchTopCaption";
ToggleSwitch.bottomCaptionClass = "toggleSwitchBottomCaption";
/**************************************/
ToggleSwitch.prototype.addEventListener = function addEventListener(eventName, handler, useCapture) {
/* Sets an event handler whenever the image element is clicked */
this.element.addEventListener(eventName, handler, useCapture);
};
/**************************************/
ToggleSwitch.prototype.set = function set(state) {
/* Changes the visible state of the switch according to the low-order
bit of "state" */
var newState = state & 1;
if (this.state ^ newState) { // the state has changed
this.state = newState;
this.element.src = (newState ? this.onImage : this.offImage);
}
};
/**************************************/
ToggleSwitch.prototype.flip = function flip() {
/* Complements the visible state of the switch */
var newState = this.state ^ 1;
this.state = newState;
this.element.src = (newState ? this.onImage : this.offImage);
};
/**************************************/
ToggleSwitch.prototype.setCaption = function setCaption(caption, atBottom) {
/* Establishes an optional caption at the top or bottom of a single switch.
Returns the caption element */
var e = (atBottom ? this.bottomCaptionDiv : this.topCaptionDiv);
if (e) {
e.textContent = caption;
} else {
e = document.createElement("div");
if (atBottom) {
this.bottomCaptionDiv = e;
e.className = ToggleSwitch.bottomCaptionClass;
} else {
this.topCaptionDiv = e;
e.className = ToggleSwitch.topCaptionClass;
}
e.appendChild(document.createTextNode(caption));
this.element.appendChild(e);
}
return e;
};
/***********************************************************************
* Panel Three-way Toggle Switch *
***********************************************************************/
function ThreeWaySwitch(parent, x, y, id, offImage, onImage1, onImage2) {
/* Constructor for the three-way toggle switch objects used within panels.
x & y are the coordinates of the switch within its containing element;
id is the DOM id */
this.state = 0; // current switch state, 0=off, 1=down, 2=up)
this.topCaptionDiv = null; // optional top caption element
this.bottomCaptionDiv = null; // optional bottom caption element
this.offImage = offImage; // image used for the off state
this.onImage1 = onImage1; // image used for the lower on state
this.onImage2 = onImage2; // image used for the upper on state
// visible DOM element
this.element = document.createElement("img");
this.element.id = id;
this.element.src = offImage;
if (x !== null) {
this.element.style.left = x.toString() + "px";
}
if (y !== null) {
this.element.style.top = y.toString() + "px";
}
if (parent) {
parent.appendChild(this.element);
}
}
/**************************************/
ThreeWaySwitch.topCaptionClass = "ToggleSwitchTopCaption";
ThreeWaySwitch.bottomCaptionClass = "ToggleSwitchBottomCaption";
/**************************************/
ThreeWaySwitch.prototype.addEventListener = function addEventListener(eventName, handler, useCapture) {
/* Sets an event handler whenever the image element is clicked */
this.element.addEventListener(eventName, handler, useCapture);
};
/**************************************/
ThreeWaySwitch.prototype.set = function set(state) {
/* Changes the visible state of the switch according to the value
of "state" */
if (this.state != state) { // the state has changed
switch (state) {
case 1: // down position
this.state = 1;
this.element.src = this.onImage1;
break;
case 2: // up position
this.state = 2;
this.element.src = this.onImage2;
break;
default: // middle position
this.state = 0;
this.element.src = this.offImage;
break;
} // switch state
}
};
/**************************************/
ThreeWaySwitch.prototype.flip = function flip() {
/* Increments the visible state of the switch */
this.set(this.state+1);
};
/**************************************/
ThreeWaySwitch.prototype.setCaption = function setCaption(caption, atBottom) {
/* Establishes an optional caption at the top or bottom of a single switch.
Returns the caption element */
var e = (atBottom ? this.bottomCaptionDiv : this.topCaptionDiv);
if (e) {
e.textContent = caption;
} else {
e = document.createElement("div");
if (atBottom) {
this.bottomCaptionDiv = e;
e.className = ThreeWaySwitch.bottomCaptionClass;
} else {
this.topCaptionDiv = e;
e.className = ThreeWaySwitch.topCaptionClass;
}
e.appendChild(document.createTextNode(caption));
this.element.appendChild(e);
}
return e;
};
/***********************************************************************
* Panel Organ Switch *
***********************************************************************/
function OrganSwitch(parent, x, y, id, offImage, onImage, momentary) {
/* Constructor for the organ switch objects used within panels. x & y are
the coordinates of the switch within its containing element; id is the DOM id */
this.state = 0; // current switch state, 0=off
this.topCaptionDiv = null; // optional top caption element
this.bottomCaptionDiv = null; // optional bottom caption element
this.offImage = offImage; // image used for the off state
this.onImage = onImage; // image used for the on state
this.momentary = momentary || false;// true if the switch is only momentary-on
// visible DOM element
this.element = document.createElement("img");
this.element.id = id;
this.element.src = offImage;
if (x !== null) {
this.element.style.left = x.toString() + "px";
}
if (y !== null) {
this.element.style.top = y.toString() + "px";
}
if (parent) {
parent.appendChild(this.element);
}
}
/**************************************/
OrganSwitch.topCaptionClass = "OrganSwitchTopCaption";
OrganSwitch.bottomCaptionClass = "OrganSwitchBottomCaption";
OrganSwitch.momentaryPeriod = 200; // time for momentary switch to bounce back, ms
/**************************************/
OrganSwitch.prototype.addEventListener = function addEventListener(eventName, handler, useCapture) {
/* Sets an event handler whenever the image element is clicked */
this.element.addEventListener(eventName, handler, useCapture);
};
/**************************************/
OrganSwitch.prototype.set = function set(state) {
/* Changes the visible state of the switch according to the low-order
bit of "state" */
var newState = state & 1;
if (this.state ^ newState) { // the state has changed
this.state = newState;
this.element.src = (newState ? this.onImage : this.offImage);
if (this.momentary && newState) {
setCallback(null, this, OrganSwitch.momentaryPeriod, this.set, 0);
}
}
};
/**************************************/
OrganSwitch.prototype.flip = function flip() {
/* Complements the visible state of the switch */
this.set(this.state ^ 1);
};
/**************************************/
OrganSwitch.prototype.setCaption = function setCaption(caption, atBottom) {
/* Establishes an optional caption at the top or bottom of a single switch.
Returns the caption element */
var e = (atBottom ? this.bottomCaptionDiv : this.topCaptionDiv);
if (e) {
e.textContent = caption;
} else {
e = document.createElement("div");
if (atBottom) {
this.bottomCaptionDiv = e;
e.className = OrganSwitch.bottomCaptionClass;
} else {
this.topCaptionDiv = e;
e.className = OrganSwitch.topCaptionClass;
}
e.appendChild(document.createTextNode(caption));
this.element.appendChild(e);
}
return e;
};
/***********************************************************************
* Black Control Knob *
***********************************************************************/
function BlackControlKnob(parent, x, y, id, initial, positions) {
/* Constructor for the black control knob objects used within panels. x & y are
the coordinates of the knob within its containing element; id is the DOM id;
initial is the 0-relative index indicating the default position of the switch;
positions is an array indicating the angular position (in degrees, where 0
is straight up) of each of the knob's positions */
this.position = 0; // current knob position
this.direction = 1; // rotate knob clockwise(1), counter-clockwise(-1)
this.topCaptionDiv = null; // optional top caption element
this.bottomCaptionDiv = null; // optional bottom caption element
this.positions = positions; // array of knob position angles
// visible DOM element
this.element = document.createElement("canvas");
this.element.id = id;
this.element.width = BlackControlKnob.size;
this.element.height = BlackControlKnob.size;
this.element.className = BlackControlKnob.className;
if (x !== null) {
this.element.style.left = x.toString() + "px";
}
if (y !== null) {
this.element.style.top = y.toString() + "px";
}
if (parent) {
parent.appendChild(this.element);
}
this.set(initial); // set to its initial position
}
/**************************************/
BlackControlKnob.topCaptionClass = "blackControlKnobTopCaption";
BlackControlKnob.bottomCaptionClass = "blackControlKnobBottomCaption";
BlackControlKnob.className = "blackControlKnob1";
BlackControlKnob.canvasColor = "transparent";
BlackControlKnob.size = 64; // width/height in pixels
/**************************************/
BlackControlKnob.prototype.addEventListener = function addEventListener(eventName, handler, useCapture) {
/* Sets an event handler whenever the canvas element is clicked */
this.element.addEventListener(eventName, handler, useCapture);
};
/**************************************/
BlackControlKnob.prototype.set = function set(position) {
/* Changes the visible state of the knob according to the position index */
var dc = this.element.getContext("2d");
var degrees = Math.PI/180;
var fullCircle = 360*degrees;
var halfSize = Math.floor(BlackControlKnob.size/2);
var quarterSize = Math.floor(BlackControlKnob.size/4);
var silverSkirt;
if (position < 0) {
this.position = 0;
this.direction = 1;
} else if (position < this.positions.length) {
this.position = position;
} else {
this.position = this.positions.length-1;
this.direction = -1;
}
dc.save();
dc.translate(halfSize+0.5, halfSize+0.5); // move origin to the center
dc.fillStyle = BlackControlKnob.canvasColor;// fill in the panel background (aids antialiasing)
dc.fillRect(-halfSize, -halfSize, BlackControlKnob.size, BlackControlKnob.size);
silverSkirt = dc.createRadialGradient(0, 0, halfSize, 0, 0, quarterSize);
silverSkirt.addColorStop(0.5, "#FFF");
silverSkirt.addColorStop(1, "#CCC");
dc.beginPath(); // draw the outer skirt of the knob
dc.arc(0, 0, halfSize-1, 0, fullCircle, false);
dc.fillStyle = silverSkirt;
dc.fill();
dc.beginPath(); // draw the central knob
dc.arc(0, 0, quarterSize, 0, fullCircle, false);
dc.fillStyle = "#000";
dc.fill();
dc.beginPath(); // draw the inset on top of the knob
dc.arc(0, 0, quarterSize-4, 0, fullCircle, false);
dc.fillStyle = "#333";
dc.fill();
dc.save(); // draw the knob indicator
dc.rotate(this.positions[this.position]*degrees);
dc.beginPath();
dc.moveTo(0, 1-halfSize);
dc.lineTo(-quarterSize/4, -halfSize+quarterSize/2);
dc.lineTo(quarterSize/4, -halfSize+quarterSize/2);
dc.closePath();
dc.fillStyle = "#000";
dc.fill();
dc.restore(); // undo the rotation
dc.restore(); // undo the translation
};
/**************************************/
BlackControlKnob.prototype.step = function step() {
/* Steps the knob to its next position. If it is at the last position, steps it
to the first position */
var position = this.position+this.direction;
if (position < 0) {
this.direction = 1;
this.set(1);
} else if (position < this.positions.length) {
this.set(position);
} else {
this.direction = -1;
this.set(this.positions.length-2);
}
};
/**************************************/
BlackControlKnob.prototype.setCaption = function setCaption(caption, atBottom) {
/* Establishes an optional caption at the top or bottom of a single switch.
Returns the caption element */
var e = (atBottom ? this.bottomCaptionDiv : this.topCaptionDiv);
if (e) {
e.textContent = caption;
} else {
e = document.createElement("div");
if (atBottom) {
this.bottomCaptionDiv = e;
e.className = BlackControlKnob.bottomCaptionClass;
} else {
this.topCaptionDiv = e;
e.className = BlackControlKnob.topCaptionClass;
}
e.appendChild(document.createTextNode(caption));
this.element.appendChild(e);
}
return e;
};
/***********************************************************************
* Panel Register *
***********************************************************************/
function PanelRegister(parent, bits, rows, idPrefix, caption) {
/* Constructor for the register objects used within panels:
parent:the DOM element (usually a <div>) within which the register will be built
bits: number of bits in register
rows: number of rows used to display the bit lamps
caption:optional caption displayed at the bottom of the register
*/
var cols = Math.floor((bits+rows-1)/rows);
var b;
var cx;
var cy;
var e;
this.element = parent; // containing element for the panel
this.bits = bits; // number of bits in the register
this.caption = caption || ""; // panel caption
this.lastValue = 0; // prior register value
this.lamps = new Array(bits); // bit lamps
cx = cols*PanelRegister.hSpacing + PanelRegister.hOffset;
for (b=0; b<bits; b++) {
if (b%rows == 0) {
cy = (rows-1)*PanelRegister.vSpacing + PanelRegister.vOffset;
cx -= PanelRegister.hSpacing;
} else {
cy -= PanelRegister.vSpacing;
}
this.lamps[b] = new NeonLampBox(parent, cx, cy, idPrefix + b.toString());
}
this.captionDiv = document.createElement("div");
this.captionDiv.className = PanelRegister.captionClass;
this.captionDiv.style.top = "0";
this.captionDiv.style.left = "0";
if (caption) {
e = document.createElement("span");
e.className = PanelRegister.captionSpanClass;
e.appendChild(document.createTextNode(caption.substring(0, 1)));
this.captionDiv.appendChild(e);
}
parent.appendChild(this.captionDiv);
this.leftClearBar = document.createElement("div");
this.leftClearBar.id = idPrefix + "LeftClear";
this.leftClearBar.className = PanelRegister.regClearBarClass;
this.leftClearBar.style.left = "0";
parent.appendChild(this.leftClearBar);
this.rightClearBar = document.createElement("div");
this.rightClearBar.id = idPrefix + "RightClear";
this.rightClearBar.className = PanelRegister.regClearBarClass;
this.rightClearBar.style.right = "0";
parent.appendChild(this.rightClearBar);
e = document.createElement("div");
e.className = PanelRegister.regSpacerClass;
e.style.top = "0";
e.style.right = "0";
parent.appendChild(e);
e = document.createElement("div");
e.className = PanelRegister.regSpacerClass;
e.style.bottom = "0";
e.style.left = "0";
parent.appendChild(e);
e = document.createElement("div");
e.className = PanelRegister.regSpacerClass;
e.style.bottom = "0";
e.style.right = "0";
parent.appendChild(e);
}
/**************************************/
PanelRegister.hSpacing = 24; // horizontal lamp spacing, pixels
PanelRegister.hOffset = 24; // horizontal lamp offset within container
PanelRegister.vSpacing = 32; // vertical lamp spacing, pixels
PanelRegister.vOffset = 0; // vertical lamp offset within container
PanelRegister.lampDiameter = 20; // lamp outer diameter, pixels
PanelRegister.panelClass = "panelRegister";
PanelRegister.captionClass = "panelRegCaption";
PanelRegister.regSpacerClass = "panelRegSpacer";
PanelRegister.regClearBarClass = "panelRegClearBar";
PanelRegister.captionSpanClass = "panelRegSpan";
PanelRegister.boxCaptionClass = "boxCaption";
/**************************************/
PanelRegister.prototype.addEventListener = function addEventListener(eventName, handler, useCapture) {
/* Sets an event handler whenever the parent element is clicked */
this.element.addEventListener(eventName, handler, useCapture);
};
/**************************************/
PanelRegister.prototype.xCoord = function xCoord(col) {
/* Returns the horizontal lamp coordinate in pixels */
return ((col-1)*PanelRegister.hSpacing + PanelRegister.hOffset);
};
/**************************************/
PanelRegister.prototype.yCoord = function yCoord(row) {
/* Returns the vertical lamp coordinate in pixels */
return ((row-1)*PanelRegister.vSpacing + PanelRegister.vOffset);
};
/**************************************/
PanelRegister.prototype.panelWidth = function panelWidth(cols) {
/* Returns the width of a register panel in pixels */
return (cols-1)*PanelRegister.hSpacing + PanelRegister.hOffset*2 + PanelRegister.lampDiameter;
};
/**************************************/
PanelRegister.prototype.panelHeight = function panelHeight(rows) {
/* Returns the height of a register panel in pixels */
return (rows-1)*PanelRegister.vSpacing + PanelRegister.vOffset*2 + PanelRegister.lampDiameter;
};
/**************************************/
PanelRegister.prototype.drawBox = function drawBox(col, lamps, rows, leftStyle, rightStyle) {
/* Creates a box centered around a specified group of lamps in a register.
leftStyle and rightStyle specify the left and right borders of the box using
standard CSS border syntax. Returns the box element */
var box = document.createElement("div");
var rightBias = (rightStyle ? 1 : 0);
box.style.position = "absolute";
box.style.left = (this.xCoord(col) - (PanelRegister.hSpacing-PanelRegister.lampDiameter)/2 + 1).toString() + "px";
box.style.width = (PanelRegister.hSpacing*lamps - 2).toString() + "px";
box.style.top = this.yCoord(1).toString() + "px";
box.style.height = (this.yCoord(rows+1) - this.yCoord(1)).toString() + "px";
box.style.borderLeft = leftStyle;
box.style.borderRight = rightStyle;
box.appendChild(document.createTextNode("\xA0"));
this.element.appendChild(box);
return box;
};
/**************************************/
PanelRegister.prototype.setBoxCaption = function setBoxCaption(box, caption) {
/* Establishes an optional caption for register lamp box.
Returns the caption element */
var e = box.captionDiv;
if (e) {
e.textContent = caption;
} else {
box.captionDiv = e = document.createElement("div");
e.className = PanelRegister.boxCaptionClass;
e.appendChild(document.createTextNode(caption));
box.appendChild(e);
}
return e;
};
/**************************************/
PanelRegister.prototype.update = function update(value) {
/* Update the register lamps from the value of the parameter. This routine
compares the value of the register that was previously updated against the new
one in an attempt to minimize the number of lamp flips that need to be done */
var bitBase = 0;
var bitNr;
var lastMask;
var lastValue = this.lastValue;
var thisMask;
var thisValue = Math.floor(Math.abs(value)) % 0x100000000000;
if (thisValue != lastValue) {
this.lastValue = thisValue; // save it for next time
do {
// Loop through the masks 30 bits at a time so we can use Javascript bit ops
bitNr = bitBase;
lastMask = lastValue % 0x40000000; // get the low-order 30 bits
lastValue = (lastValue-lastMask)/0x40000000; // shift the value right 30 bits
thisMask = thisValue % 0x40000000; // ditto for the second value
thisValue = (thisValue-thisMask)/0x40000000;
lastMask ^= thisMask; // determine which bits have changed
while (lastMask) {
if (lastMask & 0x01) {
this.lamps[bitNr].set(thisMask & 0x01);
}
if (++bitNr <= this.bits) {
lastMask >>>= 1;
thisMask >>>= 1;
} else {
thisValue = thisMask = 0;
lastValue = lastMask = 0;
break; // out of inner while loop
}
}
bitBase += 30;
} while (thisValue || lastValue);
}
};
/**************************************/
PanelRegister.prototype.updateGlow = function updateGlow(glow) {
/* Update the register lamps from the bitwise intensity values in "glow" */
var bitNr;
for (bitNr=this.bits-1; bitNr>=0; --bitNr) {
this.lamps[bitNr].set(glow[bitNr]);
}
};