Merge branch 'next-release'
This commit is contained in:
commit
634b31468f
12 changed files with 715 additions and 474 deletions
|
|
@ -122,6 +122,11 @@ function CPU(parmsCPU, nCyclesDefault)
|
|||
this.aCounts.nCyclesChecksumInterval = parmsCPU["csInterval"];
|
||||
this.aCounts.nCyclesChecksumStop = parmsCPU["csStop"];
|
||||
|
||||
/*
|
||||
* Array of countdown timers managed by addTimer() and setTimer().
|
||||
*/
|
||||
this.aTimers = [];
|
||||
|
||||
this.onRunTimeout = this.runCPU.bind(this); // function onRunTimeout() { cpu.runCPU(); };
|
||||
|
||||
this.setReady();
|
||||
|
|
@ -536,6 +541,9 @@ CPU.prototype.setBinding = function(sHTMLType, sBinding, control, sValue)
|
|||
* in anticipation of the timer requiring an update sooner than the normal nCyclesPerYield
|
||||
* period in runCPU() would normally provide.
|
||||
*
|
||||
* NOTE: In this context, "timer" refers to a timer chip (eg, an Intel 8253) being emulated by
|
||||
* by the ChipSet component, not the timers managed by the CPU (eg, addTimer(), setTimer(), etc).
|
||||
*
|
||||
* @this {CPU}
|
||||
* @param {number} nCycles is the target number of cycles to drop the current burst to
|
||||
* @return {boolean}
|
||||
|
|
@ -754,14 +762,14 @@ CPU.prototype.getSpeedTarget = function()
|
|||
*
|
||||
* NOTE: This used to return the target speed, in mhz, but no callers appear to care at this point.
|
||||
*
|
||||
* @desc Whenever the speed is changed, the running cycle count and corresponding start time must be reset,
|
||||
* so that the next effective speed calculation obtains sensible results. In fact, when runCPU() initially calls
|
||||
* setSpeed() with no parameters, that's all this function does (it doesn't change the current speed setting).
|
||||
*
|
||||
* @this {CPU}
|
||||
* @param {number} [nMultiplier] is the new proposed multiplier (reverts to 1 if the target was too high)
|
||||
* @param {boolean} [fUpdateFocus] is true to update Computer focus
|
||||
* @return {boolean} true if successful, false if not
|
||||
*
|
||||
* @desc Whenever the speed is changed, the running cycle count and corresponding start time must be reset,
|
||||
* so that the next effective speed calculation obtains sensible results. In fact, when runCPU() initially calls
|
||||
* setSpeed() with no parameters, that's all this function does (it doesn't change the current speed setting).
|
||||
*/
|
||||
CPU.prototype.setSpeed = function(nMultiplier, fUpdateFocus)
|
||||
{
|
||||
|
|
@ -939,6 +947,104 @@ CPU.prototype.calcRemainingTime = function()
|
|||
return msRemainsThisRun;
|
||||
};
|
||||
|
||||
/**
|
||||
* addTimer(callBack)
|
||||
*
|
||||
* Components that want to have timers that periodically fire after some number of milliseconds call
|
||||
* addTimer() to create the timer, and then setTimer() every time they want to arm it. There is currently
|
||||
* no removeTimer() because these are generally used for the entire lifetime of a component.
|
||||
*
|
||||
* Internally, each timer entry is a preallocated Array with two entries: a cycle countdown in element [0]
|
||||
* and a callback function in element [1]. A timer is initially dormant; dormant timers have a countdown
|
||||
* value of -1 (although any negative number will suffice) and active timers have a non-negative value.
|
||||
*
|
||||
* Why not use JavaScript's setTimeout() instead? Good question. For a good answer, see setTimer() below.
|
||||
*
|
||||
* @this {CPU}
|
||||
* @param {function()} callBack
|
||||
* @return {number} timer index
|
||||
*/
|
||||
CPU.prototype.addTimer = function(callBack)
|
||||
{
|
||||
var iTimer = this.aTimers.length;
|
||||
this.aTimers.push([-1, callBack]);
|
||||
return iTimer;
|
||||
};
|
||||
|
||||
/**
|
||||
* setTimer(iTimer, ms)
|
||||
*
|
||||
* Using the timer index from a previous addTimer() call, this sets that timer to fire after the
|
||||
* specified number of milliseconds.
|
||||
*
|
||||
* This is preferred over JavaScript's setTimeout(), because all our timers are effectively paused when
|
||||
* the CPU is paused (eg, when the Debugger halts execution). Moreover, setTimeout() handlers only run after
|
||||
* runCPU() yields, which is far too granular for some components (eg, when the SerialPort tries to simulate
|
||||
* receiver interrupts at 9600 baud).
|
||||
*
|
||||
* Ideally, the only function that would use setTimeout() is runCPU(), while the rest of the components would
|
||||
* use setTimer(); however, due to legacy code (ie, code that predates these functions) and/or laziness,
|
||||
* that's currently not the case. TODO: Fix.
|
||||
*
|
||||
* @this {CPU}
|
||||
* @param {number} iTimer
|
||||
* @param {number} ms (converted into a cycle countdown internally)
|
||||
* @return {number} (number of cycles used to arm timer, or -1 if error)
|
||||
*/
|
||||
CPU.prototype.setTimer = function(iTimer, ms)
|
||||
{
|
||||
var nCycles = -1;
|
||||
if (iTimer >= 0 && iTimer < this.aTimers.length) {
|
||||
nCycles = (this.aCounts.nCyclesPerSecond * this.aCounts.nCyclesMultiplier) / 1000 * ms;
|
||||
this.aTimers[iTimer][0] = nCycles;
|
||||
}
|
||||
return nCycles;
|
||||
};
|
||||
|
||||
/**
|
||||
* getTimerBurst(nCycles)
|
||||
*
|
||||
* Used by runCPU() to either accept or shorten the current burst if any timers need to fire soon.
|
||||
*
|
||||
* @this {CPU}
|
||||
* @param {number} nCycles (number of cycles about to execute)
|
||||
* @return {number} (either nCycles or less if a timer needs to fire)
|
||||
*/
|
||||
CPU.prototype.getTimerBurst = function(nCycles)
|
||||
{
|
||||
for (var i = 0; i < this.aTimers.length; i++) {
|
||||
var timer = this.aTimers[i];
|
||||
if (timer[0] < 0) continue;
|
||||
if (nCycles > timer[0]) {
|
||||
nCycles = timer[0];
|
||||
}
|
||||
}
|
||||
return nCycles;
|
||||
};
|
||||
|
||||
/**
|
||||
* updateTimers(nCycles)
|
||||
*
|
||||
* Used by runCPU() to reduce all active timer countdown values by the number of cycles just executed;
|
||||
* this is the function that actually "fires" any timer(s) whose countdown has reached (or dropped below)
|
||||
* zero, invoking their callback function.
|
||||
*
|
||||
* @this {CPU}
|
||||
* @param {number} nCycles (number of cycles actually executed)
|
||||
*/
|
||||
CPU.prototype.updateTimers = function(nCycles)
|
||||
{
|
||||
for (var i = 0; i < this.aTimers.length; i++) {
|
||||
var timer = this.aTimers[i];
|
||||
if (timer[0] < 0) continue;
|
||||
timer[0] -= nCycles;
|
||||
if (timer[0] <= 0) {
|
||||
timer[0] = -1; // zero is technically an "active" value, so ensure the timer is dormant now
|
||||
timer[1](); // safe to invoke the callback function now
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* runCPU(fUpdateFocus)
|
||||
*
|
||||
|
|
@ -962,22 +1068,37 @@ CPU.prototype.runCPU = function(fUpdateFocus)
|
|||
this.calcStartTime();
|
||||
try {
|
||||
do {
|
||||
var nCyclesPerBurst = (this.flags.fChecksum? 1 : this.aCounts.nCyclesPerBurst);
|
||||
|
||||
/*
|
||||
* nCyclesPerBurst is how many cycles we WANT to run on each iteration of stepCPU(), but it may run
|
||||
* significantly less (or slightly more, since we can't execute partial instructions).
|
||||
*/
|
||||
var nCyclesPerBurst = (this.flags.fChecksum? 1 : this.aCounts.nCyclesPerBurst);
|
||||
|
||||
/*
|
||||
* Adjust nCyclesPerBurst if there are any CPU timers that need to fire within the current burst.
|
||||
*/
|
||||
nCyclesPerBurst = this.getTimerBurst(nCyclesPerBurst);
|
||||
|
||||
/*
|
||||
* Execute the burst.
|
||||
*/
|
||||
this.stepCPU(nCyclesPerBurst);
|
||||
|
||||
/*
|
||||
* nBurstCycles, less any remaining nStepCycles, is how many cycles stepCPU() ACTUALLY ran (nCycles).
|
||||
* We add that to nCyclesThisRun, as well as nRunCycles, which is the cycle count since the CPU first
|
||||
* started running.
|
||||
* nCycles is how many cycles stepCPU() actually ran (nBurstCycles less any remaining nStepCycles).
|
||||
*/
|
||||
var nCycles = this.nBurstCycles - this.nStepCycles;
|
||||
this.nRunCycles += nCycles;
|
||||
|
||||
/*
|
||||
* Update any/all timers, firing those whose cycle countdowns have reached (or dropped below) zero.
|
||||
*/
|
||||
this.updateTimers(nCycles);
|
||||
|
||||
/*
|
||||
* Add nCycles to nCyclesThisRun, as well as nRunCycles (the cycle count since the CPU first started).
|
||||
*/
|
||||
this.aCounts.nCyclesThisRun += nCycles;
|
||||
this.nRunCycles += nCycles;
|
||||
this.addCycles(0, true);
|
||||
this.updateChecksum(nCycles);
|
||||
|
||||
|
|
|
|||
|
|
@ -60,17 +60,17 @@ var CPUDef = {
|
|||
* Processor Status flag definitions (stored in regPS)
|
||||
*/
|
||||
PS: {
|
||||
CF: 0x0001, // bit 0: Carry flag
|
||||
CF: 0x0001, // bit 0: Carry Flag
|
||||
BIT1: 0x0002, // bit 1: reserved, always set
|
||||
PF: 0x0004, // bit 2: Parity flag
|
||||
PF: 0x0004, // bit 2: Parity Flag
|
||||
BIT3: 0x0008, // bit 3: reserved, always clear
|
||||
AF: 0x0010, // bit 4: Auxiliary Carry flag
|
||||
AF: 0x0010, // bit 4: Auxiliary Carry Flag
|
||||
BIT5: 0x0020, // bit 5: reserved, always clear
|
||||
ZF: 0x0040, // bit 6: Zero flag
|
||||
SF: 0x0080, // bit 7: Sign flag
|
||||
ZF: 0x0040, // bit 6: Zero Flag
|
||||
SF: 0x0080, // bit 7: Sign Flag
|
||||
ALL: 0x00D5, // all "arithmetic" flags (CF, PF, AF, ZF, SF)
|
||||
MASK: 0x00FF, //
|
||||
IF: 0x0200 // bit 9: Interrupt flag (set if interrupts enabled; for internal use only)
|
||||
IF: 0x0200 // bit 9: Interrupt Flag (set if interrupts enabled; Intel calls this the INTE bit)
|
||||
},
|
||||
PARITY: [ // 256-byte array with a 1 wherever the number of set bits of the array index is EVEN
|
||||
1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
|
||||
|
|
@ -94,10 +94,9 @@ var CPUDef = {
|
|||
* Interrupt-related flags (stored in intFlags)
|
||||
*/
|
||||
INTFLAG: {
|
||||
NONE: 0x00,
|
||||
INTL: 0x07, // last interrupt level requested
|
||||
INTR: 0x08, // set if interrupt has been requested
|
||||
HALT: 0x10 // halt requested; see opHLT()
|
||||
NONE: 0x0000,
|
||||
INTR: 0x00ff, // mask for 8 bits, representing interrupt levels 0-7
|
||||
HALT: 0x0100 // halt requested; see opHLT()
|
||||
},
|
||||
/*
|
||||
* Opcode definitions
|
||||
|
|
|
|||
|
|
@ -938,47 +938,50 @@ CPUState.prototype.pushWord = function(w)
|
|||
CPUState.prototype.checkINTR = function()
|
||||
{
|
||||
if ((this.intFlags & CPUDef.INTFLAG.INTR) && this.getIF()) {
|
||||
var bRST = CPUDef.OPCODE.RST0 | ((this.intFlags & CPUDef.INTFLAG.INTL) << 3);
|
||||
this.intFlags &= ~CPUDef.INTFLAG.HALT;
|
||||
this.clearINTR();
|
||||
for (var nLevel = 0; nLevel < 8; nLevel++) {
|
||||
if (this.intFlags & (1 << nLevel)) break;
|
||||
}
|
||||
this.clearINTR(nLevel);
|
||||
this.clearIF();
|
||||
this.aOps[bRST].call(this);
|
||||
this.intFlags &= ~CPUDef.INTFLAG.HALT;
|
||||
this.aOps[CPUDef.OPCODE.RST0 | (nLevel << 3)].call(this);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* clearINTR()
|
||||
* clearINTR(nLevel)
|
||||
*
|
||||
* Clear the corresponding interrupt level.
|
||||
*
|
||||
* nLevel can either be a valid interrupt level (0-7), or -1 to clear all pending interrupts
|
||||
* (eg, in the event of a system-wide reset).
|
||||
*
|
||||
* @this {CPUState}
|
||||
* @param {number} nLevel (0-7, or -1 for all)
|
||||
*/
|
||||
CPUState.prototype.clearINTR = function()
|
||||
CPUState.prototype.clearINTR = function(nLevel)
|
||||
{
|
||||
this.intFlags &= ~(CPUDef.INTFLAG.INTL | CPUDef.INTFLAG.INTR);
|
||||
var bitsClear = nLevel < 0? 0xff : (1 << nLevel);
|
||||
this.intFlags &= ~bitsClear;
|
||||
};
|
||||
|
||||
/**
|
||||
* requestINTR(nLevel)
|
||||
*
|
||||
* This is called by any component that wants to request a h/w interrupt.
|
||||
* Request the corresponding interrupt level.
|
||||
*
|
||||
* NOTE: We allow INTR to be set regardless of the current state of interrupt flag (IF), on the theory
|
||||
* that if/when the CPU briefly turns interrupts off, it shouldn't lose the last h/w interrupt requested.
|
||||
* So instead of ignoring INTR here, checkINTR() ignores INTR as long as the interrupt flag (IF) is clear.
|
||||
*
|
||||
* The downside is that, as long as the CPU has interrupts disabled, an active INTR state will slow stepCPU()
|
||||
* down slightly. We could avoid that by introducing a two-stage interrupt tracking system, where a separate
|
||||
* variable keeps track of the last interrupt requested whenever the interrupt flag (IF) is clear, and when
|
||||
* setIF() finally occurs, that interrupt is propagated to intFlags. But for now, we're going to assume that
|
||||
* scenario is rare.
|
||||
* Each interrupt level (0-7) has its own intFlags bit (0-7). If one or more of those bits are set,
|
||||
* and the Interrupt Flag (IF) is also set, indicating that interrupts are enabled, then checkINTR()
|
||||
* chooses one of those bits, clears it, clears IF, and executes the corresponding RST opcode.
|
||||
*
|
||||
* @this {CPUState}
|
||||
* @param {number} nLevel (0-7)
|
||||
*/
|
||||
CPUState.prototype.requestINTR = function(nLevel)
|
||||
{
|
||||
this.intFlags = (this.intFlags & ~CPUDef.INTFLAG.INTL) | nLevel | CPUDef.INTFLAG.INTR;
|
||||
this.intFlags |= (1 << nLevel);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -115,15 +115,6 @@ function SerialPort(parmsSerial) {
|
|||
Component.bindExternalControl(this, sBinding, SerialPort.sIOBuffer);
|
||||
}
|
||||
|
||||
/*
|
||||
* Define a setTimeout() function that receiveData() can use when there's more data to receive.
|
||||
*/
|
||||
this.fnCheckDataReceived = function(serial) {
|
||||
return function() {
|
||||
serial.receiveData();
|
||||
}
|
||||
}(this);
|
||||
|
||||
/*
|
||||
* No connection until initBus() invokes initConnection().
|
||||
*/
|
||||
|
|
@ -368,10 +359,24 @@ SerialPort.prototype.initBus = function(cmp, bus, cpu, dbg)
|
|||
this.bus = bus;
|
||||
this.cpu = cpu;
|
||||
this.dbg = dbg;
|
||||
|
||||
var serial = this;
|
||||
this.timerReceiveNext = this.cpu.addTimer(function() {
|
||||
serial.printMessage("timerReceiveNext()");
|
||||
serial.receiveData();
|
||||
});
|
||||
this.timerTransmitNext = this.cpu.addTimer(function() {
|
||||
serial.printMessage("timerTransmitNext()");
|
||||
serial.transmitData();
|
||||
});
|
||||
|
||||
this.chipset = /** @type {ChipSet} */ (cmp.getMachineComponent("ChipSet"));
|
||||
|
||||
bus.addPortInputTable(this, SerialPort.aPortInput, this.portBase);
|
||||
bus.addPortOutputTable(this, SerialPort.aPortOutput, this.portBase);
|
||||
|
||||
this.initConnection();
|
||||
|
||||
this.setReady();
|
||||
};
|
||||
|
||||
|
|
@ -555,9 +560,11 @@ SerialPort.prototype.getBaudTimeout = function(maskRate)
|
|||
*/
|
||||
SerialPort.prototype.receiveByte = function(b)
|
||||
{
|
||||
this.printMessage("receiveByte(" + str.toHexByte(b) + "): " + str.toHexByte(this.bStatus));
|
||||
if (!(this.bStatus & SerialPort.UART8251.STATUS.RECV_FULL)) {
|
||||
this.bDataIn = b;
|
||||
this.bStatus |= SerialPort.UART8251.STATUS.RECV_FULL;
|
||||
this.printMessage("receiveByte(" + str.toHexByte(b) + "): " + str.toHexByte(this.bStatus) + " (requesting interrupt)");
|
||||
this.cpu.requestINTR(this.nIRQ);
|
||||
return true;
|
||||
}
|
||||
|
|
@ -567,6 +574,11 @@ SerialPort.prototype.receiveByte = function(b)
|
|||
/**
|
||||
* receiveData()
|
||||
*
|
||||
* Helper for clocking received data at the expected RECV_RATE.
|
||||
*
|
||||
* Currently, this is only use for test data that we "cram" down the terminal's throat, ensuring that we don't
|
||||
* cram it too rapidly.
|
||||
*
|
||||
* @this {SerialPort}
|
||||
*/
|
||||
SerialPort.prototype.receiveData = function()
|
||||
|
|
@ -575,21 +587,8 @@ SerialPort.prototype.receiveData = function()
|
|||
if (this.receiveByte(this.sDataReceived.charCodeAt(0))) {
|
||||
this.sDataReceived = this.sDataReceived.substr(1);
|
||||
}
|
||||
/*
|
||||
* TODO: If data has become undeliverable for some reason (eg, the Debugger has paused execution),
|
||||
* we should stop setting timeouts, and add one or more notification mechanisms to kickstart it again.
|
||||
*/
|
||||
if (this.sDataReceived) {
|
||||
/*
|
||||
* TODO: setTimeout() is a less-than-ideal solution, because it's too slow; timeouts won't fire until
|
||||
* the end of a CPU burst. So instead of calculating a number of milliseconds, we should calculate a
|
||||
* number of CPU cycles, and create a CPU notification mechanism that calls us back after that many cycles
|
||||
* have elapsed (and which will automatically shorten the current CPU burst as needed).
|
||||
*
|
||||
* This will also solve the other issue noted above, because if the CPU has been halted, it won't be
|
||||
* generating any notifications either.
|
||||
*/
|
||||
setTimeout(this.fnCheckDataReceived, this.getBaudTimeout(SerialPort.UART8251.BAUDRATES.RECV_RATE));
|
||||
if (this.sDataReceived && this.cpu) {
|
||||
this.cpu.setTimer(this.timerReceiveNext, this.getBaudTimeout(SerialPort.UART8251.BAUDRATES.RECV_RATE));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -612,10 +611,7 @@ SerialPort.prototype.transmitByte = function(b)
|
|||
}
|
||||
|
||||
if (this.controlIOBuffer) {
|
||||
if (b == 0x0D) {
|
||||
this.iLogicalCol = 0;
|
||||
}
|
||||
else if (b == 0x08) {
|
||||
if (b == 0x08) {
|
||||
this.controlIOBuffer.value = this.controlIOBuffer.value.slice(0, -1);
|
||||
/*
|
||||
* TODO: Back up the correct number of columns if the character erased was a tab.
|
||||
|
|
@ -623,13 +619,17 @@ SerialPort.prototype.transmitByte = function(b)
|
|||
if (this.iLogicalCol > 0) this.iLogicalCol--;
|
||||
}
|
||||
else {
|
||||
var s = String.fromCharCode(b);
|
||||
var nChars = (b >= 0x20? 1 : 0);
|
||||
var s = str.toASCIICode(b);
|
||||
var nChars = s.length;
|
||||
if (b == 0x09) {
|
||||
var tabSize = this.tabSize || 8;
|
||||
nChars = tabSize - (this.iLogicalCol % tabSize);
|
||||
if (this.tabSize) s = str.pad("", nChars);
|
||||
}
|
||||
else if (b == 0x0D) {
|
||||
this.iLogicalCol = nChars = 0;
|
||||
s = "\n";
|
||||
}
|
||||
if (this.charBOL && !this.iLogicalCol && nChars) s = String.fromCharCode(this.charBOL) + s;
|
||||
this.controlIOBuffer.value += s;
|
||||
this.controlIOBuffer.scrollTop = this.controlIOBuffer.scrollHeight;
|
||||
|
|
@ -651,6 +651,21 @@ SerialPort.prototype.transmitByte = function(b)
|
|||
return fTransmitted;
|
||||
};
|
||||
|
||||
/**
|
||||
* transmitData()
|
||||
*
|
||||
* Helper for clocking transmitted data at the expected XMIT_RATE.
|
||||
*
|
||||
* When timerTransmitNext fires, we have honored the programmed XMIT_RATE period, so we can
|
||||
* set XMIT_READY (and XMIT_EMPTY), which signals the firmware that another byte can be transmitted.
|
||||
*
|
||||
* @this {SerialPort}
|
||||
*/
|
||||
SerialPort.prototype.transmitData = function()
|
||||
{
|
||||
this.bStatus |= (SerialPort.UART8251.STATUS.XMIT_READY | SerialPort.UART8251.STATUS.XMIT_EMPTY);
|
||||
};
|
||||
|
||||
/**
|
||||
* isTransmitterReady()
|
||||
*
|
||||
|
|
@ -708,8 +723,20 @@ SerialPort.prototype.outData = function(port, bOut, addrFrom)
|
|||
this.printMessageIO(port, bOut, addrFrom, "DATA");
|
||||
this.bDataOut = bOut;
|
||||
this.bStatus &= ~(SerialPort.UART8251.STATUS.XMIT_READY | SerialPort.UART8251.STATUS.XMIT_EMPTY);
|
||||
if (this.transmitByte(bOut)) {
|
||||
this.bStatus |= (SerialPort.UART8251.STATUS.XMIT_READY | SerialPort.UART8251.STATUS.XMIT_EMPTY);
|
||||
/*
|
||||
* If we're transmitting to a virtual device that has no measurable delay, this code may clear XMIT_READY
|
||||
* too quickly.
|
||||
*
|
||||
* if (this.transmitByte(bOut)) {
|
||||
* this.bStatus |= (SerialPort.UART8251.STATUS.XMIT_READY | SerialPort.UART8251.STATUS.XMIT_EMPTY);
|
||||
* }
|
||||
*
|
||||
* A better solution is to arm a timer based on the XMIT_RATE baud rate, and clear the above bits when that
|
||||
* timer fires. Consequently, we no longer care what transmitByte() reports.
|
||||
*/
|
||||
this.transmitByte(bOut);
|
||||
if (this.cpu) {
|
||||
this.cpu.setTimer(this.timerTransmitNext, this.getBaudTimeout(SerialPort.UART8251.BAUDRATES.XMIT_RATE));
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1115,7 +1115,7 @@ Video.prototype.updateVT100 = function(fForced)
|
|||
/*
|
||||
* Cell cache logic is complicated by the fact that a line may be single-width one frame and double-width
|
||||
* the next. So we store the visible line length at the start of each row in the cache, which must match if
|
||||
* the cache is considered valid for the current line.
|
||||
* the cache can be considered valid for the current line.
|
||||
*/
|
||||
var fLineCacheValid = this.fCellCacheValid && (this.aCellCache[iCell] == nColsVisible);
|
||||
this.aCellCache[iCell++] = nColsVisible;
|
||||
|
|
@ -1153,6 +1153,9 @@ Video.prototype.updateVT100 = function(fForced)
|
|||
* cache entry, to guarantee that it's redrawn on the next update.
|
||||
*/
|
||||
this.assert(iCellUpdated >= 0);
|
||||
if (DEBUG && (this.aCellCache[iCellUpdated] & 0x7f) == 0x48) {
|
||||
console.log("spurious character?");
|
||||
}
|
||||
this.aCellCache[iCellUpdated] = -1;
|
||||
cUpdated = 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -381,7 +381,7 @@ str.replaceArray = function(a, s)
|
|||
* @param {string} s is a string
|
||||
* @param {number} cch is desired length
|
||||
* @param {boolean} [fPadLeft] (default is padding on the right)
|
||||
* @returns {string} the original string (s) with spaces padding it to the specified length
|
||||
* @return {string} the original string (s) with spaces padding it to the specified length
|
||||
*/
|
||||
str.pad = function(s, cch, fPadLeft)
|
||||
{
|
||||
|
|
@ -393,7 +393,7 @@ str.pad = function(s, cch, fPadLeft)
|
|||
* trim(s)
|
||||
*
|
||||
* @param {string} s
|
||||
* @returns {string}
|
||||
* @return {string}
|
||||
*/
|
||||
str.trim = function(s)
|
||||
{
|
||||
|
|
@ -403,4 +403,56 @@ str.trim = function(s)
|
|||
return s.replace(/^\s+|\s+$/g, "");
|
||||
};
|
||||
|
||||
str.aASCIICodes = {
|
||||
0x00: "NUL",
|
||||
0x01: "SOH", // Start of Heading
|
||||
0x02: "STX", // Start of Text
|
||||
0x03: "ETX", // End of Text
|
||||
0x04: "EOT", // End of Transmission
|
||||
0x05: "ENQ", // Enquiry
|
||||
0x06: "ACK", // Acknowledge
|
||||
0x07: "BEL", // Bell
|
||||
0x08: "BS", // Backspace
|
||||
0x09: "TAB", // Horizontal Tab
|
||||
0x0A: "LF", // Line Feed (New Line)
|
||||
0x0B: "VT", // Vertical Tab
|
||||
0x0C: "FF", // Form Feed (New Page)
|
||||
0x0D: "CR", // Carriage Return
|
||||
0x0E: "SO", // Shift Out
|
||||
0x0F: "SI", // Shift In
|
||||
0x10: "DLE", // Data Link Escape
|
||||
0x11: "DC1", // Device Control 1
|
||||
0x12: "DC2", // Device Control 2
|
||||
0x13: "DC3", // Device Control 3
|
||||
0x14: "DC4", // Device Control 4
|
||||
0x15: "NAK", // Negative Acknowledge
|
||||
0x16: "SYN", // Synchronous Idle
|
||||
0x17: "ETB", // End of Transmission Block
|
||||
0x18: "CAN", // Cancel
|
||||
0x19: "EM", // End of Medium
|
||||
0x1A: "SUB", // Substitute
|
||||
0x1B: "ESC", // Escape
|
||||
0x1C: "FS", // File Separator
|
||||
0x1D: "GS", // Group Separator
|
||||
0x1E: "RS", // Record Separator
|
||||
0x1F: "US" // Unit Separator
|
||||
};
|
||||
|
||||
/**
|
||||
* toASCIICode(b)
|
||||
*
|
||||
* @param {number} b
|
||||
* @return {string}
|
||||
*/
|
||||
str.toASCIICode = function(b)
|
||||
{
|
||||
var s = str.aASCIICodes[b];
|
||||
if (s) {
|
||||
s = '<' + s + '>';
|
||||
} else {
|
||||
s = String.fromCharCode(b);
|
||||
}
|
||||
return s;
|
||||
};
|
||||
|
||||
if (NODE) module.exports = str;
|
||||
|
|
|
|||
Loading…
Reference in a new issue