diff --git a/devices/pdp11/machine/1170/test/machine.xml b/devices/pdp11/machine/1170/test/machine.xml index 40b787114..f10d95591 100644 --- a/devices/pdp11/machine/1170/test/machine.xml +++ b/devices/pdp11/machine/1170/test/machine.xml @@ -5,9 +5,10 @@ - + + - + diff --git a/docs/pcx86/examples/pcx86-dbg.js b/docs/pcx86/examples/pcx86-dbg.js index d66cee40c..27f8c4158 100644 --- a/docs/pcx86/examples/pcx86-dbg.js +++ b/docs/pcx86/examples/pcx86-dbg.js @@ -22,7 +22,7 @@ function ob(a,b){if(void 0!==a){var c;b&&0<(c=b.indexOf("."))&&(a=b.substr(0,c+1 function rb(a,b,c){var d;b&&(void 0===d&&(d="Panel"),(d=pb(d,a.id))&&(b=d.qa[b])&&a.Pb(null,c,b))}function sb(a,b){b=tb(b.parentNode,"pcx86-control");for(var c=0;c=a.lb+b&&(a.xb=b,a.kb++,a.kb>=a.ub&&(a.kb=0,a.be++))}l.Nm=function(){var a=this.F.M&255;!(this.F.G>>8)&&128>8||!this.N)||(a=!(this.N.vc[0].Ad&64));return a?!0:!1}; var Tp={800:Pp.prototype.Lm,801:Pp.prototype.Mm,802:Pp.prototype.Km},Sp={496:Pp.prototype.il,497:Pp.prototype.Sl,498:Pp.prototype.Ul,499:Pp.prototype.Vl,500:Pp.prototype.Ql,501:Pp.prototype.Pl,502:Pp.prototype.Rl,503:Pp.prototype.Wl},Vp={800:Pp.prototype.ho,801:Pp.prototype.ko,802:Pp.prototype.jo,803:Pp.prototype.io,807:Pp.prototype.pi,811:Pp.prototype.pi,815:Pp.prototype.pi},Up={496:Pp.prototype.qn,497:Pp.prototype.vn,498:Pp.prototype.tn,499:Pp.prototype.un,500:Pp.prototype.pn,501:Pp.prototype.on, 502:Pp.prototype.rn,503:Pp.prototype.nn,1014:Pp.prototype.sn};Xa(function(){for(var a=tb(document,"pcx86","hdc"),b=0;b=":6,">":6,"<=":6,"<":6,">>>":7,">>":7,"<<":7,"-":8,"+":8,"%":9,"/":9,"*":9};mq.prototype.oh=function(){return-1};mq.prototype.ph=function(){}; -mq.prototype.Pk=function(a,b){return a.replace("["+b+"]","unimplemented")};mq.prototype.Ng=function(a,b,c){if(b)if(a){0>this.A&&this.C.length&&(this.A=0);if(0>this.A||a!=this.C[this.A])this.C.splice(0,0,a),this.A=0;this.A--}else this.va?a="end":a=this.C[this.A+1];b=[];if(a){a=a.toLowerCase().replace(/""/g,"'");var d=0,e=null;c=c||";";for(var f=0;f<=a.length;f++){var g=a.charAt(f);if('"'==g||"'"==g)e?g==e&&(e=null):e=g;else if(g==c&&!e||!g)b.push(qa(a.substring(d,f))),d=f+1}}return b}; +mq.prototype.Pk=function(a,b){return a.replace("["+b+"]","unimplemented")};mq.prototype.Ng=function(a,b,c){if(b)if(a){0>this.A&&this.C.length&&(this.A=0);if(0>this.A||a!=this.C[this.A])this.C.splice(0,0,a),this.A=0;this.A--}else this.va?a="end":a=this.C[this.A+1];b=[];if(a){a=a.replace(/""/g,"'");var d=0,e=null;c=c||";";for(var f=0;f<=a.length;f++){var g=a.charAt(f);if('"'==g||"'"==g)e?g==e&&(e=null):e=g;else if(g==c&&!e||!g)b.push(qa(a.substring(d,f))),d=f+1}}return b}; function oq(a,b,c){for(c=c||-1;c--&&b.length;){var d=b.pop();if(2>a.length)return!1;var e=a.pop(),f=a.pop();switch(d){case "*":d=f*e;break;case "/":if(!e)return!1;d=f/e;break;case "%":if(!e)return!1;d=f%e;break;case "+":d=f+e;break;case "-":d=f-e;break;case "<<":d=f<>":d=f>>e;break;case ">>>":d=f>>>e;break;case "<":d=f":d=f>e?1:0;break;case ">=":d=f>=e?1:0;break;case "==":d=f==e?1:0;break;case "!=":d=f!=e?1:0;break;case "&":d=f&e;break; case "^":d=f^e;break;case "|":d=f|e;break;case "&&":d=f&&e?1:0;break;case "||":d=f||e?1:0;break;default:return!1}a.push(d|0)}return!0} function pq(a,b,c){var d;if(b){b=qq(a,b);for(var e=0,f=!1,g=b,h=[],k=[],m=b.split(/(\|\||&&|\||^|&|!=|==|>=|>>>|>>|>|<=|<<|<|-|\+|%|\/|\*)/);ec&&(d+=" '"+String.fromCharCode(c)+"'"));a.O((null!=b?b+": ":"")+d);return e}function uq(a,b){if(b)return sq(a,b,a.ya[b]);var c=0;for(b in a.ya)sq(a,b,a.ya[b]),c++;return 0= 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; }; diff --git a/modules/pdp11/lib/debugger.js b/modules/pdp11/lib/debugger.js index 73ba0081a..f10ea3082 100644 --- a/modules/pdp11/lib/debugger.js +++ b/modules/pdp11/lib/debugger.js @@ -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 { - this.println(this.replaceRegs(a[2])); + 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,8 +3754,12 @@ if (DEBUGGER) { } break; } - 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()); + 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]) { @@ -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) { diff --git a/modules/pdp11/lib/device.js b/modules/pdp11/lib/device.js index 181343bd3..8e0f53d25 100644 --- a/modules/pdp11/lib/device.js +++ b/modules/pdp11/lib/device.js @@ -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"] }; diff --git a/modules/pdp11/lib/memory.js b/modules/pdp11/lib/memory.js index c3543e669..6f7f841fc 100644 --- a/modules/pdp11/lib/memory.js +++ b/modules/pdp11/lib/memory.js @@ -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); } } }; diff --git a/modules/pdp11/lib/rom.js b/modules/pdp11/lib/rom.js index be9486101..a8d0844f8 100644 --- a/modules/pdp11/lib/rom.js +++ b/modules/pdp11/lib/rom.js @@ -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; } diff --git a/modules/pdp11/lib/serialport.js b/modules/pdp11/lib/serialport.js new file mode 100644 index 000000000..bb94d0465 --- /dev/null +++ b/modules/pdp11/lib/serialport.js @@ -0,0 +1,694 @@ +/** + * @fileoverview Implements the PDP11 SerialPort component. + * @author Jeff Parsons + * @version 1.0 + * Created 2016-Oct-04 + * + * This file is part of PCjs, a computer emulation software project at . + * + * 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 . + * + * 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