Moved PDP-11 console I/O to SerialPort component
This commit is contained in:
parent
7923529aee
commit
d90d10ea52
15 changed files with 1241 additions and 561 deletions
|
|
@ -5,9 +5,10 @@
|
|||
<computer id="computer" busWidth="22"/>
|
||||
<cpu id="cpu" model="1170" cycles="6666667" resetAddr="0xC000"/>
|
||||
<ram id="ram" addr="0x0000" size="0xC000"/>
|
||||
<rom id="rom" addr="0xC000" size="0x0570" file="/devices/pdp11/rom/custom/boot.json"/>
|
||||
<rom id="rom" addr="0xC000" size="0x0570" file="/devices/pdp11/rom/custom/boot.json" writable="true"/>
|
||||
<ram id="ram" addr="0x10000" size="0x34000"/>
|
||||
<device id="unibus" name="unibus"/>
|
||||
<serial id="console" adapter="1" binding="print"/>
|
||||
<panel ref="/devices/pdp11/panel/1170/wide.xml"/>
|
||||
<debugger id="debugger" base="8" messages="bus|mem|trap" commands="bp 140270"/>
|
||||
<debugger id="debugger" base="8" messages="bus|mem|trap" commands="bp 140210"/>
|
||||
</machine>
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -210,13 +210,13 @@ BusPDP11.IOController = {
|
|||
}
|
||||
if (b >= 0) {
|
||||
if (DEBUGGER && this.dbg && this.dbg.messageEnabled(MessagesPDP11.BUS)) {
|
||||
this.dbg.message(afn[5] + ".readByte(" + this.dbg.toStrBase(addr) + "): " + this.dbg.toStrBase(b), true);
|
||||
this.dbg.printMessage(afn[5] + ".readByte(" + this.dbg.toStrBase(addr) + "): " + this.dbg.toStrBase(b), true);
|
||||
}
|
||||
return b;
|
||||
}
|
||||
b = bus.fnAccess(addr, -1, 1);
|
||||
b = bus.fnAccess(addr | BusPDP11.IOPAGE_22BIT, -1, 1);
|
||||
if (DEBUGGER && this.dbg && this.dbg.messageEnabled(MessagesPDP11.BUS)) {
|
||||
this.dbg.message("warning: unconverted read access to byte @" + this.dbg.toStrBase(addr) + ": " + this.dbg.toStrBase(b));
|
||||
this.dbg.printMessage("warning: unconverted read access to byte @" + this.dbg.toStrBase(addr) + ": " + this.dbg.toStrBase(b));
|
||||
}
|
||||
return b;
|
||||
},
|
||||
|
|
@ -275,13 +275,13 @@ BusPDP11.IOController = {
|
|||
}
|
||||
if (fWrite) {
|
||||
if (DEBUGGER && this.dbg && this.dbg.messageEnabled(MessagesPDP11.BUS)) {
|
||||
this.dbg.message(afn[5] + ".writeByte(" + this.dbg.toStrBase(addr) + "," + this.dbg.toStrBase(b) + ")", true);
|
||||
this.dbg.printMessage(afn[5] + ".writeByte(" + this.dbg.toStrBase(addr) + "," + this.dbg.toStrBase(b) + ")", true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
bus.fnAccess(addr, b, 1);
|
||||
bus.fnAccess(addr | BusPDP11.IOPAGE_22BIT, b, 1);
|
||||
if (DEBUGGER && this.dbg && this.dbg.messageEnabled(MessagesPDP11.BUS)) {
|
||||
this.dbg.message("warning: unconverted write access to byte @" + this.dbg.toStrBase(addr) + ": " + this.dbg.toStrBase(b));
|
||||
this.dbg.printMessage("warning: unconverted write access to byte @" + this.dbg.toStrBase(addr) + ": " + this.dbg.toStrBase(b));
|
||||
}
|
||||
},
|
||||
|
||||
|
|
@ -308,13 +308,13 @@ BusPDP11.IOController = {
|
|||
}
|
||||
if (w >= 0) {
|
||||
if (DEBUGGER && this.dbg && this.dbg.messageEnabled(MessagesPDP11.BUS)) {
|
||||
this.dbg.message(afn[5] + ".readWord(" + this.dbg.toStrBase(addr) + "): " + this.dbg.toStrBase(w), true);
|
||||
this.dbg.printMessage(afn[5] + ".readWord(" + this.dbg.toStrBase(addr) + "): " + this.dbg.toStrBase(w), true);
|
||||
}
|
||||
return w;
|
||||
}
|
||||
w = bus.fnAccess(addr, -1, 0);
|
||||
w = bus.fnAccess(addr | BusPDP11.IOPAGE_22BIT, -1, 0);
|
||||
if (DEBUGGER && this.dbg && this.dbg.messageEnabled(MessagesPDP11.BUS)) {
|
||||
this.dbg.message("warning: unconverted read access to word @" + this.dbg.toStrBase(addr) + ": " + this.dbg.toStrBase(w));
|
||||
this.dbg.printMessage("warning: unconverted read access to word @" + this.dbg.toStrBase(addr) + ": " + this.dbg.toStrBase(w));
|
||||
}
|
||||
return w;
|
||||
},
|
||||
|
|
@ -345,13 +345,13 @@ BusPDP11.IOController = {
|
|||
}
|
||||
if (fWrite) {
|
||||
if (DEBUGGER && this.dbg && this.dbg.messageEnabled(MessagesPDP11.BUS)) {
|
||||
this.dbg.message(afn[5] + ".writeWord(" + this.dbg.toStrBase(addr) + "," + this.dbg.toStrBase(w) + ")", true);
|
||||
this.dbg.printMessage(afn[5] + ".writeWord(" + this.dbg.toStrBase(addr) + "," + this.dbg.toStrBase(w) + ")", true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
bus.fnAccess(addr, w, 0);
|
||||
bus.fnAccess(addr | BusPDP11.IOPAGE_22BIT, w, 0);
|
||||
if (DEBUGGER && this.dbg && this.dbg.messageEnabled(MessagesPDP11.BUS)) {
|
||||
this.dbg.message("warning: unconverted write access to word @" + this.dbg.toStrBase(addr) + ": " + this.dbg.toStrBase(w));
|
||||
this.dbg.printMessage("warning: unconverted write access to word @" + this.dbg.toStrBase(addr) + ": " + this.dbg.toStrBase(w));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -426,7 +426,7 @@ BusPDP11.prototype.reset = function()
|
|||
};
|
||||
|
||||
/**
|
||||
* access()
|
||||
* access(addr, data, byteFlag)
|
||||
*
|
||||
* This is our default I/O handler, called whenever there's an IOPAGE access without a corresponding entry
|
||||
* in aIOHandlers; in the interim, our Device component will override this default handler with its own function
|
||||
|
|
@ -440,6 +440,9 @@ BusPDP11.prototype.reset = function()
|
|||
*/
|
||||
BusPDP11.prototype.access = function(addr, data, byteFlag)
|
||||
{
|
||||
if (DEBUGGER && this.dbg && this.dbg.messageEnabled(MessagesPDP11.BUS)) {
|
||||
this.dbg.printMessage("warning: unrecognized access(" + this.dbg.toStrBase(addr) + "," + this.dbg.toStrBase(data) + "," + byteFlag + ")");
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -214,8 +214,8 @@ if (DEBUGGER) {
|
|||
's': "set options",
|
||||
't [#]': "trace", // other variations: tr (trace and dump registers)
|
||||
'u [#]': "unassemble",
|
||||
'v': "print version",
|
||||
'var': "assign variable"
|
||||
'var': "assign variable",
|
||||
'ver': "print version"
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
@ -1135,12 +1135,32 @@ if (DEBUGGER) {
|
|||
return s;
|
||||
};
|
||||
|
||||
/**
|
||||
* disableMessages(s)
|
||||
*
|
||||
* @this {DebuggerPDP11}
|
||||
*/
|
||||
DebuggerPDP11.prototype.disableMessages = function()
|
||||
{
|
||||
this.nDisableMessages++;
|
||||
};
|
||||
|
||||
/**
|
||||
* enableMessages(s)
|
||||
*
|
||||
* @this {DebuggerPDP11}
|
||||
*/
|
||||
DebuggerPDP11.prototype.enableMessages = function()
|
||||
{
|
||||
this.nDisableMessages--;
|
||||
};
|
||||
|
||||
/**
|
||||
* message(sMessage, fAddress)
|
||||
*
|
||||
* @this {DebuggerPDP11}
|
||||
* @param {string} sMessage is any caller-defined message string
|
||||
* @param {boolean} [fAddress] is true to display the current PC
|
||||
* @param {boolean} [fAddress] is true to display the current address
|
||||
*/
|
||||
DebuggerPDP11.prototype.message = function(sMessage, fAddress)
|
||||
{
|
||||
|
|
@ -3123,6 +3143,7 @@ if (DEBUGGER) {
|
|||
DebuggerPDP11.prototype.doOptions = function(asArgs)
|
||||
{
|
||||
switch (asArgs[1]) {
|
||||
|
||||
case "base":
|
||||
if (asArgs[2]) {
|
||||
var nBase = +asArgs[2];
|
||||
|
|
@ -3135,6 +3156,7 @@ if (DEBUGGER) {
|
|||
}
|
||||
this.println("default base: " + this.nBase);
|
||||
break;
|
||||
|
||||
case "cs":
|
||||
var nCycles;
|
||||
if (asArgs[3] !== undefined) nCycles = +asArgs[3]; // warning: decimal instead of hex conversion
|
||||
|
|
@ -3167,6 +3189,13 @@ if (DEBUGGER) {
|
|||
this.println("target speed: " + this.cpu.getSpeedTarget() + " (" + this.cpu.getSpeed() + "x)");
|
||||
return;
|
||||
|
||||
default:
|
||||
if (asArgs[1]) {
|
||||
this.println("unknown option: " + asArgs[1]);
|
||||
return;
|
||||
}
|
||||
/* falls through */
|
||||
|
||||
case "?":
|
||||
this.println("debugger options:");
|
||||
this.println("\tbase #\t\tset default base to #");
|
||||
|
|
@ -3175,13 +3204,6 @@ if (DEBUGGER) {
|
|||
this.println("\tcs stop #\tset checksum cycle stop count to #");
|
||||
this.println("\tsp #\t\tset speed multiplier to #");
|
||||
break;
|
||||
|
||||
default:
|
||||
if (asArgs[1]) {
|
||||
this.println("unknown option: " + asArgs[1]);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -3310,7 +3332,11 @@ if (DEBUGGER) {
|
|||
if (!a) {
|
||||
this.parseExpression(sCmd, true);
|
||||
} else {
|
||||
if (a[2].length > 1) {
|
||||
this.println(this.replaceRegs(a[2]));
|
||||
} else {
|
||||
this.printValue(null, a[2].charCodeAt(0));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -3644,7 +3670,9 @@ if (DEBUGGER) {
|
|||
sCmd = "a " + this.toStrAddr(this.dbgAddrAssemble) + ' ' + sCmd;
|
||||
}
|
||||
|
||||
var fError = false;
|
||||
var asArgs = this.shiftArgs(sCmd.replace(/ +/g, ' ').split(' '));
|
||||
asArgs[0] = asArgs[0].toLowerCase();
|
||||
|
||||
switch (asArgs[0].charAt(0)) {
|
||||
case 'a':
|
||||
|
|
@ -3679,7 +3707,9 @@ if (DEBUGGER) {
|
|||
if (!this.doIf(sCmd.substr(2), fQuiet)) {
|
||||
result = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
fError = true;
|
||||
break;
|
||||
case 'k':
|
||||
this.doStackTrace(asArgs[0], asArgs[1]);
|
||||
|
|
@ -3689,6 +3719,7 @@ if (DEBUGGER) {
|
|||
this.doList(asArgs[1], true);
|
||||
break;
|
||||
}
|
||||
fError = true;
|
||||
break;
|
||||
case 'm':
|
||||
this.doMessages(asArgs);
|
||||
|
|
@ -3723,9 +3754,13 @@ if (DEBUGGER) {
|
|||
}
|
||||
break;
|
||||
}
|
||||
if (asArgs[0] == "ver") {
|
||||
this.println((PDP11.APPNAME || "PDP11") + " version " + (XMLVERSION || PDP11.APPVERSION) + " (" + this.cpu.model + (PDP11.COMPILED? ",RELEASE" : (PDP11.DEBUG? ",DEBUG" : ",NODEBUG")) + (PDP11.TYPEDARRAYS? ",TYPEDARRAYS" : (PDP11.BYTEARRAYS? ",BYTEARRAYS" : ",LONGARRAYS")) + ')');
|
||||
this.println(web.getUserAgent());
|
||||
break;
|
||||
}
|
||||
fError = true;
|
||||
break;
|
||||
case '?':
|
||||
if (asArgs[1]) {
|
||||
this.doPrint(sCmd.substr(1));
|
||||
|
|
@ -3742,9 +3777,12 @@ if (DEBUGGER) {
|
|||
if (this.doInfo(asArgs)) break;
|
||||
/* falls through */
|
||||
default:
|
||||
fError = true;
|
||||
break;
|
||||
}
|
||||
if (fError) {
|
||||
this.println("unknown command: " + sCmd);
|
||||
result = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch(e) {
|
||||
|
|
|
|||
|
|
@ -60,14 +60,6 @@ function DevicePDP11(parmsDevice)
|
|||
Component.call(this, "Device", parmsDevice, DevicePDP11);
|
||||
this.sDeviceName = parmsDevice['name'];
|
||||
|
||||
this.tty = {
|
||||
rbuf: [],
|
||||
rcsr: 0,
|
||||
xcsr: PDP11.DL11.XCSR.READY,
|
||||
delCode: 127,
|
||||
del: 0
|
||||
};
|
||||
|
||||
this.display = {
|
||||
data: 0,
|
||||
address: 0,
|
||||
|
|
@ -268,67 +260,6 @@ DevicePDP11.prototype.writeMMR0 = function(data, addr)
|
|||
this.display.misc = (this.display.misc & ~7) | idx;
|
||||
};
|
||||
|
||||
/**
|
||||
* readRCSR(addr)
|
||||
*
|
||||
* @this {DevicePDP11}
|
||||
* @param {number} addr (eg, PDP11.UNIBUS.RCSR or 177560)
|
||||
* @return {number}
|
||||
*/
|
||||
DevicePDP11.prototype.readRCSR = function(addr)
|
||||
{
|
||||
return this.tty.rcsr;
|
||||
};
|
||||
|
||||
/**
|
||||
* writeRCSR(data, addr)
|
||||
*
|
||||
* @this {DevicePDP11}
|
||||
* @param {number} data
|
||||
* @param {number} addr (eg, PDP11.UNIBUS.RCSR or 177560)
|
||||
*/
|
||||
DevicePDP11.prototype.writeRCSR = function(data, addr)
|
||||
{
|
||||
this.tty.rcsr = (this.tty.rcsr & 0x80) | (data & ~0x80);
|
||||
};
|
||||
|
||||
/**
|
||||
* readXCSR(addr)
|
||||
*
|
||||
* @this {DevicePDP11}
|
||||
* @param {number} addr (eg, PDP11.UNIBUS.XCSR or 177564)
|
||||
* @return {number}
|
||||
*/
|
||||
DevicePDP11.prototype.readXCSR = function(addr)
|
||||
{
|
||||
return this.tty.xcsr;
|
||||
};
|
||||
|
||||
/**
|
||||
* writeXCSR(data, addr)
|
||||
*
|
||||
* @this {DevicePDP11}
|
||||
* @param {number} data
|
||||
* @param {number} addr (eg, PDP11.UNIBUS.XCSR or 177564)
|
||||
*/
|
||||
DevicePDP11.prototype.writeXCSR = function(data, addr)
|
||||
{
|
||||
/*
|
||||
* If the device is READY, and INT_ENABLE is transitioning to *set*, then generate an interrupt.
|
||||
*/
|
||||
if ((this.tty.xcsr & (PDP11.DL11.XCSR.READY | PDP11.DL11.XCSR.INT_ENABLE)) == PDP11.DL11.XCSR.READY && (data & PDP11.DL11.XCSR.INT_ENABLE)) {
|
||||
var device = this;
|
||||
this.cpu.interrupt(PDP11.DL11.DELAY, PDP11.DL11.PRI, PDP11.DL11.VEC, function() {
|
||||
device.tty.xcsr |= PDP11.DL11.XCSR.READY;
|
||||
return !!(device.tty.xcsr & PDP11.DL11.XCSR.INT_ENABLE);
|
||||
});
|
||||
}
|
||||
/*
|
||||
* In any event, propagate all bits from data *except* for READY.
|
||||
*/
|
||||
this.tty.xcsr = (this.tty.xcsr & PDP11.DL11.XCSR.READY) | (data & ~PDP11.DL11.XCSR.READY);
|
||||
};
|
||||
|
||||
/**
|
||||
* readPSW(addr)
|
||||
*
|
||||
|
|
@ -924,8 +855,6 @@ DevicePDP11.prototype.writeData = function(meta, block, address, count)
|
|||
DevicePDP11.prototype.reset = function()
|
||||
{
|
||||
this.display.misc = (this.display.misc & ~0x77) | 0x14; // kernel 16 bit
|
||||
this.tty.rcsr = 0;
|
||||
this.tty.xcsr = 0x80; /*0200*/
|
||||
this.kw11.lks = 0;
|
||||
this.rk11.rkcs = 0x80; /*0200*/
|
||||
this.rl11.csr = 0x80;
|
||||
|
|
@ -1110,7 +1039,7 @@ DevicePDP11.prototype.access = function(physicalAddress, data, byteFlag)
|
|||
}
|
||||
break;
|
||||
case 0x3FFF40: /*017777500*/ // 017777500 - 017777577
|
||||
var tty = this.tty;
|
||||
// var tty = this.tty;
|
||||
var kw11 = this.kw11;
|
||||
switch (physicalAddress & ~1) {
|
||||
case 0x3FFF7E: /*017777576*/ // MMR2
|
||||
|
|
@ -1155,44 +1084,44 @@ DevicePDP11.prototype.access = function(physicalAddress, data, byteFlag)
|
|||
if (result >= 0) this.display.data = result;
|
||||
}
|
||||
break;
|
||||
case 0x3FFF76: /*017777566*/ // console xbuf
|
||||
if (data >= 0) {
|
||||
data &= 0x7f;
|
||||
if (data) {
|
||||
switch (data) {
|
||||
case 0:
|
||||
case 0xD: /*015*/
|
||||
/*
|
||||
if (document.forms.console.line.value.length > 9000) {
|
||||
document.forms.console.line.value = document.forms.console.line.value.substring(document.forms.console.line.value.length - 8192);
|
||||
}
|
||||
*/
|
||||
break;
|
||||
case 0x8: /*010*/
|
||||
/*
|
||||
if (!tty.del) {
|
||||
document.forms.console.line.value = document.forms.console.line.value.substring(0, document.forms.console.line.value.length - 1);
|
||||
}
|
||||
*/
|
||||
break;
|
||||
default:
|
||||
/*
|
||||
document.forms.console.line.value += String.fromCharCode(data);
|
||||
if (data == 0x0A) {
|
||||
document.forms.console.line.scrollTop = document.forms.console.line.scrollHeight;
|
||||
}
|
||||
*/
|
||||
}
|
||||
tty.del = 0;
|
||||
}
|
||||
tty.xcsr &= ~0x80; /*0200*/
|
||||
cpu.interrupt(100, 4, 0x34, /*064*/ function() {
|
||||
tty.xcsr |= 0x80; /*0200*/
|
||||
return !!(tty.xcsr & 0x40); /*0100*/
|
||||
});
|
||||
}
|
||||
result = 0;
|
||||
break;
|
||||
// case 0x3FFF76: /*017777566*/ // console xbuf
|
||||
// if (data >= 0) {
|
||||
// data &= 0x7f;
|
||||
// if (data) {
|
||||
// switch (data) {
|
||||
// case 0:
|
||||
// case 0xD: /*015*/
|
||||
// /*
|
||||
// if (document.forms.console.line.value.length > 9000) {
|
||||
// document.forms.console.line.value = document.forms.console.line.value.substring(document.forms.console.line.value.length - 8192);
|
||||
// }
|
||||
// */
|
||||
// break;
|
||||
// case 0x8: /*010*/
|
||||
// /*
|
||||
// if (!tty.del) {
|
||||
// document.forms.console.line.value = document.forms.console.line.value.substring(0, document.forms.console.line.value.length - 1);
|
||||
// }
|
||||
// */
|
||||
// break;
|
||||
// default:
|
||||
// /*
|
||||
// document.forms.console.line.value += String.fromCharCode(data);
|
||||
// if (data == 0x0A) {
|
||||
// document.forms.console.line.scrollTop = document.forms.console.line.scrollHeight;
|
||||
// }
|
||||
// */
|
||||
// }
|
||||
// tty.del = 0;
|
||||
// }
|
||||
// tty.xcsr &= ~0x80; /*0200*/
|
||||
// cpu.interrupt(100, 4, 0x34, /*064*/ function() {
|
||||
// tty.xcsr |= 0x80; /*0200*/
|
||||
// return !!(tty.xcsr & 0x40); /*0100*/
|
||||
// });
|
||||
// }
|
||||
// result = 0;
|
||||
// break;
|
||||
// case 0x3FFF74: /*017777564*/ // console xcsr
|
||||
// if (data < 0) {
|
||||
// result = tty.xcsr;
|
||||
|
|
@ -1209,29 +1138,29 @@ DevicePDP11.prototype.access = function(physicalAddress, data, byteFlag)
|
|||
// }
|
||||
// }
|
||||
// break;
|
||||
case 0x3FFF72: /*017777562*/ // console rbuf
|
||||
result = 0;
|
||||
if (data < 0) {
|
||||
tty.rcsr &= ~0x80; /*0200*/
|
||||
if (tty.rbuf.length > 0) {
|
||||
result = tty.rbuf.shift();
|
||||
if (tty.rbuf.length > 0) {
|
||||
setTimeout(function() {
|
||||
tty.rcsr |= 0x80; /*0200*/
|
||||
if (tty.rcsr & 0x40) /*0100*/ cpu.interrupt(40, 4, 0x30) /*060*/;
|
||||
}, 50);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 0x3FFF70: /*017777560*/ // console rcsr
|
||||
if (data < 0) {
|
||||
result = tty.rcsr;
|
||||
} else {
|
||||
result = this.insertData(tty.rcsr, physicalAddress, data, byteFlag);
|
||||
if (result >= 0) tty.rcsr = (tty.rcsr & 0x80) /*0200*/ | (result & ~0x80) /*0200*/;
|
||||
}
|
||||
break;
|
||||
// case 0x3FFF72: /*017777562*/ // console rbuf
|
||||
// result = 0;
|
||||
// if (data < 0) {
|
||||
// tty.rcsr &= ~0x80; /*0200*/
|
||||
// if (tty.rbuf.length > 0) {
|
||||
// result = tty.rbuf.shift();
|
||||
// if (tty.rbuf.length > 0) {
|
||||
// setTimeout(function() {
|
||||
// tty.rcsr |= 0x80; /*0200*/
|
||||
// if (tty.rcsr & 0x40) /*0100*/ cpu.interrupt(40, 4, 0x30) /*060*/;
|
||||
// }, 50);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// break;
|
||||
// case 0x3FFF70: /*017777560*/ // console rcsr
|
||||
// if (data < 0) {
|
||||
// result = tty.rcsr;
|
||||
// } else {
|
||||
// result = this.insertData(tty.rcsr, physicalAddress, data, byteFlag);
|
||||
// if (result >= 0) tty.rcsr = (tty.rcsr & 0x80) /*0200*/ | (result & ~0x80) /*0200*/;
|
||||
// }
|
||||
// break;
|
||||
// case 0x3FFF66: /*017777546*/ // kw11.lks
|
||||
// if (data < 0) {
|
||||
// result = kw11.lks;
|
||||
|
|
@ -1539,8 +1468,6 @@ DevicePDP11.prototype.access = function(physicalAddress, data, byteFlag)
|
|||
*/
|
||||
DevicePDP11.UNIBUS_IOTABLE = {
|
||||
[PDP11.UNIBUS.LKS]: /* 177546 */ [null, null, DevicePDP11.prototype.readLKS, DevicePDP11.prototype.writeLKS, "LKS"],
|
||||
[PDP11.UNIBUS.RCSR]: /* 177560 */ [null, null, DevicePDP11.prototype.readRCSR, DevicePDP11.prototype.writeRCSR, "RCSR"],
|
||||
[PDP11.UNIBUS.XCSR]: /* 177564 */ [null, null, DevicePDP11.prototype.readXCSR, DevicePDP11.prototype.writeXCSR, "XCSR"],
|
||||
[PDP11.UNIBUS.MMR0]: /* 177572 */ [null, null, DevicePDP11.prototype.readMMR0, DevicePDP11.prototype.writeMMR0, "MMR0"],
|
||||
[PDP11.UNIBUS.PSW]: /* 177776 */ [null, null, DevicePDP11.prototype.readPSW, DevicePDP11.prototype.writePSW, "PSW"]
|
||||
};
|
||||
|
|
|
|||
|
|
@ -59,18 +59,18 @@ var littleEndian = (TYPEDARRAYS? (function() {
|
|||
/**
|
||||
* MemoryPDP11(addr, used, size, type, controller)
|
||||
*
|
||||
* The Bus component allocates MemoryPDP11 objects so that each has a memory buffer with a
|
||||
* The Bus component allocates Memory objects so that each has a memory buffer with a
|
||||
* block-granular starting address and an address range equal to bus.nBlockSize; however,
|
||||
* the size of any given MemoryPDP11 object's underlying buffer can be either zero or bus.nBlockSize;
|
||||
* the size of any given Memory object's underlying buffer can be either zero or bus.nBlockSize;
|
||||
* memory read/write functions for empty (buffer-less) blocks are mapped to readNone/writeNone.
|
||||
*
|
||||
* The Bus allocates empty blocks for the entire address space during initialization, so that
|
||||
* any reads/writes to undefined addresses will have no effect. Later, the ROM and RAM
|
||||
* components will ask the Bus to allocate memory for specific ranges, and the Bus will allocate
|
||||
* as many new blockSize MemoryPDP11 objects as the ranges require. Partial MemoryPDP11 blocks could
|
||||
* as many new blockSize Memory objects as the ranges require. Partial Memory blocks could
|
||||
* also be supported in theory, but in practice, they're not.
|
||||
*
|
||||
* Because MemoryPDP11 blocks now allow us to have a "sparse" address space, we could choose to
|
||||
* Because Memory blocks now allow us to have a "sparse" address space, we could choose to
|
||||
* take the memory hit of allocating 4K arrays per block, where each element stores only one byte,
|
||||
* instead of the more frugal but slightly slower approach of allocating arrays of 32-bit dwords
|
||||
* (LONGARRAYS) and shifting/masking bytes/words to/from dwords; in theory, byte accesses would
|
||||
|
|
@ -84,7 +84,7 @@ var littleEndian = (TYPEDARRAYS? (function() {
|
|||
* size at this point. Also, not all JavaScript implementations support TYPEDARRAYS (IE9 is probably
|
||||
* the only real outlier: it lacks typed arrays but otherwise has all the necessary HTML5 support).
|
||||
*
|
||||
* WARNING: Since MemoryPDP11 blocks are low-level objects that have no UI requirements, they
|
||||
* WARNING: Since Memory blocks are low-level objects that have no UI requirements, they
|
||||
* do not inherit from the Component class, so if you want to use any Component class methods,
|
||||
* such as Component.assert(), use the corresponding Debugger methods instead (assuming a debugger
|
||||
* is available).
|
||||
|
|
@ -188,7 +188,7 @@ function MemoryPDP11(addr, used, size, type, controller)
|
|||
* 'little endian") storage. ROM is equally conventional, except that the fReadOnly property is set,
|
||||
* disabling writes. VIDEO is treated exactly like RAM, unless a controller is provided. Both RAM and
|
||||
* VIDEO memory are always considered writable, and even ROM can be written using the Bus setByteDirect()
|
||||
* interface (which in turn uses the MemoryPDP11 writeByteDirect() interface), allowing the ROM component to
|
||||
* interface (which in turn uses the Memory writeByteDirect() interface), allowing the ROM component to
|
||||
* initialize its own memory. The CONTROLLER type is used to identify memory-mapped devices that do not
|
||||
* need any default storage and always provide their own controller.
|
||||
*
|
||||
|
|
@ -237,7 +237,7 @@ MemoryPDP11.prototype = {
|
|||
/**
|
||||
* init(addr)
|
||||
*
|
||||
* Quick reinitializer when reusing a MemoryPDP11 block.
|
||||
* Quick reinitializer when reusing a Memory block.
|
||||
*
|
||||
* @this {MemoryPDP11}
|
||||
* @param {number} addr
|
||||
|
|
@ -248,7 +248,7 @@ MemoryPDP11.prototype = {
|
|||
/**
|
||||
* clone(mem, type)
|
||||
*
|
||||
* Converts the current MemoryPDP11 block (this) into a clone of the given MemoryPDP11 block (mem),
|
||||
* Converts the current Memory block (this) into a clone of the given Memory block (mem),
|
||||
* and optionally overrides the current block's type with the specified type.
|
||||
*
|
||||
* @this {MemoryPDP11}
|
||||
|
|
@ -289,10 +289,10 @@ MemoryPDP11.prototype = {
|
|||
/**
|
||||
* save()
|
||||
*
|
||||
* This gets the contents of a MemoryPDP11 block as an array of 32-bit values; used by BusPDP11.saveMemory(),
|
||||
* which in turn is called by CPUStatePDP11.save().
|
||||
* This gets the contents of a Memory block as an array of 32-bit values; used by Bus.saveMemory(),
|
||||
* which in turn is called by CPUState.save().
|
||||
*
|
||||
* MemoryPDP11 blocks with custom memory controllers do NOT save their contents; that's the responsibility
|
||||
* Memory blocks with custom memory controllers do NOT save their contents; that's the responsibility
|
||||
* of the controller component.
|
||||
*
|
||||
* @this {MemoryPDP11}
|
||||
|
|
@ -335,9 +335,9 @@ MemoryPDP11.prototype = {
|
|||
/**
|
||||
* restore(adw)
|
||||
*
|
||||
* This restores the contents of a MemoryPDP11 block from an array of 32-bit values;
|
||||
* used by BusPDP11.restoreMemory(), which is called by CPUStatePDP11.restore(), after all other
|
||||
* components have been restored and thus all MemoryPDP11 blocks have been allocated
|
||||
* This restores the contents of a Memory block from an array of 32-bit values;
|
||||
* used by Bus.restoreMemory(), which is called by CPUState.restore(), after all other
|
||||
* components have been restored and thus all Memory blocks have been allocated
|
||||
* by their respective components.
|
||||
*
|
||||
* @this {MemoryPDP11}
|
||||
|
|
@ -386,7 +386,7 @@ MemoryPDP11.prototype = {
|
|||
* The afn parameter should be a 4-entry function table containing two byte handlers and
|
||||
* two word handlers. See the static afnMemory table for an example.
|
||||
*
|
||||
* If no function table is specified, a default is selected based on the MemoryPDP11 type;
|
||||
* If no function table is specified, a default is selected based on the Memory type;
|
||||
* similarly, any undefined entries in the table are filled with default handlers that fall
|
||||
* back to the byte handlers, and if one or both byte handlers are undefined, they default
|
||||
* to handlers that simply ignore the access.
|
||||
|
|
@ -469,7 +469,7 @@ MemoryPDP11.prototype = {
|
|||
*/
|
||||
printAddr: function(sMessage) {
|
||||
if (DEBUG && this.dbg && this.dbg.messageEnabled(MessagesPDP11.MEM)) {
|
||||
this.dbg.printMessage(sMessage + ' ' + (this.addr != null? ('%' + str.toHex(this.addr)) : '#' + this.id), true);
|
||||
this.dbg.printMessage(sMessage + ' ' + (this.addr != null? ('@' + this.dbg.toStrBase(this.addr)) : '#' + this.id), true);
|
||||
}
|
||||
},
|
||||
/**
|
||||
|
|
@ -548,7 +548,7 @@ MemoryPDP11.prototype = {
|
|||
* that a system would require nonexistent memory locations to return ALL bits set.
|
||||
*
|
||||
* Also, I'm reluctant to address that potential issue by simply returning -1, because to date, the above
|
||||
* MemoryPDP11 interfaces have always returned values that are properly masked to 8, 16 or 32 bits, respectively.
|
||||
* Memory interfaces have always returned values that are properly masked to 8, 16 or 32 bits, respectively.
|
||||
*
|
||||
* @this {MemoryPDP11}
|
||||
* @param {number} off
|
||||
|
|
@ -557,7 +557,8 @@ MemoryPDP11.prototype = {
|
|||
*/
|
||||
readNone: function readNone(off, addr) {
|
||||
if (DEBUGGER && this.dbg && this.dbg.messageEnabled(MessagesPDP11.MEM) /* && !off */) {
|
||||
this.dbg.message("attempt to read invalid block %" + str.toHex(this.addr), true);
|
||||
this.dbg.printMessage("attempt to read invalid block %" + str.toHex(this.addr), true);
|
||||
this.dbg.stopCPU();
|
||||
}
|
||||
return 0xff;
|
||||
},
|
||||
|
|
@ -571,7 +572,8 @@ MemoryPDP11.prototype = {
|
|||
*/
|
||||
writeNone: function writeNone(off, v, addr) {
|
||||
if (DEBUGGER && this.dbg && this.dbg.messageEnabled(MessagesPDP11.MEM) /* && !off */) {
|
||||
this.dbg.message("attempt to write " + str.toHexWord(v) + " to invalid block %" + str.toHex(this.addr), true);
|
||||
this.dbg.printMessage("attempt to write " + str.toHexWord(v) + " to invalid block %" + str.toHex(this.addr), true);
|
||||
this.dbg.stopCPU();
|
||||
}
|
||||
},
|
||||
/**
|
||||
|
|
@ -755,7 +757,7 @@ MemoryPDP11.prototype = {
|
|||
readByteLE: function readByteLE(off, addr) {
|
||||
var b = this.ab[off];
|
||||
if (DEBUGGER && this.dbg && this.dbg.messageEnabled(MessagesPDP11.MEM)) {
|
||||
this.dbg.message("Memory.readByteLE(" + this.dbg.toStrBase(addr) + "): " + this.dbg.toStrBase(b), true);
|
||||
this.dbg.printMessage("Memory.readByte(" + this.dbg.toStrBase(addr) + "): " + this.dbg.toStrBase(b), true);
|
||||
}
|
||||
return b;
|
||||
},
|
||||
|
|
@ -785,7 +787,7 @@ MemoryPDP11.prototype = {
|
|||
*/
|
||||
var w = (off & 0x1)? (this.ab[off] | (this.ab[off+1] << 8)) : this.aw[off >> 1];
|
||||
if (DEBUGGER && this.dbg && this.dbg.messageEnabled(MessagesPDP11.MEM)) {
|
||||
this.dbg.message("Memory.readWordLE(" + this.dbg.toStrBase(addr) + "): " + this.dbg.toStrBase(w), true);
|
||||
this.dbg.printMessage("Memory.readWord(" + this.dbg.toStrBase(addr) + "): " + this.dbg.toStrBase(w), true);
|
||||
}
|
||||
return w;
|
||||
},
|
||||
|
|
@ -813,7 +815,7 @@ MemoryPDP11.prototype = {
|
|||
this.ab[off] = b;
|
||||
this.fDirty = true;
|
||||
if (DEBUGGER && this.dbg && this.dbg.messageEnabled(MessagesPDP11.MEM)) {
|
||||
this.dbg.message("Memory.writeByteLE(" + this.dbg.toStrBase(addr) + "," + this.dbg.toStrBase(b) + ")", true);
|
||||
this.dbg.printMessage("Memory.writeByte(" + this.dbg.toStrBase(addr) + "," + this.dbg.toStrBase(b) + ")", true);
|
||||
}
|
||||
},
|
||||
/**
|
||||
|
|
@ -849,7 +851,7 @@ MemoryPDP11.prototype = {
|
|||
}
|
||||
this.fDirty = true;
|
||||
if (DEBUGGER && this.dbg && this.dbg.messageEnabled(MessagesPDP11.MEM)) {
|
||||
this.dbg.message("Memory.writeWordLE(" + this.dbg.toStrBase(addr) + "," + this.dbg.toStrBase(w) + ")", true);
|
||||
this.dbg.printMessage("Memory.writeWord(" + this.dbg.toStrBase(addr) + "," + this.dbg.toStrBase(w) + ")", true);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -342,7 +342,9 @@ ROMPDP11.prototype.addROM = function(addr)
|
|||
if (DEBUG) this.log("addROM(): copying ROM to " + str.toHexLong(addr) + " (" + str.toHexLong(this.abROM.length) + " bytes)");
|
||||
var i;
|
||||
for (i = 0; i < this.abROM.length; i++) {
|
||||
if (DEBUGGER && this.dbg) this.dbg.disableMessages();
|
||||
this.bus.setByteDirect(addr + i, this.abROM[i]);
|
||||
if (DEBUGGER && this.dbg) this.dbg.enableMessages();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
694
modules/pdp11/lib/serialport.js
Normal file
694
modules/pdp11/lib/serialport.js
Normal file
|
|
@ -0,0 +1,694 @@
|
|||
/**
|
||||
* @fileoverview Implements the PDP11 SerialPort component.
|
||||
* @author <a href="mailto:Jeff@pcjs.org">Jeff Parsons</a>
|
||||
* @version 1.0
|
||||
* Created 2016-Oct-04
|
||||
*
|
||||
* This file is part of PCjs, a computer emulation software project at <http://pcjs.org/>.
|
||||
*
|
||||
* It has been adapted from the JavaScript PDP 11/70 Emulator v1.3 written by Paul Nankervis
|
||||
* (paulnank@hotmail.com) as of August 2016 from http://skn.noip.me/pdp11/pdp11.html. This code
|
||||
* may be used freely provided the original author name is acknowledged in any modified source code.
|
||||
*
|
||||
* PCjs is free software: you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation, either version 3
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCjs is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
|
||||
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCjs. If not,
|
||||
* see <http://www.gnu.org/licenses/gpl.html>.
|
||||
*
|
||||
* You are required to include the above copyright notice in every source code file of every
|
||||
* copy or modified version of this work, and to display that copyright notice on every screen
|
||||
* that loads or runs any version of this software (see COPYRIGHT in /modules/shared/lib/defines.js).
|
||||
*
|
||||
* Some PCjs files also attempt to load external resource files, such as character-image files,
|
||||
* ROM files, and disk image files. Those external resource files are not considered part of PCjs
|
||||
* for purposes of the GNU General Public License, and the author does not claim any copyright
|
||||
* as to their contents.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
if (NODE) {
|
||||
var str = require("../../shared/lib/strlib");
|
||||
var web = require("../../shared/lib/weblib");
|
||||
var Component = require("../../shared/lib/component");
|
||||
var State = require("../../shared/lib/state");
|
||||
var PDP11 = require("./defines");
|
||||
var MessagesPDP11 = require("./messages");
|
||||
}
|
||||
|
||||
/**
|
||||
* SerialPortPDP11(parmsSerial)
|
||||
*
|
||||
* The SerialPort component has the following component-specific (parmsSerial) properties:
|
||||
*
|
||||
* adapter: adapter number; 0 if not defined
|
||||
*
|
||||
* binding: name of a control (based on its "binding" attribute) to bind to this port's I/O
|
||||
*
|
||||
* tabSize: set to a non-zero number to convert tabs to spaces (applies only to output to
|
||||
* the above binding); default is 0 (no conversion)
|
||||
*
|
||||
* In the future, we may support 'port' and 'irq' properties that allow the machine to define a
|
||||
* non-standard serial port configuration, instead of only our pre-defined 'adapter' configurations.
|
||||
*
|
||||
* NOTE: Since the XSL file defines 'adapter' as a number, not a string, there's no need to use
|
||||
* parseInt(), and as an added benefit, we don't need to worry about whether a hex or decimal format
|
||||
* was used.
|
||||
*
|
||||
* @constructor
|
||||
* @extends Component
|
||||
* @param {Object} parmsSerial
|
||||
*/
|
||||
function SerialPortPDP11(parmsSerial) {
|
||||
|
||||
this.iAdapter = parmsSerial['adapter'];
|
||||
|
||||
this.tty = {
|
||||
rbuf: [],
|
||||
rcsr: 0,
|
||||
xcsr: PDP11.DL11.XCSR.READY,
|
||||
delCode: 127,
|
||||
del: 0
|
||||
};
|
||||
|
||||
/**
|
||||
* consoleOutput becomes a string that records serial port output if the 'binding' property is set to the
|
||||
* reserved name "console". Nothing is written to the console, however, until a linefeed (0x0A) is output
|
||||
* or the string length reaches a threshold (currently, 1024 characters).
|
||||
*
|
||||
* @type {string|null}
|
||||
*/
|
||||
this.consoleOutput = null;
|
||||
|
||||
/**
|
||||
* controlIOBuffer is a DOM element bound to the port (currently used for output only; see transmitByte()).
|
||||
*
|
||||
* Example: CTTY COM2
|
||||
*
|
||||
* The CTTY DOS command redirects all CON I/O to the specified serial port (eg, COM2), which it assumes is
|
||||
* connected to a serial terminal, and therefore anything it *transmits* via COM2 will be displayed by the
|
||||
* terminal. It further assumes that anything typed on such a terminal is NOT displayed, so as DOS *receives*
|
||||
* serial input, DOS *transmits* the appropriate characters back to the terminal via COM2.
|
||||
*
|
||||
* As a result, controlIOBuffer only needs to be updated by the transmitByte() function.
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
this.controlIOBuffer = null;
|
||||
|
||||
/*
|
||||
* If controlIOBuffer is being used AND 'tabSize' is set, then we make an attempt to monitor the characters
|
||||
* being echoed via transmitByte(), maintain a logical column position, and convert any tabs into the appropriate
|
||||
* number of spaces.
|
||||
*
|
||||
* charBOL, if nonzero, is a character to automatically output at the beginning of every line. This probably
|
||||
* isn't generally useful; I use it internally to preformat serial output.
|
||||
*/
|
||||
this.tabSize = parmsSerial['tabSize'];
|
||||
this.charBOL = parmsSerial['charBOL'];
|
||||
this.iLogicalCol = 0;
|
||||
|
||||
Component.call(this, "SerialPort", parmsSerial, SerialPortPDP11, MessagesPDP11.SERIAL);
|
||||
|
||||
var sBinding = parmsSerial['binding'];
|
||||
if (sBinding == "console") {
|
||||
this.consoleOutput = "";
|
||||
} else {
|
||||
/*
|
||||
* NOTE: If sBinding is not the name of a valid Control Panel DOM element, this call does nothing.
|
||||
*/
|
||||
Component.bindExternalControl(this, sBinding, SerialPortPDP11.sIOBuffer);
|
||||
}
|
||||
|
||||
/*
|
||||
* No connection until initConnection() is called.
|
||||
*/
|
||||
this.sDataReceived = "";
|
||||
this.connection = this.sendData = null;
|
||||
|
||||
/*
|
||||
* Export all functions required by initConnection(); currently, this is the bare minimum (no flow control yet).
|
||||
*/
|
||||
this['exports'] = {
|
||||
'connect': this.initConnection,
|
||||
'receiveData': this.receiveData
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
* class SerialPortPDP11
|
||||
* property {number} iAdapter
|
||||
* property {number} portBase
|
||||
* property {number} nIRQ
|
||||
* property {Object} controlIOBuffer is a DOM element bound to the port (for rudimentary output; see transmitByte())
|
||||
*
|
||||
* NOTE: This class declaration started as a way of informing the code inspector of the controlIOBuffer property,
|
||||
* which remained undefined until a setBinding() call set it later, but I've since decided that explicitly
|
||||
* initializing such properties in the constructor is a better way to go -- even though it's more code -- because
|
||||
* JavaScript compilers are supposed to be happier when the underlying object structures aren't constantly changing.
|
||||
*
|
||||
* Besides, I'm not sure I want to get into documenting every property this way, for this or any/every other class,
|
||||
* let alone getting into which ones should be considered private or protected, because PCjs isn't really a library
|
||||
* for third-party apps.
|
||||
*/
|
||||
|
||||
Component.subclass(SerialPortPDP11);
|
||||
|
||||
/*
|
||||
* Internal name used for the I/O buffer control, if any, that we bind to the SerialPort.
|
||||
*
|
||||
* Alternatively, if SerialPort wants to use another component's control (eg, the Panel's
|
||||
* "print" control), it can specify the name of that control with the 'binding' property.
|
||||
*
|
||||
* For that binding to succeed, we also need to know the target component; for now, that's
|
||||
* been hard-coded to "Panel", in part because that's one of the few components we can rely
|
||||
* upon initializing before we do, but it would be a simple matter to include a component type
|
||||
* or ID as part of the 'binding' property as well, if we need more flexibility later.
|
||||
*/
|
||||
SerialPortPDP11.sIOBuffer = "buffer";
|
||||
|
||||
/**
|
||||
* setBinding(sHTMLType, sBinding, control, sValue)
|
||||
*
|
||||
* @this {SerialPortPDP11}
|
||||
* @param {string|null} sHTMLType is the type of the HTML control (eg, "button", "list", "text", "submit", "textarea", "canvas")
|
||||
* @param {string} sBinding is the value of the 'binding' parameter stored in the HTML control's "data-value" attribute (eg, "buffer")
|
||||
* @param {Object} control is the HTML control DOM object (eg, HTMLButtonElement)
|
||||
* @param {string} [sValue] optional data value
|
||||
* @return {boolean} true if binding was successful, false if unrecognized binding request
|
||||
*/
|
||||
SerialPortPDP11.prototype.setBinding = function(sHTMLType, sBinding, control, sValue)
|
||||
{
|
||||
var serial = this;
|
||||
|
||||
switch (sBinding) {
|
||||
case SerialPortPDP11.sIOBuffer:
|
||||
this.bindings[sBinding] = this.controlIOBuffer = control;
|
||||
|
||||
/*
|
||||
* By establishing an onkeypress handler here, we make it possible for DOS commands like
|
||||
* "CTTY COM1" to more or less work (use "CTTY CON" to restore control to the DOS console).
|
||||
*/
|
||||
control.onkeydown = function onKeyDown(event) {
|
||||
/*
|
||||
* This is required in addition to onkeypress, because it's the only way to prevent
|
||||
* BACKSPACE (keyCode 8) from being interpreted by the browser as a "Back" operation;
|
||||
* moreover, not all browsers generate an onkeypress notification for BACKSPACE.
|
||||
*
|
||||
* A related problem exists for Ctrl-key combinations in most Windows-based browsers
|
||||
* (eg, IE, Edge, Chrome for Windows, etc), because keys like Ctrl-C and Ctrl-S have
|
||||
* special meanings (eg, Copy, Save). To the extent the browser will allow it, we
|
||||
* attempt to disable that default behavior when this control receives an onkeydown
|
||||
* event for one of those keys (probably the only event the browser generates for them).
|
||||
*/
|
||||
event = event || window.event;
|
||||
var keyCode = event.keyCode;
|
||||
if (keyCode === 0x08 || event.ctrlKey && keyCode >= 0x41 && keyCode <= 0x5A) {
|
||||
if (event.preventDefault) event.preventDefault();
|
||||
if (keyCode > 0x40) keyCode -= 0x40;
|
||||
serial.receiveData(keyCode);
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
control.onkeypress = function onKeyPress(event) {
|
||||
/*
|
||||
* Browser-independent keyCode extraction; refer to onKeyPress() and the other key event
|
||||
* handlers in keyboard.js.
|
||||
*/
|
||||
event = event || window.event;
|
||||
var keyCode = event.which || event.keyCode;
|
||||
serial.receiveData(keyCode);
|
||||
/*
|
||||
* Since we're going to remove the "readonly" attribute from the <textarea> control
|
||||
* (so that the soft keyboard activates on iOS), instead of calling preventDefault() for
|
||||
* selected keys (eg, the SPACE key, whose default behavior is to scroll the page), we must
|
||||
* now call it for *all* keys, so that the keyCode isn't added to the control immediately,
|
||||
* on top of whatever the machine is echoing back, resulting in double characters.
|
||||
*/
|
||||
if (event.preventDefault) event.preventDefault();
|
||||
return true;
|
||||
};
|
||||
|
||||
/*
|
||||
* Now that we've added an onkeypress handler that calls preventDefault() for ALL keys, the control
|
||||
* itself no longer needs the "readonly" attribute; we primarily need to remove it for iOS browsers,
|
||||
* so that the soft keyboard will activate, but it shouldn't hurt to remove the attribute for all browsers.
|
||||
*/
|
||||
control.removeAttribute("readonly");
|
||||
|
||||
return true;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* initBus(cmp, bus, cpu, dbg)
|
||||
*
|
||||
* @this {SerialPortPDP11}
|
||||
* @param {ComputerPDP11} cmp
|
||||
* @param {BusPDP11} bus
|
||||
* @param {CPUStatePDP11} cpu
|
||||
* @param {DebuggerPDP11} dbg
|
||||
*/
|
||||
SerialPortPDP11.prototype.initBus = function(cmp, bus, cpu, dbg)
|
||||
{
|
||||
this.cmp = cmp;
|
||||
this.bus = bus;
|
||||
this.cpu = cpu;
|
||||
this.dbg = dbg;
|
||||
|
||||
bus.addIOTable(this, SerialPortPDP11.UNIBUS_IOTABLE);
|
||||
this.setReady();
|
||||
};
|
||||
|
||||
/**
|
||||
* readRCSR(addr)
|
||||
*
|
||||
* @this {SerialPortPDP11}
|
||||
* @param {number} addr (eg, PDP11.UNIBUS.RCSR or 177560)
|
||||
* @return {number}
|
||||
*/
|
||||
SerialPortPDP11.prototype.readRCSR = function(addr)
|
||||
{
|
||||
return this.tty.rcsr;
|
||||
};
|
||||
|
||||
/**
|
||||
* writeRCSR(data, addr)
|
||||
*
|
||||
* @this {SerialPortPDP11}
|
||||
* @param {number} data
|
||||
* @param {number} addr (eg, PDP11.UNIBUS.RCSR or 177560)
|
||||
*/
|
||||
SerialPortPDP11.prototype.writeRCSR = function(data, addr)
|
||||
{
|
||||
this.tty.rcsr = (this.tty.rcsr & 0x80) | (data & ~0x80);
|
||||
};
|
||||
|
||||
/**
|
||||
* readRBUF(addr)
|
||||
*
|
||||
* @this {SerialPortPDP11}
|
||||
* @param {number} addr (eg, PDP11.UNIBUS.RBUF or 177562)
|
||||
* @return {number}
|
||||
*/
|
||||
SerialPortPDP11.prototype.readRBUF = function(addr)
|
||||
{
|
||||
return 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* writeRBUF(data, addr)
|
||||
*
|
||||
* @this {SerialPortPDP11}
|
||||
* @param {number} data
|
||||
* @param {number} addr (eg, PDP11.UNIBUS.RBUF or 177562)
|
||||
*/
|
||||
SerialPortPDP11.prototype.writeRBUF = function(data, addr)
|
||||
{
|
||||
};
|
||||
|
||||
/**
|
||||
* readXCSR(addr)
|
||||
*
|
||||
* @this {SerialPortPDP11}
|
||||
* @param {number} addr (eg, PDP11.UNIBUS.XCSR or 177564)
|
||||
* @return {number}
|
||||
*/
|
||||
SerialPortPDP11.prototype.readXCSR = function(addr)
|
||||
{
|
||||
return this.tty.xcsr;
|
||||
};
|
||||
|
||||
/**
|
||||
* writeXCSR(data, addr)
|
||||
*
|
||||
* @this {SerialPortPDP11}
|
||||
* @param {number} data
|
||||
* @param {number} addr (eg, PDP11.UNIBUS.XCSR or 177564)
|
||||
*/
|
||||
SerialPortPDP11.prototype.writeXCSR = function(data, addr)
|
||||
{
|
||||
/*
|
||||
* If the device is READY, and INT_ENABLE is transitioning to *set*, then generate an interrupt.
|
||||
*/
|
||||
if ((this.tty.xcsr & (PDP11.DL11.XCSR.READY | PDP11.DL11.XCSR.INT_ENABLE)) == PDP11.DL11.XCSR.READY && (data & PDP11.DL11.XCSR.INT_ENABLE)) {
|
||||
var device = this;
|
||||
this.cpu.interrupt(PDP11.DL11.DELAY, PDP11.DL11.PRI, PDP11.DL11.VEC, function() {
|
||||
device.tty.xcsr |= PDP11.DL11.XCSR.READY;
|
||||
return !!(device.tty.xcsr & PDP11.DL11.XCSR.INT_ENABLE);
|
||||
});
|
||||
}
|
||||
/*
|
||||
* In any event, propagate all bits from data *except* for READY.
|
||||
*/
|
||||
this.tty.xcsr = (this.tty.xcsr & PDP11.DL11.XCSR.READY) | (data & ~PDP11.DL11.XCSR.READY);
|
||||
};
|
||||
|
||||
/**
|
||||
* readXBUF(addr)
|
||||
*
|
||||
* @this {SerialPortPDP11}
|
||||
* @param {number} addr (eg, PDP11.UNIBUS.XBUF or 177566)
|
||||
* @return {number}
|
||||
*/
|
||||
SerialPortPDP11.prototype.readXBUF = function(addr)
|
||||
{
|
||||
return 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* writeXBUF(data, addr)
|
||||
*
|
||||
* @this {SerialPortPDP11}
|
||||
* @param {number} data
|
||||
* @param {number} addr (eg, PDP11.UNIBUS.XBUF or 177566)
|
||||
*/
|
||||
SerialPortPDP11.prototype.writeXBUF = function(data, addr)
|
||||
{
|
||||
// data &= 0x7f;
|
||||
// if (data) {
|
||||
// switch (data) {
|
||||
// case 0:
|
||||
// case 0xD: /*015*/
|
||||
// /*
|
||||
// if (document.forms.console.line.value.length > 9000) {
|
||||
// document.forms.console.line.value = document.forms.console.line.value.substring(document.forms.console.line.value.length - 8192);
|
||||
// }
|
||||
// */
|
||||
// break;
|
||||
// case 0x8: /*010*/
|
||||
// /*
|
||||
// if (!tty.del) {
|
||||
// document.forms.console.line.value = document.forms.console.line.value.substring(0, document.forms.console.line.value.length - 1);
|
||||
// }
|
||||
// */
|
||||
// break;
|
||||
// default:
|
||||
// /*
|
||||
// document.forms.console.line.value += String.fromCharCode(data);
|
||||
// if (data == 0x0A) {
|
||||
// document.forms.console.line.scrollTop = document.forms.console.line.scrollHeight;
|
||||
// }
|
||||
// */
|
||||
// }
|
||||
// tty.del = 0;
|
||||
// }
|
||||
// tty.xcsr &= ~0x80; /*0200*/
|
||||
// cpu.interrupt(100, 4, 0x34, /*064*/ function() {
|
||||
// tty.xcsr |= 0x80; /*0200*/
|
||||
// return !!(tty.xcsr & 0x40); /*0100*/
|
||||
// });
|
||||
};
|
||||
|
||||
/**
|
||||
* initConnection()
|
||||
*
|
||||
* 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}".
|
||||
*
|
||||
* 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)
|
||||
*
|
||||
* 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
|
||||
* directions should be established.
|
||||
*
|
||||
* For added robustness, if the target machine initializes much more slowly than we do, and our connection attempt
|
||||
* fails, that's OK, because when it finally initializes, its initConnection() will call our initConnection();
|
||||
* if we've already initialized, no harm done.
|
||||
*
|
||||
* @this {SerialPortPDP11}
|
||||
*/
|
||||
SerialPortPDP11.prototype.initConnection = function()
|
||||
{
|
||||
if (!this.connection) {
|
||||
var sConnection = this.cmp.getMachineParm("connection");
|
||||
if (sConnection) {
|
||||
var asParts = sConnection.split('->');
|
||||
if (asParts.length == 2) {
|
||||
var sSourceID = str.trim(asParts[0]);
|
||||
if (sSourceID != this.idComponent) return; // this connection string is intended for another instance
|
||||
var sTargetID = str.trim(asParts[1]);
|
||||
this.connection = Component.getComponentByID(sTargetID);
|
||||
if (this.connection) {
|
||||
var exports = this.connection['exports'];
|
||||
if (exports) {
|
||||
var fnConnect = exports['connect'];
|
||||
if (fnConnect) fnConnect.call(this.connection);
|
||||
this.sendData = exports['receiveData'];
|
||||
if (this.sendData) {
|
||||
this.status(this.idMachine + '.' + sSourceID + " connected to " + sTargetID);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Changed from notice() to status() because sometimes a connection fails simply because one of us is a laggard.
|
||||
*/
|
||||
this.status("Unable to establish connection: " + sConnection);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* powerUp(data, fRepower)
|
||||
*
|
||||
* @this {SerialPortPDP11}
|
||||
* @param {Object|null} data
|
||||
* @param {boolean} [fRepower]
|
||||
* @return {boolean} true if successful, false if failure
|
||||
*/
|
||||
SerialPortPDP11.prototype.powerUp = function(data, fRepower)
|
||||
{
|
||||
if (!fRepower) {
|
||||
|
||||
/*
|
||||
* This is as late as we can currently wait to make our first inter-machine connection attempt;
|
||||
* even so, the target machine's initialization process may still be ongoing, so any connection
|
||||
* may be not fully resolved until the target machine performs its own initConnection(), which will
|
||||
* in turn invoke our initConnection() again.
|
||||
*/
|
||||
this.initConnection();
|
||||
|
||||
if (!data || !this.restore) {
|
||||
this.reset();
|
||||
} else {
|
||||
if (!this.restore(data)) return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* powerDown(fSave, fShutdown)
|
||||
*
|
||||
* @this {SerialPortPDP11}
|
||||
* @param {boolean} [fSave]
|
||||
* @param {boolean} [fShutdown]
|
||||
* @return {Object|boolean} component state if fSave; otherwise, true if successful, false if failure
|
||||
*/
|
||||
SerialPortPDP11.prototype.powerDown = function(fSave, fShutdown)
|
||||
{
|
||||
return fSave? this.save() : true;
|
||||
};
|
||||
|
||||
/**
|
||||
* reset()
|
||||
*
|
||||
* @this {SerialPortPDP11}
|
||||
*/
|
||||
SerialPortPDP11.prototype.reset = function()
|
||||
{
|
||||
this.tty.rcsr = 0;
|
||||
this.tty.xcsr = 0x80; /*0200*/
|
||||
this.initState();
|
||||
};
|
||||
|
||||
/**
|
||||
* save()
|
||||
*
|
||||
* This implements save support for the SerialPort component.
|
||||
*
|
||||
* @this {SerialPortPDP11}
|
||||
* @return {Object}
|
||||
*/
|
||||
SerialPortPDP11.prototype.save = function()
|
||||
{
|
||||
var state = new State(this);
|
||||
state.set(0, this.saveRegisters());
|
||||
return state.data();
|
||||
};
|
||||
|
||||
/**
|
||||
* restore(data)
|
||||
*
|
||||
* This implements restore support for the SerialPort component.
|
||||
*
|
||||
* @this {SerialPortPDP11}
|
||||
* @param {Object} data
|
||||
* @return {boolean} true if successful, false if failure
|
||||
*/
|
||||
SerialPortPDP11.prototype.restore = function(data)
|
||||
{
|
||||
return this.initState(data[0]);
|
||||
};
|
||||
|
||||
/**
|
||||
* initState(data)
|
||||
*
|
||||
* @this {SerialPortPDP11}
|
||||
* @param {Array} [data]
|
||||
* @return {boolean} true if successful, false if failure
|
||||
*/
|
||||
SerialPortPDP11.prototype.initState = function(data)
|
||||
{
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* saveRegisters()
|
||||
*
|
||||
* @this {SerialPortPDP11}
|
||||
* @return {Array}
|
||||
*/
|
||||
SerialPortPDP11.prototype.saveRegisters = function()
|
||||
{
|
||||
return [];
|
||||
};
|
||||
|
||||
/**
|
||||
* receiveData(data)
|
||||
*
|
||||
* This replaces the old sendRBR() function, which expected an Array of bytes. We still support that,
|
||||
* but in order to support connections with other SerialPort components (ie, the PC8080 SerialPort), we
|
||||
* have added support for numbers and strings as well.
|
||||
*
|
||||
* @this {SerialPortPDP11}
|
||||
* @param {number|string|Array} data
|
||||
* @return {boolean} true if received, false if not
|
||||
*/
|
||||
SerialPortPDP11.prototype.receiveData = function(data)
|
||||
{
|
||||
if (typeof data == "number") {
|
||||
this.abReceive.push(data);
|
||||
}
|
||||
else if (typeof data == "string") {
|
||||
for (var i = 0; i < data.length; i++) {
|
||||
this.abReceive.push(data.charCodeAt(i));
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.abReceive = this.abReceive.concat(data);
|
||||
}
|
||||
// this.advanceRBR();
|
||||
return true; // for now, return true regardless, since we're buffering everything anyway
|
||||
};
|
||||
|
||||
/**
|
||||
* transmitByte(b)
|
||||
*
|
||||
* @this {SerialPortPDP11}
|
||||
* @param {number} b
|
||||
* @return {boolean} true if transmitted, false if not
|
||||
*/
|
||||
SerialPortPDP11.prototype.transmitByte = function(b)
|
||||
{
|
||||
var fTransmitted = false;
|
||||
|
||||
this.printMessage("transmitByte(" + str.toHexByte(b) + ")");
|
||||
|
||||
if (this.sendData) {
|
||||
if (this.sendData.call(this.connection, b)) {
|
||||
fTransmitted = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.controlIOBuffer) {
|
||||
if (b == 0x0D) {
|
||||
this.iLogicalCol = 0;
|
||||
}
|
||||
else 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.
|
||||
*/
|
||||
if (this.iLogicalCol > 0) this.iLogicalCol--;
|
||||
}
|
||||
else {
|
||||
var s = str.toASCIICode(b); // formerly: String.fromCharCode(b);
|
||||
var nChars = s.length; // formerly: (b >= 0x20? 1 : 0);
|
||||
if (b < 0x20 && nChars == 1) nChars = 0;
|
||||
if (b == 0x09) {
|
||||
var tabSize = this.tabSize || 8;
|
||||
nChars = tabSize - (this.iLogicalCol % tabSize);
|
||||
if (this.tabSize) s = str.pad("", nChars);
|
||||
}
|
||||
if (this.charBOL && !this.iLogicalCol && nChars) s = String.fromCharCode(this.charBOL) + s;
|
||||
this.controlIOBuffer.value += s;
|
||||
this.controlIOBuffer.scrollTop = this.controlIOBuffer.scrollHeight;
|
||||
this.iLogicalCol += nChars;
|
||||
}
|
||||
fTransmitted = true;
|
||||
}
|
||||
else if (this.consoleOutput != null) {
|
||||
if (b == 0x0A || this.consoleOutput.length >= 1024) {
|
||||
this.println(this.consoleOutput);
|
||||
this.consoleOutput = "";
|
||||
}
|
||||
if (b != 0x0A) {
|
||||
this.consoleOutput += String.fromCharCode(b);
|
||||
}
|
||||
fTransmitted = true;
|
||||
}
|
||||
|
||||
return fTransmitted;
|
||||
};
|
||||
|
||||
/*
|
||||
* ES6 ALERT: As you can see below, I've finally started using computed property names.
|
||||
*/
|
||||
SerialPortPDP11.UNIBUS_IOTABLE = {
|
||||
[PDP11.UNIBUS.RCSR]: /* 177560 */ [null, null, SerialPortPDP11.prototype.readRCSR, SerialPortPDP11.prototype.writeRCSR, "RCSR"],
|
||||
[PDP11.UNIBUS.RBUF]: /* 177562 */ [null, null, SerialPortPDP11.prototype.readRBUF, SerialPortPDP11.prototype.writeRBUF, "RBUF"],
|
||||
[PDP11.UNIBUS.XCSR]: /* 177564 */ [null, null, SerialPortPDP11.prototype.readXCSR, SerialPortPDP11.prototype.writeXCSR, "XCSR"],
|
||||
[PDP11.UNIBUS.XBUF]: /* 177566 */ [null, null, SerialPortPDP11.prototype.readXBUF, SerialPortPDP11.prototype.writeXBUF, "XBUF"]
|
||||
};
|
||||
|
||||
/**
|
||||
* SerialPortPDP11.init()
|
||||
*
|
||||
* This function operates on every HTML element of class "serial", extracting the
|
||||
* JSON-encoded parameters for the SerialPort constructor from the element's "data-value"
|
||||
* attribute, invoking the constructor to create a SerialPort component, and then binding
|
||||
* any associated HTML controls to the new component.
|
||||
*/
|
||||
SerialPortPDP11.init = function()
|
||||
{
|
||||
var aeSerial = Component.getElementsByClass(document, PDP11.APPCLASS, "serial");
|
||||
for (var iSerial = 0; iSerial < aeSerial.length; iSerial++) {
|
||||
var eSerial = aeSerial[iSerial];
|
||||
var parmsSerial = Component.getComponentParms(eSerial);
|
||||
var serial = new SerialPortPDP11(parmsSerial);
|
||||
Component.bindComponentControls(serial, eSerial, PDP11.APPCLASS);
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Initialize every SerialPort module on the page.
|
||||
*/
|
||||
web.onInit(SerialPortPDP11.init);
|
||||
|
||||
if (NODE) module.exports = SerialPortPDP11;
|
||||
|
|
@ -806,7 +806,7 @@ Component.prototype = {
|
|||
}
|
||||
},
|
||||
/**
|
||||
* println(s, type)
|
||||
* println(s, type, id)
|
||||
*
|
||||
* For non-diagnostic messages, which components may override to control the destination/appearance of their output.
|
||||
*
|
||||
|
|
@ -1027,7 +1027,7 @@ Component.prototype = {
|
|||
printMessage: function(sMessage, bitsMessage, fAddress) {
|
||||
if (DEBUGGER && this.dbg) {
|
||||
if (bitsMessage === true || this.messageEnabled(bitsMessage | 0)) {
|
||||
this.dbg.message(sMessage, fAddress);
|
||||
this.dbg.message(sMessage, fAddress || bitsMessage === true);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -275,7 +275,7 @@ if (DEBUGGER) {
|
|||
* Also, to allow quoted strings *inside* breakpoint commands, we first replace all
|
||||
* DOUBLE double-quotes with single quotes.
|
||||
*/
|
||||
sCmd = sCmd.toLowerCase().replace(/""/g, "'");
|
||||
sCmd = sCmd.replace(/""/g, "'");
|
||||
|
||||
var iPrev = 0;
|
||||
var chQuote = null;
|
||||
|
|
@ -604,6 +604,9 @@ if (DEBUGGER) {
|
|||
if (value !== undefined) {
|
||||
fDefined = true;
|
||||
sValue = str.toHex(value, 0, true) + " " + value + ". " + str.toOct(value, 0, true) + " " + str.toBinBytes(value, 0, true);
|
||||
if (value >= 0x20 && value < 0x7F) {
|
||||
sValue += " '" + String.fromCharCode(value) + "'";
|
||||
}
|
||||
}
|
||||
sVar = (sVar != null? (sVar + ": ") : "");
|
||||
this.println(sVar + sValue);
|
||||
|
|
|
|||
|
|
@ -196,6 +196,7 @@
|
|||
"./modules/pdp11/lib/rom.js",
|
||||
"./modules/pdp11/lib/ram.js",
|
||||
"./modules/pdp11/lib/keyboard.js",
|
||||
"./modules/pdp11/lib/serialport.js",
|
||||
"./modules/shared/lib/debugger.js",
|
||||
"./modules/pdp11/lib/debugger.js",
|
||||
"./modules/pdp11/lib/computer.js",
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Loading…
Reference in a new issue