Merge branch 'next-release'

This commit is contained in:
Jeff Parsons 2016-12-01 21:27:50 -08:00
commit 55e47ac9d0
61 changed files with 3727 additions and 2718 deletions

View file

@ -111,6 +111,7 @@ function SerialPort8080(parmsSerial) {
*/
this.fAutoXOFF = true;
this.fAutoStop = false;
this.fNullModem = true;
Component.call(this, "SerialPort", parmsSerial, SerialPort8080, Messages8080.SERIAL);
@ -128,14 +129,15 @@ function SerialPort8080(parmsSerial) {
* No connection until initConnection() is called.
*/
this.sDataReceived = "";
this.connection = this.sendData = null;
this.connection = this.sendData = this.updateStatus = null;
/*
* Export all functions required by initConnection(); currently, this is the bare minimum (no flow control yet).
* Export all functions required by initConnection().
*/
this['exports'] = {
'connect': this.initConnection,
'receiveData': this.receiveData
'receiveData': this.receiveData,
'receiveStatus': this.receiveStatus
};
}
@ -466,7 +468,7 @@ SerialPort8080.prototype.initBus = function(cmp, bus, cpu, dbg)
};
/**
* initConnection()
* initConnection(fNullModem)
*
* If a machine 'connection' parameter exists of the form "{sourcePort}->{targetMachine}.{targetPort}",
* and "{sourcePort}" matches our idComponent, then look for a component with id "{targetMachine}.{targetPort}".
@ -474,6 +476,7 @@ SerialPort8080.prototype.initBus = function(cmp, bus, cpu, dbg)
* If the target component is found, then verify that it has exported functions with the following names:
*
* receiveData(data): called when we have data to transmit; aliased internally to sendData(data)
* receiveStatus(pins): called when our control signals have changed; aliased internally to updateStatus(pins)
*
* For now, we're not going to worry about communication in the other direction, because when the target component
* performs its own initConnection(), it will find our receiveData() function, at which point communication in both
@ -484,8 +487,9 @@ SerialPort8080.prototype.initBus = function(cmp, bus, cpu, dbg)
* if we've already initialized, no harm done.
*
* @this {SerialPort8080}
* @param {boolean} [fNullModem] (caller's null-modem setting, to ensure our settings are in agreement)
*/
SerialPort8080.prototype.initConnection = function()
SerialPort8080.prototype.initConnection = function(fNullModem)
{
if (!this.connection) {
var sConnection = this.cmp.getMachineParm("connection");
@ -500,9 +504,11 @@ SerialPort8080.prototype.initConnection = function()
var exports = this.connection['exports'];
if (exports) {
var fnConnect = exports['connect'];
if (fnConnect) fnConnect.call(this.connection);
if (fnConnect) fnConnect.call(this.connection, this.fNullModem);
this.sendData = exports['receiveData'];
if (this.sendData) {
this.fNullModem = fNullModem;
this.updateStatus = exports['receiveStatus'];
this.status(this.idMachine + '.' + sSourceID + " connected to " + sTargetID);
return;
}
@ -535,7 +541,7 @@ SerialPort8080.prototype.powerUp = function(data, fRepower)
* may be not fully resolved until the target machine performs its own initConnection(), which will
* in turn invoke our initConnection() again.
*/
this.initConnection();
this.initConnection(this.fNullModem);
if (!data || !this.restore) {
this.reset();
@ -713,6 +719,22 @@ SerialPort8080.prototype.receiveData = function(data)
return true; // for now, return true regardless, since we're buffering everything anyway
};
/**
* receiveStatus(pins)
*
* NOTE: Prior to the addition of this interface, the DSR bit was initialized set and remained set for the life
* of the machine. It is entirely appropriate that this is the only way the bit can be changed, because it represents
* an external control signal.
*
* @this {SerialPort8080}
* @param {number} pins
*/
SerialPort8080.prototype.receiveStatus = function(pins)
{
this.bStatus &= ~SerialPort8080.UART8251.STATUS.DSR;
if (pins & RS232.DSR.MASK) this.bStatus |= SerialPort8080.UART8251.STATUS.DSR;
};
/**
* transmitByte(b)
*
@ -865,6 +887,23 @@ SerialPort8080.prototype.outControl = function(port, bOut, addrFrom)
this.bMode = bOut;
this.fReady = true;
} else {
/*
* Whenever DTR or RTS changes, we also want to notify any connected machine, via updateStatus().
*/
if (this.updateStatus) {
var delta = (bOut ^ this.bCommand);
if (delta & (SerialPort8080.UART8251.COMMAND.RTS | SerialPort8080.UART8251.COMMAND.DTR)) {
var pins = 0;
if (this.fNullModem) {
pins |= (bOut & SerialPort8080.UART8251.COMMAND.RTS)? RS232.CTS.MASK : 0;
pins |= (bOut & SerialPort8080.UART8251.COMMAND.DTR)? (RS232.DSR.MASK | RS232.CD.MASK): 0;
} else {
pins |= (bOut & SerialPort8080.UART8251.COMMAND.RTS)? RS232.RTS.MASK : 0;
pins |= (bOut & SerialPort8080.UART8251.COMMAND.DTR)? RS232.DTR.MASK : 0;
}
this.updateStatus.call(this.connection, pins);
}
}
this.bCommand = bOut;
if (this.bCommand & SerialPort8080.UART8251.COMMAND.INTERNAL_RESET) {
this.fReady = false;

View file

@ -245,7 +245,7 @@ Mouse.prototype.powerUp = function(data, fRepower)
var componentAdapter = null;
while ((componentAdapter = this.cmp.getMachineComponent(this.sAdapterType, componentAdapter))) {
if (componentAdapter.attachMouse) {
this.componentAdapter = componentAdapter.attachMouse(this.idAdapter, this);
this.componentAdapter = componentAdapter.attachMouse(this.idAdapter, this, this.receiveStatus);
if (this.componentAdapter) {
/*
* It's possible that the SerialPort we've just attached to might want to bring us "up to speed"
@ -351,7 +351,14 @@ Mouse.prototype.initState = function(data)
this.yDelta = data[i++];
this.fButton1 = data[i++]; // FYI, we consider button1 to be the LEFT button
this.fButton2 = data[i++]; // FYI, we consider button2 to be the RIGHT button
this.bMCR = data[i];
this.pins = data[i];
/*
* Convert old UART "MCR" data to new RS-232 "pins" data, in case we're loading an old state;
* detection and conversion relies on the fact that the MCR bits don't overlap with any RS-232 bits.
*/
if (this.pins & (SerialPort.MCR.DTR | SerialPort.MCR.RTS)) {
this.pins = ((this.pins & SerialPort.MCR.DTR)? RS232.DTR.MASK : 0) | ((this.pins & SerialPort.MCR.RTS)? RS232.RTS.MASK : 0);
}
return true;
};
@ -372,7 +379,7 @@ Mouse.prototype.saveState = function()
data[i++] = this.yDelta;
data[i++] = this.fButton1;
data[i++] = this.fButton2;
data[i] = this.bMCR;
data[i] = this.pins;
return data;
};
@ -626,7 +633,7 @@ Mouse.prototype.sendPacket = function(sDiag, xDiag, yDiag)
};
/**
* notifyMCR(bMCR)
* receiveStatus(pins)
*
* The SerialPort notifies us whenever SerialPort.MCR.DTR or SerialPort.MCR.RTS changes.
*
@ -641,20 +648,20 @@ Mouse.prototype.sendPacket = function(sDiag, xDiag, yDiag)
* for sending the identification byte. Right or wrong, this gets the ball rolling for Windows v1.01.
*
* @this {Mouse}
* @param {number} bMCR
* @param {number} pins
*/
Mouse.prototype.notifyMCR = function(bMCR)
Mouse.prototype.receiveStatus = function(pins)
{
var fActive = ((bMCR & (SerialPort.MCR.DTR | SerialPort.MCR.RTS)) == (SerialPort.MCR.DTR | SerialPort.MCR.RTS));
var fActive = ((pins & (RS232.DTR.MASK | RS232.RTS.MASK)) == (RS232.DTR.MASK | RS232.RTS.MASK));
if (fActive) {
if (!this.fActive) {
var fIdentify = false;
if (!(this.bMCR & SerialPort.MCR.RTS)) {
if (!(this.pins & RS232.RTS.MASK)) {
this.reset();
this.printMessage("serial mouse reset");
fIdentify = true;
}
if (!(this.bMCR & SerialPort.MCR.DTR)) {
if (!(this.pins & RS232.DTR.MASK)) {
this.printMessage("serial mouse ID requested");
fIdentify = true;
}
@ -704,7 +711,7 @@ Mouse.prototype.notifyMCR = function(bMCR)
this.setActive(fActive);
}
}
this.bMCR = bMCR;
this.pins = pins;
};
/**

View file

@ -122,6 +122,9 @@ function SerialPort(parmsSerial) {
this.charBOL = parmsSerial['charBOL'];
this.iLogicalCol = 0;
this.bMSRInit = SerialPort.MSR.CTS | SerialPort.MSR.DSR;
this.fNullModem = true;
Component.call(this, "SerialPort", parmsSerial, SerialPort, Messages.SERIAL);
var sBinding = parmsSerial['binding'];
@ -138,14 +141,15 @@ function SerialPort(parmsSerial) {
* No connection until initConnection() is called.
*/
this.sDataReceived = "";
this.connection = this.sendData = null;
this.connection = this.sendData = this.updateStatus = null;
/*
* Export all functions required by initConnection(); currently, this is the bare minimum (no flow control yet).
* Export all functions required by initConnection().
*/
this['exports'] = {
'connect': this.initConnection,
'receiveData': this.receiveData
'receiveData': this.receiveData,
'receiveStatus': this.receiveStatus
};
}
@ -339,35 +343,26 @@ SerialPort.MSR.RLSD = 0x80; // complement of the RLSD (Received Line
SerialPort.SCR = {REG: 7};
/**
* attachMouse(id, mouse)
* attachMouse(id, mouse, fnUpdate)
*
* @this {SerialPort}
* @param {string} id
* @param {Mouse} mouse component
* @return {Component} this or null, based on whether or not the specified ID matches
* @param {Mouse} mouse
* @param {function(number)} fnUpdate
* @return {Component|null}
*/
SerialPort.prototype.attachMouse = function(id, mouse)
SerialPort.prototype.attachMouse = function(id, mouse, fnUpdate)
{
if (id == this.idComponent) {
this.mouse = mouse;
return this;
var component = null;
if (id == this.idComponent && !this.connection) {
this.connection = mouse;
this.updateStatus = fnUpdate;
this.fNullModem = false;
component = this;
}
return null;
return component;
};
/**
* syncMouse()
*
* NOTE: This is probably obsolete, but the Mouse component still might discover a need for it. See Mouse.powerUp().
*
* @this {SerialPort}
*
SerialPort.prototype.syncMouse = function()
{
if (this.mouse) this.mouse.notifyMCR(this.bMCR);
};
*/
/**
* setBinding(sHTMLType, sBinding, control, sValue)
*
@ -471,7 +466,7 @@ SerialPort.prototype.initBus = function(cmp, bus, cpu, dbg)
};
/**
* initConnection()
* initConnection(fNullModem)
*
* If a machine 'connection' parameter exists of the form "{sourcePort}->{targetMachine}.{targetPort}",
* and "{sourcePort}" matches our idComponent, then look for a component with id "{targetMachine}.{targetPort}".
@ -479,6 +474,7 @@ SerialPort.prototype.initBus = function(cmp, bus, cpu, dbg)
* If the target component is found, then verify that it has exported functions with the following names:
*
* receiveData(data): called when we have data to transmit; aliased internally to sendData(data)
* receiveStatus(pins): called when our control signals have changed; aliased internally to updateStatus(pins)
*
* For now, we're not going to worry about communication in the other direction, because when the target component
* performs its own initConnection(), it will find our receiveData() function, at which point communication in both
@ -489,8 +485,9 @@ SerialPort.prototype.initBus = function(cmp, bus, cpu, dbg)
* if we've already initialized, no harm done.
*
* @this {SerialPort}
* @param {boolean} [fNullModem] (caller's null-modem setting, to ensure our settings are in agreement)
*/
SerialPort.prototype.initConnection = function()
SerialPort.prototype.initConnection = function(fNullModem)
{
if (!this.connection) {
var sConnection = this.cmp.getMachineParm("connection");
@ -505,9 +502,11 @@ SerialPort.prototype.initConnection = function()
var exports = this.connection['exports'];
if (exports) {
var fnConnect = exports['connect'];
if (fnConnect) fnConnect.call(this.connection);
if (fnConnect) fnConnect.call(this.connection, this.fNullModem);
this.sendData = exports['receiveData'];
if (this.sendData) {
this.fNullModem = fNullModem;
this.updateStatus = exports['receiveStatus'];
this.status(this.idMachine + '.' + sSourceID + " connected to " + sTargetID);
return;
}
@ -540,7 +539,7 @@ SerialPort.prototype.powerUp = function(data, fRepower)
* may be not fully resolved until the target machine performs its own initConnection(), which will
* in turn invoke our initConnection() again.
*/
this.initConnection();
this.initConnection(this.fNullModem);
if (!data || !this.restore) {
this.reset();
@ -628,7 +627,7 @@ SerialPort.prototype.initState = function(data)
0, // LCR
0, // MCR
SerialPort.LSR.THRE | SerialPort.LSR.TSRE, // LSR
SerialPort.MSR.CTS | SerialPort.MSR.DSR, // MSR (instead of the normal 0 default, we indicate a state of readiness -- to be revisited)
this.bMSRInit, // MSR
[]
];
}
@ -696,6 +695,29 @@ SerialPort.prototype.receiveData = function(data)
return true; // for now, return true regardless, since we're buffering everything anyway
};
/**
* receiveStatus(pins)
*
* NOTE: Prior to the addition of this interface, the CTS and DSR bits were initialized set and remained set for the life
* of the machine. It is entirely appropriate that this is the only way those bits can be changed, because they represent
* external control signals.
*
* @this {SerialPort}
* @param {number} pins
*/
SerialPort.prototype.receiveStatus = function(pins)
{
var bMSROld = this.bMSR;
this.bMSR &= ~(SerialPort.MSR.CTS | SerialPort.MSR.DSR);
if (pins & RS232.CTS.MASK) {
this.bMSR |= SerialPort.MSR.CTS | SerialPort.MSR.DCTS;
}
if (pins & RS232.DSR.MASK) {
this.bMSR |= SerialPort.MSR.DSR | SerialPort.MSR.DDSR;
}
if (bMSROld != this.bMSR) this.updateIRR();
};
/**
* advanceRBR()
*
@ -714,7 +736,7 @@ SerialPort.prototype.advanceRBR = function()
* inRBR(port, addrFrom)
*
* @this {SerialPort}
* @param {number} port (0x3F8 or 0x2F8)
* @param {number} port (eg, 0x3F8 or 0x2F8)
* @param {number} [addrFrom] (not defined whenever the Debugger tries to read the specified port)
* @return {number} simulated port value
*/
@ -731,7 +753,7 @@ SerialPort.prototype.inRBR = function(port, addrFrom)
* inIER(port, addrFrom)
*
* @this {SerialPort}
* @param {number} port (0x3F9 or 0x2F9)
* @param {number} port (eg, 0x3F9 or 0x2F9)
* @param {number} [addrFrom] (not defined whenever the Debugger tries to read the specified port)
* @return {number} simulated port value
*/
@ -746,7 +768,7 @@ SerialPort.prototype.inIER = function(port, addrFrom)
* inIIR(port, addrFrom)
*
* @this {SerialPort}
* @param {number} port (0x3FA or 0x2FA)
* @param {number} port (eg, 0x3FA or 0x2FA)
* @param {number} [addrFrom] (not defined whenever the Debugger tries to read the specified port)
* @return {number} simulated port value
*/
@ -761,7 +783,7 @@ SerialPort.prototype.inIIR = function(port, addrFrom)
* inLCR(port, addrFrom)
*
* @this {SerialPort}
* @param {number} port (0x3FB or 0x2FB)
* @param {number} port (eg, 0x3FB or 0x2FB)
* @param {number} [addrFrom] (not defined whenever the Debugger tries to read the specified port)
* @return {number} simulated port value
*/
@ -776,7 +798,7 @@ SerialPort.prototype.inLCR = function(port, addrFrom)
* inMCR(port, addrFrom)
*
* @this {SerialPort}
* @param {number} port (0x3FC or 0x2FC)
* @param {number} port (eg, 0x3FC or 0x2FC)
* @param {number} [addrFrom] (not defined whenever the Debugger tries to read the specified port)
* @return {number} simulated port value
*/
@ -791,7 +813,7 @@ SerialPort.prototype.inMCR = function(port, addrFrom)
* inLSR(port, addrFrom)
*
* @this {SerialPort}
* @param {number} port (0x3FD or 0x2FD)
* @param {number} port (eg, 0x3FD or 0x2FD)
* @param {number} [addrFrom] (not defined whenever the Debugger tries to read the specified port)
* @return {number} simulated port value
*/
@ -806,13 +828,14 @@ SerialPort.prototype.inLSR = function(port, addrFrom)
* inMSR(port, addrFrom)
*
* @this {SerialPort}
* @param {number} port (0x3FE or 0x2FE)
* @param {number} port (eg, 0x3FE or 0x2FE)
* @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)
{
var b = this.bMSR;
this.bMSR &= ~(SerialPort.MSR.DCTS | SerialPort.MSR.DDSR);
this.printMessageIO(port, null, addrFrom, "MSR", b);
return b;
};
@ -821,7 +844,7 @@ SerialPort.prototype.inMSR = function(port, addrFrom)
* outTHR(port, bOut, addrFrom)
*
* @this {SerialPort}
* @param {number} port (0x3F8 or 0x2F8)
* @param {number} port (eg, 0x3F8 or 0x2F8)
* @param {number} bOut
* @param {number} [addrFrom] (not defined whenever the Debugger tries to write the specified port)
*/
@ -846,7 +869,7 @@ SerialPort.prototype.outTHR = function(port, bOut, addrFrom)
* outIER(port, bOut, addrFrom)
*
* @this {SerialPort}
* @param {number} port (0x3F9 or 0x2F9)
* @param {number} port (eg, 0x3F9 or 0x2F9)
* @param {number} bOut
* @param {number} [addrFrom] (not defined whenever the Debugger tries to write the specified port)
*/
@ -864,7 +887,7 @@ SerialPort.prototype.outIER = function(port, bOut, addrFrom)
* outLCR(port, bOut, addrFrom)
*
* @this {SerialPort}
* @param {number} port (0x3FB or 0x2FB)
* @param {number} port (eg, 0x3FB or 0x2FB)
* @param {number} bOut
* @param {number} [addrFrom] (not defined whenever the Debugger tries to write the specified port)
*/
@ -878,17 +901,30 @@ SerialPort.prototype.outLCR = function(port, bOut, addrFrom)
* outMCR(port, bOut, addrFrom)
*
* @this {SerialPort}
* @param {number} port (0x3FC or 0x2FC)
* @param {number} port (eg, 0x3FC or 0x2FC)
* @param {number} bOut
* @param {number} [addrFrom] (not defined whenever the Debugger tries to write the specified port)
*/
SerialPort.prototype.outMCR = function(port, bOut, addrFrom)
{
var bPrev = this.bMCR;
var delta = (bOut ^ this.bMCR);
this.printMessageIO(port, bOut, addrFrom, "MCR");
this.bMCR = bOut;
if (this.mouse && (bPrev ^ bOut) & (SerialPort.MCR.DTR | SerialPort.MCR.RTS)) {
this.mouse.notifyMCR(this.bMCR);
/*
* Whenever DTR or RTS changes, we also need to notify any connected machine or mouse, via updateStatus().
*/
if (delta & (SerialPort.MCR.DTR | SerialPort.MCR.RTS)) {
if (this.updateStatus) {
var pins = 0;
if (this.fNullModem) {
pins |= (bOut & SerialPort.MCR.RTS)? RS232.CTS.MASK : 0;
pins |= (bOut & SerialPort.MCR.DTR)? (RS232.DSR.MASK | RS232.CD.MASK): 0;
} else {
pins |= (bOut & SerialPort.MCR.RTS)? RS232.RTS.MASK : 0;
pins |= (bOut & SerialPort.MCR.DTR)? RS232.DTR.MASK : 0;
}
this.updateStatus.call(this.connection, pins);
}
}
};
@ -903,6 +939,9 @@ SerialPort.prototype.updateIRR = function()
if ((this.bLSR & SerialPort.LSR.DR) && (this.bIER & SerialPort.IER.RBR_AVAIL)) {
bIIR = SerialPort.IIR.INT_RBR;
}
else if ((this.bMSR & (SerialPort.MSR.DCTS | SerialPort.MSR.DDSR)) && (this.bIER & SerialPort.IER.MSR_DELTA)) {
bIIR = SerialPort.IIR.INT_MSR;
}
if (bIIR >= 0) {
this.bIIR &= ~(SerialPort.IIR.NO_INT | SerialPort.IIR.INT_BITS);
this.bIIR |= bIIR;

View file

@ -913,6 +913,55 @@ BusPDP11.prototype.getByte = function(addr)
return this.aMemBlocks[(addr & this.nMemMask) >>> this.nBlockShift].readByte(addr & this.nBlockLimit, addr);
};
/**
* getWord(addr)
*
* @this {BusPDP11}
* @param {number} addr is a physical address
* @return {number} word (16-bit) value at that address
*/
BusPDP11.prototype.getWord = function(addr)
{
var off = addr & this.nBlockLimit;
var iBlock = (addr & this.nMemMask) >>> this.nBlockShift;
if (!PDP11.WORDBUS && off == this.nBlockLimit) {
return this.aMemBlocks[iBlock++].readByte(off, addr) | (this.aMemBlocks[iBlock & this.nBlockMask].readByte(0, addr + 1) << 8);
}
return this.aMemBlocks[iBlock].readWord(off, addr);
};
/**
* setByte(addr, b)
*
* @this {BusPDP11}
* @param {number} addr is a physical address
* @param {number} b is the byte (8-bit) value to write
*/
BusPDP11.prototype.setByte = function(addr, b)
{
this.assert(!(b & ~0xff));
this.aMemBlocks[(addr & this.nMemMask) >>> this.nBlockShift].writeByte(addr & this.nBlockLimit, b, addr);
};
/**
* setWord(addr, w)
*
* @this {BusPDP11}
* @param {number} addr is a physical address
* @param {number} w is the word (16-bit) value to write
*/
BusPDP11.prototype.setWord = function(addr, w)
{
var off = addr & this.nBlockLimit;
var iBlock = (addr & this.nMemMask) >>> this.nBlockShift;
if (!PDP11.WORDBUS && off == this.nBlockLimit) {
this.aMemBlocks[iBlock++].writeByte(off, w & 0xff, addr);
this.aMemBlocks[iBlock & this.nBlockMask].writeByte(0, (w >> 8) & 0xff, addr + 1);
return;
}
this.aMemBlocks[iBlock].writeWord(off, w, addr);
};
/**
* getBlockDirect(addr)
*
@ -943,23 +992,6 @@ BusPDP11.prototype.getByteDirect = function(addr)
return b;
};
/**
* getWord(addr)
*
* @this {BusPDP11}
* @param {number} addr is a physical address
* @return {number} word (16-bit) value at that address
*/
BusPDP11.prototype.getWord = function(addr)
{
var off = addr & this.nBlockLimit;
var iBlock = (addr & this.nMemMask) >>> this.nBlockShift;
if (!PDP11.WORDBUS && off == this.nBlockLimit) {
return this.aMemBlocks[iBlock++].readByte(off, addr) | (this.aMemBlocks[iBlock & this.nBlockMask].readByte(0, addr + 1) << 8);
}
return this.aMemBlocks[iBlock].readWord(off, addr);
};
/**
* getWordDirect(addr)
*
@ -985,18 +1017,6 @@ BusPDP11.prototype.getWordDirect = function(addr)
return w;
};
/**
* setByte(addr, b)
*
* @this {BusPDP11}
* @param {number} addr is a physical address
* @param {number} b is the byte (8-bit) value to write (we truncate it to 8 bits to be safe)
*/
BusPDP11.prototype.setByte = function(addr, b)
{
this.aMemBlocks[(addr & this.nMemMask) >>> this.nBlockShift].writeByte(addr & this.nBlockLimit, b & 0xff, addr);
};
/**
* setByteDirect(addr, b)
*
@ -1014,25 +1034,6 @@ BusPDP11.prototype.setByteDirect = function(addr, b)
this.nDisableFaults--;
};
/**
* setWord(addr, w)
*
* @this {BusPDP11}
* @param {number} addr is a physical address
* @param {number} w is the word (16-bit) value to write (we truncate it to 16 bits to be safe)
*/
BusPDP11.prototype.setWord = function(addr, w)
{
var off = addr & this.nBlockLimit;
var iBlock = (addr & this.nMemMask) >>> this.nBlockShift;
if (!PDP11.WORDBUS && off == this.nBlockLimit) {
this.aMemBlocks[iBlock++].writeByte(off, w & 0xff, addr);
this.aMemBlocks[iBlock & this.nBlockMask].writeByte(0, (w >> 8) & 0xff, addr + 1);
return;
}
this.aMemBlocks[iBlock].writeWord(off, w & 0xffff, addr);
};
/**
* setWordDirect(addr, w)
*
@ -1236,20 +1237,21 @@ BusPDP11.prototype.addIOHandlers = function(start, end, fnReadByte, fnWriteByte,
};
/**
* addIOTable(component, table)
* addIOTable(component, table, offReg)
*
* Add I/O notification handlers from the specified table (a batch version of addIOHandlers).
*
* @this {BusPDP11}
* @param {Component} component
* @param {Object} table
* @param {number} [offReg] (optional offset to add to all register addresses)
* @return {boolean} (true if entire range successfully registered, false if any conflicts)
*/
BusPDP11.prototype.addIOTable = function(component, table)
BusPDP11.prototype.addIOTable = function(component, table, offReg)
{
for (var port in table) {
var addr = +port;
var afn = table[port];
for (var reg in table) {
var addr = +reg + (offReg || 0);
var afn = table[reg];
/*
* Don't install (ie, ignore) handlers for I/O addresses that are defined with a model number

View file

@ -206,6 +206,17 @@ CPUPDP11.prototype.initBus = function(cmp, bus, cpu, dbg)
this.setReady();
};
/**
* finish()
*
* Stub for final initialization call (overridden by the CPUStatePDP11 component).
*
* @this {CPUPDP11}
*/
CPUPDP11.prototype.finish = function()
{
};
/**
* reset()
*
@ -255,6 +266,7 @@ CPUPDP11.prototype.restore = function(data)
CPUPDP11.prototype.powerUp = function(data, fRepower)
{
if (!fRepower) {
this.finish();
if (!data || !this.restore) {
this.reset();
} else {

View file

@ -875,7 +875,7 @@ PDP11.opBVS = function(opCode)
*/
PDP11.opCLR = function(opCode)
{
this.updateAllFlags(this.writeDstWord(opCode, 0));
this.writeDstWord(opCode, 0, this.updateAllFlags);
this.nStepCycles -= (this.dstMode? (8 + 1) : (2 + 1) + (this.dstReg == 7? 2 : 0));
};
@ -887,7 +887,7 @@ PDP11.opCLR = function(opCode)
*/
PDP11.opCLRB = function(opCode)
{
this.updateAllFlags(this.writeDstByte(opCode, 0, PDP11.WRITE.BYTE));
this.writeDstByte(opCode, 0, PDP11.WRITE.BYTE, this.updateAllFlags);
this.nStepCycles -= (this.dstMode? (8 + 1) : (2 + 1) + (this.dstReg == 7? 2 : 0));
};
@ -1283,8 +1283,8 @@ PDP11.opMARK = function(opCode)
PDP11.opMFPD = function(opCode)
{
var data = this.readWordFromPrevSpace(opCode, PDP11.ACCESS.DSPACE);
this.pushWord(data);
this.updateNZVFlags(data);
this.pushWord(data);
this.nStepCycles -= (10 + 1);
};
@ -1297,8 +1297,8 @@ PDP11.opMFPD = function(opCode)
PDP11.opMFPI = function(opCode)
{
var data = this.readWordFromPrevSpace(opCode, PDP11.ACCESS.ISPACE);
this.pushWord(data);
this.updateNZVFlags(data);
this.pushWord(data);
this.nStepCycles -= (10 + 1);
};
@ -1346,7 +1346,7 @@ PDP11.opMOV = function(opCode)
*/
var data = this.readSrcWord(opCode);
this.nSnapCycles = this.nStepCycles;
this.updateNZVFlags(this.writeDstWord(opCode, data));
this.writeDstWord(opCode, data, this.updateNZVFlags);
this.nStepCycles = this.nSnapCycles - PDP11.MOV_CYCLES[(this.srcMode? 8 : 0) + this.dstMode] + (this.dstReg == 7 && !this.dstMode? 2 : 0);
};
@ -1359,7 +1359,7 @@ PDP11.opMOV = function(opCode)
PDP11.opMOVB = function(opCode)
{
var data = this.readSrcByte(opCode);
this.updateNZVFlags(this.writeDstByte(opCode, data, PDP11.WRITE.SBYTE) << 8);
this.writeDstByte(opCode, data, PDP11.WRITE.SBYTE, this.updateNZVFlags);
this.nStepCycles -= (this.dstMode? (8 + 1) + (this.srcReg && this.dstReg >= 6? 1 : 0) : (this.srcMode? (3 + 2) : (2 + 1)) + (this.dstReg == 7? 2 : 0));
};
@ -1381,8 +1381,8 @@ PDP11.opMTPD = function(opCode)
*/
var data = this.popWord();
this.nSnapCycles = this.nStepCycles;
this.writeWordToPrevSpace(opCode, PDP11.ACCESS.DSPACE, data);
this.updateNZVFlags(data);
this.writeWordToPrevSpace(opCode, PDP11.ACCESS.DSPACE, data);
this.nStepCycles = this.nSnapCycles - PDP11.MTP_CYCLES[this.dstMode];
};
@ -1400,8 +1400,8 @@ PDP11.opMTPI = function(opCode)
*/
var data = this.popWord();
this.nSnapCycles = this.nStepCycles;
this.writeWordToPrevSpace(opCode, PDP11.ACCESS.ISPACE, data);
this.updateNZVFlags(data);
this.writeWordToPrevSpace(opCode, PDP11.ACCESS.ISPACE, data);
this.nStepCycles = this.nSnapCycles - PDP11.MTP_CYCLES[this.dstMode];
};
@ -1775,7 +1775,7 @@ PDP11.opSWAB = function(opCode)
*/
PDP11.opSXT = function(opCode)
{
this.updateNZVFlags(this.writeDstWord(opCode, this.getNF()? 0xffff : 0));
this.writeDstWord(opCode, this.getNF()? 0xffff : 0, this.updateNZVFlags);
this.nStepCycles -= (this.dstMode? (8 + 1) : (2 + 1) + (this.dstReg == 7? 2 : 0));
};

View file

@ -149,16 +149,35 @@ CPUStatePDP11.prototype.initProcessor = function()
this.nDisableTraps = 0;
/** @type {IRQ|null} */
this.irqNext = null; // the head of the active IRQ list, in priority order
this.irqNext = null; // the head of the active IRQ list, in priority order
if (DEBUG) {
/** @type {Array.<IRQ>} */
this.aIRQs = []; // list of all IRQs, active or not (just for debugging)
}
/** @type {Array.<IRQ>} */
this.aIRQs = []; // list of all IRQs, active or not (to be used for auto-configuration)
this.flags.complete = false;
};
/**
* finish()
*
* TODO: This function simply ensures that we don't leave any IRQs installed with unresolved floating
* (negative) vectors; properly assigning vectors according to device type (ie, auto-configuration) is an
* exercise left for another day.
*
* @this {CPUStatePDP11}
*/
CPUStatePDP11.prototype.finish = function()
{
var vectorFloating = 0o300;
for (var i = 0; i < this.aIRQs.length; i++) {
var irq = this.aIRQs[i];
if (irq.vector < 0) {
irq.vector = vectorFloating;
vectorFloating += 4;
}
}
};
/**
* reset()
*
@ -170,7 +189,7 @@ CPUStatePDP11.prototype.reset = function()
if (this.flags.running) this.stopCPU();
this.initRegs();
this.resetCycles();
this.clearError(); // clear any fatal error/exception that setError() may have flagged
this.clearError(); // clear any fatal error/exception that setError() may have flagged
this.parent.reset.call(this);
};
@ -264,7 +283,7 @@ CPUStatePDP11.prototype.resetMMU = function()
this.regMMR1 = 0; // 177574
this.regMMR2 = 0; // 177576
this.regMMR3 = 0; // 172516
this.regErr = 0; // 177766 TODO: Do we ever need to clear this, because it appears to accumulate errors...
this.regErr = 0; // 177766 TODO: Do we ever need to automatically clear this, or is it manually cleared?
this.regPIR = 0; // 177772
this.regSL = 0xff; // 177774
this.mmuEnable = 0; // MMU enabled for PDP11.ACCESS.READ or PDP11.ACCESS.WRITE
@ -774,7 +793,7 @@ CPUStatePDP11.prototype.setSP = function(addr)
* addIRQ(vector, priority, message)
*
* @this {CPUStatePDP11}
* @param {number} vector
* @param {number} vector (-1 for floating vector)
* @param {number} priority
* @param {number} [message]
* @return {IRQ}
@ -782,10 +801,8 @@ CPUStatePDP11.prototype.setSP = function(addr)
CPUStatePDP11.prototype.addIRQ = function(vector, priority, message)
{
var irq = {vector: vector, priority: priority, message: message || 0, next: null};
if (DEBUG) {
irq.name = PDP11.VECTORS[vector];
this.aIRQs.push(irq);
}
irq.name = PDP11.VECTORS[vector];
this.aIRQs.push(irq);
return irq;
};
@ -1171,10 +1188,8 @@ CPUStatePDP11.prototype.setPIR = function(newPIR)
*/
CPUStatePDP11.prototype.updateNZVFlags = function(result)
{
if (!(this.opFlags & PDP11.OPFLAG.NO_FLAGS)) {
this.flagN = this.flagZ = result;
this.flagV = 0;
}
this.flagN = this.flagZ = result;
this.flagV = 0;
};
/**
@ -1187,10 +1202,8 @@ CPUStatePDP11.prototype.updateNZVFlags = function(result)
*/
CPUStatePDP11.prototype.updateNZVCFlags = function(result)
{
if (!(this.opFlags & PDP11.OPFLAG.NO_FLAGS)) {
this.flagN = this.flagZ = result;
this.flagV = this.flagC = 0;
}
this.flagN = this.flagZ = result;
this.flagV = this.flagC = 0;
};
/**
@ -1204,10 +1217,8 @@ CPUStatePDP11.prototype.updateNZVCFlags = function(result)
*/
CPUStatePDP11.prototype.updateAllFlags = function(result, overflow)
{
if (!(this.opFlags & PDP11.OPFLAG.NO_FLAGS)) {
this.flagN = this.flagZ = this.flagC = result;
this.flagV = overflow || 0;
}
this.flagN = this.flagZ = this.flagC = result;
this.flagV = overflow || 0;
};
/**
@ -1220,10 +1231,8 @@ CPUStatePDP11.prototype.updateAllFlags = function(result, overflow)
*/
CPUStatePDP11.prototype.updateAddFlags = function(result, src, dst)
{
if (!(this.opFlags & PDP11.OPFLAG.NO_FLAGS)) {
this.flagN = this.flagZ = this.flagC = result;
this.flagV = (src ^ result) & (dst ^ result);
}
this.flagN = this.flagZ = this.flagC = result;
this.flagV = (src ^ result) & (dst ^ result);
};
/**
@ -1237,11 +1246,11 @@ CPUStatePDP11.prototype.updateAddFlags = function(result, src, dst)
*/
CPUStatePDP11.prototype.updateDecFlags = function(result, dst)
{
if (!(this.opFlags & PDP11.OPFLAG.NO_FLAGS)) {
this.flagN = this.flagZ = result;
// Because src is always 1 (with a zero sign bit), it can be optimized out of this calculation
this.flagV = (/* src ^ */ dst) & (dst ^ result);
}
this.flagN = this.flagZ = result;
/*
* Because src is always 1 (with a zero sign bit), it can be optimized out of this calculation.
*/
this.flagV = (/* src ^ */ dst) & (dst ^ result);
};
/**
@ -1255,11 +1264,11 @@ CPUStatePDP11.prototype.updateDecFlags = function(result, dst)
*/
CPUStatePDP11.prototype.updateIncFlags = function(result, dst)
{
if (!(this.opFlags & PDP11.OPFLAG.NO_FLAGS)) {
this.flagN = this.flagZ = result;
// Because src is always 1 (with a zero sign bit), it can be optimized out of this calculation
this.flagV = (/* src ^ */ result) & (dst ^ result);
}
this.flagN = this.flagZ = result;
/*
* Because src is always 1 (with a zero sign bit), it can be optimized out of this calculation.
*/
this.flagV = (/* src ^ */ result) & (dst ^ result);
};
/**
@ -1270,23 +1279,10 @@ CPUStatePDP11.prototype.updateIncFlags = function(result, dst)
*/
CPUStatePDP11.prototype.updateMulFlags = function(result)
{
/*
* NOTE: Technically, the MUL instruction doesn't need to worry about NO_FLAGS, because that instruction
* doesn't write to the bus, and therefore can't modify the PSW directly. But it doesn't hurt to be consistent.
*
* TODO: Conduct a review of all opcode handlers, because one possible alternative to all this NO_FLAGS nonsense
* would be to pass the appropriate flag update function to the writeDstByte()/writeDstWord() functions, and
* have them update the flags BEFORE the write occurs, thus allowing any subsequent write to the PSW to be honored.
*
* That already happens automatically with the updateDstByte()/updateDstWord() functions, since generally the
* specified modify function also updates the flags BEFORE the write occurs.
*/
if (!(this.opFlags & PDP11.OPFLAG.NO_FLAGS)) {
this.flagN = result >> 16;
this.flagZ = this.flagN | result;
this.flagV = 0;
this.flagC = (result < -32768 || result > 32767)? 0x10000 : 0;
}
this.flagN = result >> 16;
this.flagZ = this.flagN | result;
this.flagV = 0;
this.flagC = (result < -32768 || result > 32767)? 0x10000 : 0;
};
/**
@ -1297,10 +1293,8 @@ CPUStatePDP11.prototype.updateMulFlags = function(result)
*/
CPUStatePDP11.prototype.updateShiftFlags = function(result)
{
if (!(this.opFlags & PDP11.OPFLAG.NO_FLAGS)) {
this.flagN = this.flagZ = this.flagC = result;
this.flagV = this.flagN ^ (this.flagC >> 1);
}
this.flagN = this.flagZ = this.flagC = result;
this.flagV = this.flagN ^ (this.flagC >> 1);
};
/**
@ -1316,10 +1310,8 @@ CPUStatePDP11.prototype.updateShiftFlags = function(result)
*/
CPUStatePDP11.prototype.updateSubFlags = function(result, src, dst)
{
if (!(this.opFlags & PDP11.OPFLAG.NO_FLAGS)) {
this.flagN = this.flagZ = this.flagC = result;
this.flagV = (src ^ dst) & (dst ^ result);
}
this.flagN = this.flagZ = this.flagC = result;
this.flagV = (src ^ dst) & (dst ^ result);
};
/**
@ -1351,47 +1343,50 @@ CPUStatePDP11.prototype.trap = function(vector, flag, reason)
reason = PDP11.REASON.RED; // double-fault (nested trap) forces a RED condition
}
var fRed = false;
if (reason == PDP11.REASON.RED) {
vector = 4;
if (this.opFlags & PDP11.OPFLAG.TRAP_RED) {
reason = PDP11.REASON.PANIC;
}
this.opFlags |= PDP11.OPFLAG.TRAP_RED;
/*
* The next two lines used to be deferred until after the setPSW() below, but
* I'm not seeing any dependencies on these registers, so I'm consolidating the code.
*/
this.regErr |= PDP11.CPUERR.RED;
this.regsGen[6] = 4;
fRed = true;
this.regsGen[6] = vector = 4;
}
/*
* NOTE: Pre-setting the auto-dec values for MMR1 to 0xF6F6 is a work-around for an "EKBEE1"
* diagnostic (PC 056710), which tests what happens when a misaligned read triggers a BUS trap,
* and that trap then triggers an MMU trap during the first pushWord() below.
*
* One would think it would be fine to zero those bits by setting opLast to vector alone,
* and then letting each of the pushWord() calls below shift their own 0xF6 auto-dec value into
* opLast. When the first pushWord() triggers an MMU trap, we obviously won't get to the second
* pushWord(), yet the diagnostic expects TWO auto-decs to be recorded. I'm puzzled why the
* hardware apparently indicates TWO auto-decs, if SP wasn't actually decremented twice, but who
* am I to judge.
*/
this.opLast = vector | 0xf6f60000;
if (reason != PDP11.REASON.PANIC) {
/*
* NOTE: Pre-setting the auto-dec values for MMR1 to 0xF6F6 is a work-around for an "EKBEE1"
* diagnostic (PC 056710), which tests what happens when a misaligned read triggers a BUS trap,
* and that trap then triggers an MMU trap during the first pushWord() below.
*
* One would think it would be fine to zero those bits by setting opLast to vector alone,
* and then letting each of the pushWord() calls below shift their own 0xF6 auto-dec value into
* opLast. When the first pushWord() triggers an MMU trap, we obviously won't get to the second
* pushWord(), yet the diagnostic expects TWO auto-decs to be recorded. I'm puzzled why the
* hardware apparently indicates TWO auto-decs, if SP wasn't actually decremented twice, but who
* am I to judge.
*/
this.opLast = vector | 0xf6f60000;
/*
* Read from kernel D space
*/
this.mmuMode = 0;
var newPC = this.readWord(vector | this.addrDSpace);
var newPSW = this.readWord(((vector + 2) & 0xffff) | this.addrDSpace);
/*
* Read from kernel D space
*/
this.mmuMode = 0;
var newPC = this.readWord(vector | this.addrDSpace);
var newPSW = this.readWord(((vector + 2) & 0xffff) | this.addrDSpace);
/*
* Set new PSW with previous mode
*/
this.setPSW((newPSW & ~PDP11.PSW.PMODE) | ((this.trapPSW >> 2) & PDP11.PSW.PMODE));
/*
* Set new PSW with previous mode
*/
this.setPSW((newPSW & ~PDP11.PSW.PMODE) | ((this.trapPSW >> 2) & PDP11.PSW.PMODE));
this.pushWord(this.trapPSW, fRed);
this.pushWord(this.regsGen[7], fRed);
this.setPC(newPC);
this.pushWord(this.trapPSW);
this.pushWord(this.regsGen[7]);
this.setPC(newPC);
}
/*
* TODO: Determine the appropriate number of cycles for traps; all I've done for now is move the
@ -1442,6 +1437,9 @@ CPUStatePDP11.prototype.trap = function(vector, flag, reason)
this.trapVector = vector;
this.trapReason = reason;
if (reason == PDP11.REASON.PANIC) {
this.stopCPU();
}
if (reason >= PDP11.REASON.RED) throw vector;
};
@ -1773,18 +1771,17 @@ CPUStatePDP11.prototype.popWord = function()
};
/**
* pushWord(data, fRed)
* pushWord(data)
*
* @this {CPUStatePDP11}
* @param {number} data
* @param {boolean} [fRed]
*/
CPUStatePDP11.prototype.pushWord = function(data, fRed)
CPUStatePDP11.prototype.pushWord = function(data)
{
var addrVirtual = (this.regsGen[6] - 2) & 0xffff;
this.regsGen[6] = addrVirtual; // BSD needs SP updated before any fault :-(
this.opLast = (this.opLast & 0xffff) | ((this.opLast & ~0xffff) << 8) | (0x00f6 << 16);
if (!fRed) this.checkStackLimit(PDP11.ACCESS.WRITE_WORD, -2, addrVirtual);
if (!(this.opFlags & PDP11.OPFLAG.TRAP_RED)) this.checkStackLimit(PDP11.ACCESS.WRITE_WORD, -2, addrVirtual);
this.writeWord(addrVirtual, data);
};
@ -2152,7 +2149,8 @@ CPUStatePDP11.prototype.readWordFromVirtual = function(addrVirtual)
CPUStatePDP11.prototype.writeWordToPhysical = function(addr, data)
{
if (addr >= BusPDP11.UNIBUS_22BIT) addr = this.mapUnibus(addr);
this.bus.setWord(this.addrLast = addr, data & 0xffff);
this.assert(!(data & ~0xffff));
this.bus.setWord(this.addrLast = addr, data);
};
/**
@ -2397,10 +2395,6 @@ CPUStatePDP11.prototype.updateDstWord = function(opCode, data, fnOp)
var reg = this.dstReg = opCode & PDP11.OPREG.MASK;
var mode = this.dstMode = (opCode & PDP11.OPMODE.MASK) >> PDP11.OPMODE.SHIFT;
/*
* TODO: If callers are careful about masking data, then we don't need to mask it here or in bus.setWord().
* We've eliminated the 0xffff data mask here, but bus.setWord() is still masking.
*/
this.assert(data < 0 && data >= -8 || !(data & ~0xffff));
if (!mode) {
@ -2412,7 +2406,7 @@ CPUStatePDP11.prototype.updateDstWord = function(opCode, data, fnOp)
};
/**
* writeDstByte(opCode, data, writeFlags)
* writeDstByte(opCode, data, writeFlags, fnFlags)
*
* Used whenever the DST operand (as described by opCode) does NOT need to be read before writing.
*
@ -2420,9 +2414,9 @@ CPUStatePDP11.prototype.updateDstWord = function(opCode, data, fnOp)
* @param {number} opCode
* @param {number} data
* @param {number} writeFlags (WRITE.BYTE aka 0xff, or WRITE.SBYTE aka 0xffff)
* @return {number}
* @param {function(number)} fnFlags
*/
CPUStatePDP11.prototype.writeDstByte = function(opCode, data, writeFlags)
CPUStatePDP11.prototype.writeDstByte = function(opCode, data, writeFlags, fnFlags)
{
this.assert(writeFlags);
var reg = this.dstReg = opCode & PDP11.OPREG.MASK;
@ -2441,41 +2435,39 @@ CPUStatePDP11.prototype.writeDstByte = function(opCode, data, writeFlags)
data = (data < 0? (this.regsGen[-data-1] & 0xff): data);
this.regsGen[reg] = (this.regsGen[reg] & ~writeFlags) | (((data << 24) >> 24) & writeFlags);
}
fnFlags.call(this, data << 8);
} else {
var addr = this.getAddr(mode, reg, PDP11.ACCESS.WRITE_BYTE);
this.writeByteToPhysical(addr, (data = data < 0? (this.regsGen[-data-1] & 0xff): data));
fnFlags.call(this, (data = data < 0? (this.regsGen[-data-1] & 0xff) : data) << 8);
this.writeByteToPhysical(addr, data);
}
return data;
};
/**
* writeDstWord(opCode, data)
* writeDstWord(opCode, data, fnFlags)
*
* Used whenever the DST operand (as described by opCode) does NOT need to be read before writing.
*
* @this {CPUStatePDP11}
* @param {number} opCode
* @param {number} data
* @return {number}
* @param {function(number)} fnFlags
*/
CPUStatePDP11.prototype.writeDstWord = function(opCode, data)
CPUStatePDP11.prototype.writeDstWord = function(opCode, data, fnFlags)
{
var reg = this.dstReg = opCode & PDP11.OPREG.MASK;
var mode = this.dstMode = (opCode & PDP11.OPMODE.MASK) >> PDP11.OPMODE.SHIFT;
/*
* TODO: If callers are careful about masking data, then we don't need to mask it here or in bus.setWord().
* We've eliminated the 0xffff data mask here, but bus.setWord() is still masking.
*/
this.assert(data < 0 && data >= -8 || !(data & ~0xffff));
if (!mode) {
this.regsGen[reg] = (data = (data < 0? this.regsGen[-data-1] : data));
this.regsGen[reg] = (data = data < 0? this.regsGen[-data-1] : data);
fnFlags.call(this, data);
} else {
var addr = this.getAddr(mode, reg, PDP11.ACCESS.WRITE_WORD);
this.bus.setWord(addr, (data = (data < 0? this.regsGen[-data-1] : data)));
fnFlags.call(this, (data = data < 0? this.regsGen[-data-1] : data));
this.bus.setWord(addr, data);
}
return data;
};
/**
@ -2606,6 +2598,7 @@ CPUStatePDP11.prototype.stepCPU = function(nMinCycles)
} else {
this.assert(!this.irqNext && !this.regPIR);
}
/*
* Snapshot the TF bit in opFlags, while clearing all other opFlags (except those in PRESERVE);
* we'll check the TRAP_TF bit in opFlags when we come back around for another opcode.

View file

@ -219,18 +219,20 @@ var PDP11 = {
* immediate (thrown) traps, as they are considered ABORTs; the rest generate synchronous traps.
*/
REASON: {
ABORT: -1, // immediate MMU fault
ILLEGAL: -2, // immediate invalid opcode (BUS)
RED: -3, // immediate stack overflow fault (BUS)
YELLOW: -4, // deferred stack overflow fault (BUS)
FAULT: -5, // deferred MMU fault
TRACE: -6, // deferred TF fault (BPT)
HALT: -7, // illegal HALT (BUS)
OPCODE: -8, // opcode-generated trap (eg, BPT, EMT, IOT, TRAP, or RESERVED opcode)
INTERRUPT: -9, // device-generated trap (vector is device-specific)
PANIC: -1, // immediate halt (internal error)
ABORT: -2, // immediate MMU fault
ILLEGAL: -3, // immediate invalid opcode (BUS)
RED: -4, // immediate stack overflow fault (BUS)
YELLOW: -5, // deferred stack overflow fault (BUS)
FAULT: -6, // deferred MMU fault
TRACE: -7, // deferred TF fault (BPT)
HALT: -8, // illegal HALT (BUS)
OPCODE: -9, // opcode-generated trap (eg, BPT, EMT, IOT, TRAP, or RESERVED opcode)
INTERRUPT: -10, // device-generated trap (vector is device-specific)
},
REASONS: [
"UNKNOWN",
"PANIC",
"ABORT",
"ILLEGAL",
"RED",
@ -271,7 +273,7 @@ var PDP11 = {
TRAP_MMU: 0x0040,
TRAP_MASK: 0x0070,
TRAP_LAST: 0x0080, // set if last operation was a trap (see trapLast for the vector, and trapReason for the reason)
NO_FLAGS: 0x0100 // set whenever the PSW is written directly, requiring all updateXXXFlags() functions to leave flags unchanged
TRAP_RED: 0x0100, // set whenever a RED trap occurs, used to catch double RED traps (time to PANIC)
},
/*
* Opcode reg (opcode bits 2-0)
@ -489,6 +491,8 @@ var PDP11 = {
RLMP: 0o174406, // RL11 Multi-Purpose Register
RLBE: 0o174410, // RL11 Bus (Address) Extension Register (RLV12 controller only)
DL11: 0o176500, // DL11 Additional Register Range (ends at 0o176676)
RKDS: 0o177400, // RK11 Drive Status Register
RKER: 0o177402, // RK11 Error Register
RKCS: 0o177404, // RK11 Control Status Register
@ -609,6 +613,7 @@ var PDP11 = {
DSC: 0x8000, // Dataset Status Change (R/O)
RMASK: 0xFFFE, // bits readable (TODO: All I know for sure is that bit 0 is NOT readable; see readRCSR())
WMASK: 0x006F, // bits writable
RS232: 0x0006, // bits affecting RS-232 status updates
BAUD: 9600
},
RBUF: { // 177562: DL11 Receiver Data Buffer Register
@ -846,10 +851,10 @@ var PDP11 = {
}
},
VECTORS: {
0o060: "DL11.RCSR",
0o064: "DL11.XCSR",
0o070: "PC11.PRS",
0o074: "PC11.PPS",
0o060: "DL11R",
0o064: "DL11X",
0o070: "PC11R",
0o074: "PC11X",
0o100: "KW11",
0o160: "RL11",
0o220: "RK11"

View file

@ -1065,7 +1065,6 @@ DevicePDP11.prototype.writePSW = function(data, addr)
*/
var maskDisallowed = PDP11.PSW.UNUSED;
this.cpu.setPSW((data & ~maskDisallowed) | (this.cpu.getPSW() & maskDisallowed));
this.cpu.opFlags |= PDP11.OPFLAG.NO_FLAGS;
};
/**

View file

@ -713,15 +713,15 @@ PC11.prototype.parseTape = function(sTapeName, sTapePath, nTapeTarget, aBytes, a
this.sTapePath = "";
this.sTapeSource = PC11.SOURCE.NONE;
this.nTapeTarget = PC11.TARGET.NONE;
this.notice("No load address available for tape: " + sTapeName);
this.notice('No load address available for tape "' + sTapeName + '"');
return;
}
this.status("tape loaded: " + sTapeName);
this.status('Loaded tape "' + sTapeName + '"');
return;
}
this.iTapeData = 0;
this.aTapeData = aBytes;
this.status("tape attached: " + sTapeName);
this.status('Attached tape "' + sTapeName + '"');
this.displayProgress(0);
};

View file

@ -215,6 +215,8 @@ RAMPDP11.prototype.initRAM = function()
if (!this.fAllocated && this.sizeRAM) {
if (this.bus.addMemory(this.addrRAM, this.sizeRAM, MemoryPDP11.TYPE.RAM)) {
this.fAllocated = true;
} else {
this.sizeRAM = 0; // don't bother trying again (it just results in redundant error messages)
}
}
if (!this.isReady()) {

View file

@ -117,6 +117,7 @@ function SerialPortPDP11(parmsSerial) {
this.tabSize = parmsSerial['tabSize'];
this.charBOL = parmsSerial['charBOL'];
this.iLogicalCol = 0;
this.fNullModem = true;
Component.call(this, "SerialPort", parmsSerial, SerialPortPDP11, MessagesPDP11.SERIAL);
@ -134,14 +135,15 @@ function SerialPortPDP11(parmsSerial) {
* No connection until initConnection() is called.
*/
this.sDataReceived = "";
this.connection = this.sendData = null;
this.connection = this.sendData = this.updateStatus = null;
/*
* Export all functions required by initConnection(); currently, this is the bare minimum (no flow control yet).
* Export all functions required by initConnection().
*/
this['exports'] = {
'connect': this.initConnection,
'receiveData': this.receiveData
'receiveData': this.receiveData,
'receiveStatus': this.receiveStatus
};
}
@ -308,7 +310,7 @@ SerialPortPDP11.prototype.initBus = function(cmp, bus, cpu, dbg)
var serial = this;
this.irqReceiver = this.cpu.addIRQ(PDP11.DL11.RVEC, PDP11.DL11.PRI, MessagesPDP11.DL11);
this.irqReceiver = this.cpu.addIRQ(this.iAdapter? -1 : PDP11.DL11.RVEC, PDP11.DL11.PRI, MessagesPDP11.DL11);
this.timerReceiveInterrupt = this.cpu.addTimer(function readyReceiver() {
var b = serial.receiveByte();
@ -325,7 +327,7 @@ SerialPortPDP11.prototype.initBus = function(cmp, bus, cpu, dbg)
}
});
this.irqTransmitter = this.cpu.addIRQ(PDP11.DL11.XVEC, PDP11.DL11.PRI, MessagesPDP11.DL11);
this.irqTransmitter = this.cpu.addIRQ(this.iAdapter? -1 : PDP11.DL11.XVEC, PDP11.DL11.PRI, MessagesPDP11.DL11);
this.timerTransmitInterrupt = this.cpu.addTimer(function readyTransmitter() {
serial.regXCSR |= PDP11.DL11.XCSR.READY;
@ -334,14 +336,14 @@ SerialPortPDP11.prototype.initBus = function(cmp, bus, cpu, dbg)
}
});
bus.addIOTable(this, SerialPortPDP11.UNIBUS_IOTABLE);
bus.addIOTable(this, SerialPortPDP11.UNIBUS_IOTABLE, this.iAdapter? ((PDP11.UNIBUS.DL11 + (this.iAdapter - 1) * 8) - PDP11.UNIBUS.RCSR) : 0);
bus.addResetHandler(this.reset.bind(this));
this.setReady();
};
/**
* initConnection()
* initConnection(fNullModem)
*
* If a machine 'connection' parameter exists of the form "{sourcePort}->{targetMachine}.{targetPort}",
* and "{sourcePort}" matches our idComponent, then look for a component with id "{targetMachine}.{targetPort}".
@ -349,6 +351,7 @@ SerialPortPDP11.prototype.initBus = function(cmp, bus, cpu, dbg)
* If the target component is found, then verify that it has exported functions with the following names:
*
* receiveData(data): called when we have data to transmit; aliased internally to sendData(data)
* receiveStatus(pins): called when our control signals have changed; aliased internally to updateStatus(pins)
*
* For now, we're not going to worry about communication in the other direction, because when the target component
* performs its own initConnection(), it will find our receiveData() function, at which point communication in both
@ -359,8 +362,9 @@ SerialPortPDP11.prototype.initBus = function(cmp, bus, cpu, dbg)
* if we've already initialized, no harm done.
*
* @this {SerialPortPDP11}
* @param {boolean} [fNullModem] (caller's null-modem setting, to ensure our settings are in agreement)
*/
SerialPortPDP11.prototype.initConnection = function()
SerialPortPDP11.prototype.initConnection = function(fNullModem)
{
if (!this.connection) {
var sConnection = this.cmp.getMachineParm("connection");
@ -375,9 +379,11 @@ SerialPortPDP11.prototype.initConnection = function()
var exports = this.connection['exports'];
if (exports) {
var fnConnect = exports['connect'];
if (fnConnect) fnConnect.call(this.connection);
if (fnConnect) fnConnect.call(this.connection, this.fNullModem);
this.sendData = exports['receiveData'];
if (this.sendData) {
this.fNullModem = fNullModem;
this.updateStatus = exports['receiveStatus'];
this.status(this.idMachine + '.' + sSourceID + " connected to " + sTargetID);
return;
}
@ -410,7 +416,7 @@ SerialPortPDP11.prototype.powerUp = function(data, fRepower)
* may be not fully resolved until the target machine performs its own initConnection(), which will
* in turn invoke our initConnection() again.
*/
this.initConnection();
this.initConnection(this.fNullModem);
if (!data || !this.restore) {
this.reset();
@ -483,7 +489,7 @@ SerialPortPDP11.prototype.restore = function(data)
SerialPortPDP11.prototype.initState = function(data)
{
this.regRBUF = 0;
this.regRCSR = 0;
this.regRCSR = PDP11.DL11.RCSR.CTS; // TODO: I didn't use to set this initially; is this wise?
this.regXCSR = PDP11.DL11.XCSR.READY;
this.abReceive = [];
return true;
@ -592,6 +598,30 @@ SerialPortPDP11.prototype.receiveByte = function()
return b;
};
/**
* receiveStatus(pins)
*
* @this {SerialPortPDP11}
* @param {number} pins
*/
SerialPortPDP11.prototype.receiveStatus = function(pins)
{
var oldRCSR = this.regRCSR;
this.regRCSR &= ~(PDP11.DL11.RCSR.CTS | PDP11.DL11.RCSR.CD);
if (pins & RS232.CTS.MASK) {
this.regRCSR |= PDP11.DL11.RCSR.CTS;
}
if (pins & RS232.CD.MASK) {
this.regRCSR |= PDP11.DL11.RCSR.CD;
}
if (oldRCSR != this.regRCSR) {
this.regRCSR |= PDP11.DL11.RCSR.DSC;
if (this.regRCSR & PDP11.DL11.RCSR.DIE) {
this.cpu.setIRQ(this.irqReceiver);
}
}
};
/**
* transmitByte(b)
*
@ -681,7 +711,9 @@ SerialPortPDP11.prototype.transmitByte = function(b)
*/
SerialPortPDP11.prototype.readRCSR = function(addr)
{
return this.regRCSR & PDP11.DL11.RCSR.RMASK;
var data = this.regRCSR & PDP11.DL11.RCSR.RMASK;
this.regRCSR &= ~PDP11.DL11.RCSR.DSC;
return data;
};
/**
@ -693,7 +725,24 @@ SerialPortPDP11.prototype.readRCSR = function(addr)
*/
SerialPortPDP11.prototype.writeRCSR = function(data, addr)
{
var delta = (data ^ this.regRCSR);
this.regRCSR = (this.regRCSR & ~PDP11.DL11.RCSR.WMASK) | (data & PDP11.DL11.RCSR.WMASK);
/*
* Whenever DTR or RTS changes, we also want to notify any connected machine, via updateStatus().
*/
if (this.updateStatus) {
if (delta & PDP11.DL11.RCSR.RS232) {
var pins = 0;
if (this.fNullModem) {
pins |= (data & PDP11.DL11.RCSR.RTS)? RS232.CTS.MASK : 0;
pins |= (data & PDP11.DL11.RCSR.DTR)? (RS232.DSR.MASK | RS232.CD.MASK): 0;
} else {
pins |= (data & PDP11.DL11.RCSR.RTS)? RS232.RTS.MASK : 0;
pins |= (data & PDP11.DL11.RCSR.DTR)? RS232.DTR.MASK : 0;
}
this.updateStatus.call(this.connection, pins);
}
}
};
/**

View file

@ -62,6 +62,51 @@ var MAXDEBUG = false; // this @define is overridden by the Closure Com
*/
var PRIVATE = false; // this @define is overridden by the Closure Compiler (to false) to enable PRIVATE code
/*
* RS-232 DB-25 Pin Definitions, mapped to bits 1-25 in a 32-bit status value.
*
* SerialPorts in PCjs machines are considered DTE (Data Terminal Equipment), which means they should be "virtually"
* connected to each other via a null-modem cable, which assumes the following cross-wiring:
*
* G 1 <-> 1 G (Ground)
* TD 2 <-> 3 RD (Received Data)
* RD 3 <-> 2 TD (Transmitted Data)
* RTS 4 <-> 5 CTS (Clear To Send)
* CTS 5 <-> 4 RTS (Request To Send)
* DSR 6+8 <-> 20 DTR (Data Terminal Ready)
* SG 7 <-> 7 SG (Signal Ground)
* DTR 20 <-> 6+8 DSR (Data Set Ready + Carrier Detect)
* RI 22 <-> 22 RI (Ring Indicator)
*
* TODO: Move these definitions to a more appropriate shared file at some point.
*/
var RS232 = {
RTS: {
PIN: 4,
MASK: 0x00000010
},
CTS: {
PIN: 5,
MASK: 0x00000020
},
DSR: {
PIN: 6,
MASK: 0x00000040
},
CD: {
PIN: 8,
MASK: 0x00000100
},
DTR: {
PIN: 20,
MASK: 0x00100000
},
RI: {
PIN: 22,
MASK: 0x00400000
}
};
/*
* NODE should be true if we're running under NodeJS (eg, command-line), false if not (eg, web browser)
*/
@ -82,6 +127,7 @@ if (NODE) {
global.DEBUG = DEBUG;
global.MAXDEBUG = MAXDEBUG;
global.PRIVATE = PRIVATE;
global.RS232 = RS232;
global.NODE = NODE;
/*
* TODO: When we're "required" by Node, should we return anything via module.exports?

View file

@ -57,7 +57,7 @@
}
.pcjs-progress {
height: 20px;
width: 300px;
width: 200px;
margin-top: 8px;
border: 1px solid black;
position: relative;
@ -71,7 +71,7 @@
}
.pcjs-progress-text {
height: 20px;
width: 300px;
width: 200px;
font-size: small;
line-height: 20px;
text-align: center;
@ -229,7 +229,7 @@
text-decoration: none;
}
@media screen and (max-width: 900px) {
@media screen and (max-width: 800px) {
.pcjs-textarea {
width: 100% !important;
}
@ -247,3 +247,13 @@
padding-right: 0 !important;
}
}
@media screen and (min-width: 1400px) {
.machine-left, .machine-right {
max-width: 45%;
float: left;
}
.machine-right + * {
clear: both;
}
}