Work-around for serial mouse interrupts arriving too quickly

This commit is contained in:
Jeff Parsons 2015-03-18 14:45:36 -07:00 • committed by jeffpar
commit bc153c3016
9 changed files with 96 additions and 41 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -3043,7 +3043,7 @@ ChipSet.prototype.outPICLo = function(iPIC, bOut, addrFrom)
var nIRQ = (nIRL == null? undefined : pic.nIRQBase + nIRL);
if (pic.bISR & bIREnd) {
if (DEBUG && this.messageEnabled(this.messageBitsIRQ(nIRQ))) {
this.printMessage("outPIC" + iPIC + "(0x" + str.toHexByte(pic.port) + "): IRQ " + nIRQ + " going out of service", true);
this.printMessage("outPIC" + iPIC + "(0x" + str.toHexByte(pic.port) + "): IRQ " + nIRQ + " ending @" + this.dbg.hexOffset(this.cpu.getIP(), this.cpu.getCS()) + " stack=" + this.dbg.hexOffset(this.cpu.getSP(), this.cpu.getSS()), true);
}
pic.bISR &= ~bIREnd;
this.checkIRR();

View file

@ -573,6 +573,9 @@ CPU.prototype.setBurstCycles = function(nCycles)
* NOTE: If nDelta is negative, we will actually be increasing nStepCycles and nBurstCycles.
* Which is OK, but if we're also taking snapshots of the cycle counts, to make sure that instruction
* costs are being properly assessed, then we need to update nSnapCycles as well.
*
* TODO: If the delta is negative, we could simply ignore the request, but we must first carefully
* consider the impact on the ChipSet timers.
*/
if (DEBUG) this.nSnapCycles -= nDelta;
this.nStepCycles -= nDelta;

View file

@ -470,8 +470,8 @@ Mouse.prototype.moveMouse = function(event)
}
if (this.xDelta || this.yDelta) {
/*
* As sendPacket() indicates, the x and y coordinates we pass below are for diagnostic purposes
* only. sendPacket() really only cares about xDelta and yDelta, which it then zeroes on completion.
* As sendPacket() indicates, any x and y coordinates we supply are for diagnostic purposes only.
* sendPacket() only cares about the xDelta and yDelta properties, which it then zeroes on completion.
*/
this.sendPacket(null, event.clientX, event.clientY);
}
@ -499,19 +499,17 @@ Mouse.prototype.clickMouse = function(iButton, fDown)
this.fLocked = null;
}
}
var sDiag;
var sDiag = DEBUGGER? ("mouse button" + iButton + ' ' + (fDown? "dn" : "up")) : null;
switch (iButton) {
case 0:
if (this.fButton1 != fDown) {
this.fButton1 = fDown;
sDiag = DEBUGGER? ("mouse button1 " + (fDown? "dn" : "up")) : null;
this.sendPacket(sDiag);
}
break;
case 2:
if (this.fButton2 != fDown) {
this.fButton2 = fDown;
sDiag = DEBUGGER? ("mouse button2 " + (fDown? "dn" : "up")) : null;
this.sendPacket(sDiag);
}
break;

View file

@ -156,6 +156,33 @@ SerialPort.DLL = {REG: 0}; // Divisor Latch LSB (only when SerialPo
SerialPort.THR = {REG: 0}; // Transmitter Holding Register (write)
SerialPort.DL_DEFAULT = 0x180; // we select an arbitrary default Divisor Latch equivalent to 300 baud
/*
* The divisor is stored in wDL. If we take the frequency value 1843200 and divide it by wDL*128, we get the
* maximum number of bytes per second that the SerialPort interface should generate. For example, if a baud
* rate of 1200 is being used, the divisor will be 0x60 (96), so we calculate 1843200/(96*128) = 150, which means
* there should be a 1000ms/150 or 6.667ms delay between bytes delivered.
*
* TODO: Enforce that delay. However, the delay should be converted from real-world milliseconds to the
* appropriate number of CPU cycles we can pass to setBurstCycles(). This will also require the CPU to call
* us at the start of each burst, to see if advanceRBR() has more data to deliver. For now, I'm throttling
* SerialPort interrupts by passing a hard-coded delay to setIRR(). The setIRR() delay does not ensure any
* particular baud rate, it simply gives the underlying Interrupt Service Routine (ISR) some breathing room.
*
* The Microsoft Windows 1.01 serial mouse driver ISR issues an EOI before it has safely exited, relying solely
* on the fact that a 1200 baud serial device would not normally interrupt frequently enough to blow the stack.
* However, in PCjs, all you have to do is enable Debugger messages on every serial interrupt and mouse event,
* eg:
*
* m serial on;m pic on;m mouse on
*
* to slow the machine down to the point where serial mouse interrupts overwhelm the ISR. The Debugger messages
* display the current stack pointer, which you can watch drop to zero and then wrap around, no doubt trampling
* lots of code and data along the way.
*
* This problem can also occur without being forced by the Debugger; eg, whenever the physical machine's mouse is
* configured for a high interrupt rate.
*/
/*
* Receiver Buffer Register (RBR.REG, offset 0; eg, 0x3F8 or 0x2F8)
*/
@ -261,7 +288,8 @@ SerialPort.SCR = {REG: 7};
* @param {Mouse} mouse component
* @return {Component} this or null, based on whether or not the specified ID matches
*/
SerialPort.prototype.attachMouse = function(id, mouse) {
SerialPort.prototype.attachMouse = function(id, mouse)
{
if (id == this.idComponent) {
this.mouse = mouse;
return this;
@ -276,7 +304,8 @@ SerialPort.prototype.attachMouse = function(id, mouse) {
*
* @this {SerialPort}
*
SerialPort.prototype.syncMouse = function() {
SerialPort.prototype.syncMouse = function()
{
if (this.mouse) this.mouse.notifyMCR(this.bMCR);
};
*/
@ -290,7 +319,8 @@ SerialPort.prototype.syncMouse = function() {
* @param {Object} control is the HTML control DOM object (eg, HTMLButtonElement)
* @return {boolean} true if binding was successful, false if unrecognized binding request
*/
SerialPort.prototype.setBinding = function(sHTMLType, sBinding, control) {
SerialPort.prototype.setBinding = function(sHTMLType, sBinding, control)
{
var serial = this;
switch (sBinding) {
case SerialPort.sIOBuffer:
@ -339,7 +369,8 @@ SerialPort.prototype.setBinding = function(sHTMLType, sBinding, control) {
* @param {X86CPU} cpu
* @param {Debugger} dbg
*/
SerialPort.prototype.initBus = function(cmp, bus, cpu, dbg) {
SerialPort.prototype.initBus = function(cmp, bus, cpu, dbg)
{
this.bus = bus;
this.cpu = cpu;
this.dbg = dbg;
@ -357,7 +388,8 @@ SerialPort.prototype.initBus = function(cmp, bus, cpu, dbg) {
* @param {boolean} [fRepower]
* @return {boolean} true if successful, false if failure
*/
SerialPort.prototype.powerUp = function(data, fRepower) {
SerialPort.prototype.powerUp = function(data, fRepower)
{
if (!fRepower) {
if (!data || !this.restore) {
this.reset();
@ -375,7 +407,8 @@ SerialPort.prototype.powerUp = function(data, fRepower) {
* @param {boolean} fSave
* @return {Object|boolean}
*/
SerialPort.prototype.powerDown = function(fSave) {
SerialPort.prototype.powerDown = function(fSave)
{
return fSave && this.save ? this.save() : true;
};
@ -384,7 +417,8 @@ SerialPort.prototype.powerDown = function(fSave) {
*
* @this {SerialPort}
*/
SerialPort.prototype.reset = function() {
SerialPort.prototype.reset = function()
{
this.initState();
};
@ -396,7 +430,8 @@ SerialPort.prototype.reset = function() {
* @this {SerialPort}
* @return {Object}
*/
SerialPort.prototype.save = function() {
SerialPort.prototype.save = function()
{
var state = new State(this);
state.set(0, this.saveRegisters());
return state.data();
@ -411,7 +446,8 @@ SerialPort.prototype.save = function() {
* @param {Object} data
* @return {boolean} true if successful, false if failure
*/
SerialPort.prototype.restore = function(data) {
SerialPort.prototype.restore = function(data)
{
return this.initState(data[0]);
};
@ -422,7 +458,8 @@ SerialPort.prototype.restore = function(data) {
* @param {Array} [data]
* @return {boolean} true if successful, false if failure
*/
SerialPort.prototype.initState = function(data) {
SerialPort.prototype.initState = function(data)
{
/*
* The NS8250A spec doesn't explicitly say what the RBR and THR are initialized to on a reset,
* but I think we can safely assume zeros. Similarly, we reset the baud rate Divisor Latch (wDL)
@ -462,7 +499,8 @@ SerialPort.prototype.initState = function(data) {
* @this {SerialPort}
* @return {Array}
*/
SerialPort.prototype.saveRegisters = function() {
SerialPort.prototype.saveRegisters = function()
{
var i = 0;
var data = [];
data[i++] = this.bRBR;
@ -484,7 +522,8 @@ SerialPort.prototype.saveRegisters = function() {
* @this {SerialPort}
* @param {Array} ab is an array of bytes to propagate to the bRBR (Receiver Buffer Register)
*/
SerialPort.prototype.sendRBR = function(ab) {
SerialPort.prototype.sendRBR = function(ab)
{
this.abReceive = this.abReceive.concat(ab);
this.advanceRBR();
};
@ -494,7 +533,8 @@ SerialPort.prototype.sendRBR = function(ab) {
*
* @this {SerialPort}
*/
SerialPort.prototype.advanceRBR = function() {
SerialPort.prototype.advanceRBR = function()
{
if (this.abReceive.length > 0 && !(this.bLSR & SerialPort.LSR.DR)) {
this.bRBR = this.abReceive.shift();
this.bLSR |= SerialPort.LSR.DR;
@ -510,7 +550,8 @@ SerialPort.prototype.advanceRBR = function() {
* @param {number} [addrFrom] (not defined whenever the Debugger tries to read the specified port)
* @return {number} simulated port value
*/
SerialPort.prototype.inRBR = function(port, addrFrom) {
SerialPort.prototype.inRBR = function(port, addrFrom)
{
var b = ((this.bLCR & SerialPort.LCR.DLAB) ? (this.wDL & 0xff) : this.bRBR);
this.printMessageIO(port, null, addrFrom, (this.bLCR & SerialPort.LCR.DLAB) ? "DLL" : "RBR", b);
this.bLSR &= ~SerialPort.LSR.DR;
@ -526,7 +567,8 @@ SerialPort.prototype.inRBR = function(port, addrFrom) {
* @param {number} [addrFrom] (not defined whenever the Debugger tries to read the specified port)
* @return {number} simulated port value
*/
SerialPort.prototype.inIER = function(port, addrFrom) {
SerialPort.prototype.inIER = function(port, addrFrom)
{
var b = ((this.bLCR & SerialPort.LCR.DLAB) ? (this.wDL >> 8) : this.bIER);
this.printMessageIO(port, null, addrFrom, (this.bLCR & SerialPort.LCR.DLAB) ? "DLM" : "IER", b);
return b;
@ -540,7 +582,8 @@ SerialPort.prototype.inIER = function(port, addrFrom) {
* @param {number} [addrFrom] (not defined whenever the Debugger tries to read the specified port)
* @return {number} simulated port value
*/
SerialPort.prototype.inIIR = function(port, addrFrom) {
SerialPort.prototype.inIIR = function(port, addrFrom)
{
var b = this.bIIR;
this.printMessageIO(port, null, addrFrom, "IIR", b);
return b;
@ -554,7 +597,8 @@ SerialPort.prototype.inIIR = function(port, addrFrom) {
* @param {number} [addrFrom] (not defined whenever the Debugger tries to read the specified port)
* @return {number} simulated port value
*/
SerialPort.prototype.inLCR = function(port, addrFrom) {
SerialPort.prototype.inLCR = function(port, addrFrom)
{
var b = this.bLCR;
this.printMessageIO(port, null, addrFrom, "LCR", b);
return b;
@ -568,7 +612,8 @@ SerialPort.prototype.inLCR = function(port, addrFrom) {
* @param {number} [addrFrom] (not defined whenever the Debugger tries to read the specified port)
* @return {number} simulated port value
*/
SerialPort.prototype.inMCR = function(port, addrFrom) {
SerialPort.prototype.inMCR = function(port, addrFrom)
{
var b = this.bMCR;
this.printMessageIO(port, null, addrFrom, "MCR", b);
return b;
@ -582,7 +627,8 @@ SerialPort.prototype.inMCR = function(port, addrFrom) {
* @param {number} [addrFrom] (not defined whenever the Debugger tries to read the specified port)
* @return {number} simulated port value
*/
SerialPort.prototype.inLSR = function(port, addrFrom) {
SerialPort.prototype.inLSR = function(port, addrFrom)
{
var b = this.bLSR;
this.printMessageIO(port, null, addrFrom, "LSR", b);
return b;
@ -596,7 +642,8 @@ SerialPort.prototype.inLSR = function(port, addrFrom) {
* @param {number} [addrFrom] (not defined whenever the Debugger tries to read the specified port)
* @return {number} simulated port value
*/
SerialPort.prototype.inMSR = function(port, addrFrom) {
SerialPort.prototype.inMSR = function(port, addrFrom)
{
var b = this.bMSR;
this.printMessageIO(port, null, addrFrom, "MSR", b);
return b;
@ -610,7 +657,8 @@ SerialPort.prototype.inMSR = function(port, addrFrom) {
* @param {number} bOut
* @param {number} [addrFrom] (not defined whenever the Debugger tries to write the specified port)
*/
SerialPort.prototype.outTHR = function(port, bOut, addrFrom) {
SerialPort.prototype.outTHR = function(port, bOut, addrFrom)
{
this.printMessageIO(port, bOut, addrFrom, (this.bLCR & SerialPort.LCR.DLAB) ? "DLL" : "THR");
if (this.bLCR & SerialPort.LCR.DLAB) {
this.wDL = (this.wDL & ~0xff) | bOut;
@ -634,7 +682,8 @@ SerialPort.prototype.outTHR = function(port, bOut, addrFrom) {
* @param {number} bOut
* @param {number} [addrFrom] (not defined whenever the Debugger tries to write the specified port)
*/
SerialPort.prototype.outIER = function(port, bOut, addrFrom) {
SerialPort.prototype.outIER = function(port, bOut, addrFrom)
{
this.printMessageIO(port, bOut, addrFrom, (this.bLCR & SerialPort.LCR.DLAB) ? "DLM" : "IER");
if (this.bLCR & SerialPort.LCR.DLAB) {
this.wDL = (this.wDL & 0xff) | (bOut << 8);
@ -651,7 +700,8 @@ SerialPort.prototype.outIER = function(port, bOut, addrFrom) {
* @param {number} bOut
* @param {number} [addrFrom] (not defined whenever the Debugger tries to write the specified port)
*/
SerialPort.prototype.outLCR = function(port, bOut, addrFrom) {
SerialPort.prototype.outLCR = function(port, bOut, addrFrom)
{
this.printMessageIO(port, bOut, addrFrom, "LCR");
this.bLCR = bOut;
};
@ -664,7 +714,8 @@ SerialPort.prototype.outLCR = function(port, bOut, addrFrom) {
* @param {number} bOut
* @param {number} [addrFrom] (not defined whenever the Debugger tries to write the specified port)
*/
SerialPort.prototype.outMCR = function(port, bOut, addrFrom) {
SerialPort.prototype.outMCR = function(port, bOut, addrFrom)
{
var bPrev = this.bMCR;
this.printMessageIO(port, bOut, addrFrom, "MCR");
this.bMCR = bOut;
@ -678,7 +729,8 @@ SerialPort.prototype.outMCR = function(port, bOut, addrFrom) {
*
* @this {SerialPort}
*/
SerialPort.prototype.updateIRR = function() {
SerialPort.prototype.updateIRR = function()
{
var bIIR = -1;
if ((this.bLSR & SerialPort.LSR.DR) && (this.bIER & SerialPort.IER.RBR_AVAIL)) {
bIIR = SerialPort.IIR.INT_RBR;
@ -686,7 +738,7 @@ SerialPort.prototype.updateIRR = function() {
if (bIIR >= 0) {
this.bIIR &= ~(SerialPort.IIR.NO_INT | SerialPort.IIR.INT_BITS);
this.bIIR |= bIIR;
if (this.chipset && this.nIRQ) this.chipset.setIRR(this.nIRQ);
if (this.chipset && this.nIRQ) this.chipset.setIRR(this.nIRQ, 100);
} else {
this.bIIR |= SerialPort.IIR.NO_INT;
if (this.chipset && this.nIRQ) this.chipset.clearIRR(this.nIRQ);
@ -700,7 +752,8 @@ SerialPort.prototype.updateIRR = function() {
* @param {number} b
* @return {boolean} true if echoed, false if not
*/
SerialPort.prototype.echoByte = function(b) {
SerialPort.prototype.echoByte = function(b)
{
if (this.controlIOBuffer) {
if (b != 0x0D) {
if (b == 0x08) {
@ -746,7 +799,8 @@ SerialPort.aPortOutput = {
* attribute, invoking the constructor to create a SerialPort component, and then binding
* any associated HTML controls to the new component.
*/
SerialPort.init = function() {
SerialPort.init = function()
{
var aeSerial = Component.getElementsByClass(window.document, PCJSCLASS, "serial");
for (var iSerial = 0; iSerial < aeSerial.length; iSerial++) {
var eSerial = aeSerial[iSerial];

View file

@ -85,7 +85,7 @@ if (I386) {
* cycles would hurt overall performance. Similarly, 8254 timer counters are updated only on-demand.
*
* The 8237 and 8254, along with the 8259 interrupt controller and several other "chips", are combined
* into a single "Chipset" component, to keep the number of components we juggle to a minimum.
* into a single ChipSet component, to keep the number of components we juggle to a minimum.
*
* All that being said, this does not change the overall goal: to produce as accurate a simulation as
* possible, within the limits of what JavaScript allows and how precisely/predictably it behaves.

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long