diff --git a/apps/pdp11/tapes/diags/README.md b/apps/pdp11/tapes/diags/README.md index ef5905075..050815b63 100644 --- a/apps/pdp11/tapes/diags/README.md +++ b/apps/pdp11/tapes/diags/README.md @@ -361,3 +361,47 @@ which predates the 11/45 and 11/70, expects this instruction: to trap when SP is 150. That contradicts this newer test (ie, that TST should not cause an overflow trap "BECAUSE TST IS A NON MODIFYING INST"). For this and other reasons, PDPjs now installs different checkStackLimit() handlers based on the CPU model. + +--- + +Here's the next test that PDPjs failed. Again, I found the same sequence of instructions in the 11/40 and 11/45 +version of the diagnostic, at PC 023360 rather than 023450, so I have annotated the code below with DEC's comments: + + 023450: 012704 000001 MOV #1,R4 ;SET R4 + 023454: 006767 000060 SXT 023540 ;PRESET DATA=0 + 023460: 074467 000054 XOR R4,023540 + 023464: 100423 BMI 023534 + 023466: 006304 ASL R4 ;SHIFT R4 + 023470: 102373 BVC 023460 ;UNTIL V SETS (R4=100000) + 023472: 100020 BPL 023534 ;BRANCH IF 'N' IS CLEAR + 023474: 074467 000040 XOR R4,023540 ;XOR6A=177777 + 023500: 100015 BPL 023534 + 023502: 074767 000032 XOR PC,023540 ;XOR PC WITH XOR6A (177777) + 023506: 010767 000030 MOV PC,023542 ;FORM PC AS USED IN XOR ABOVE + 023512: 162767 000004 000022 SUB #4,023542 + 023520: 005167 000016 COM 023542 + 023524: 026767 000012 000006 CMP 023542,023540 ;XOR6A SHOULD = COMPLEMENT OF PC + 023532: 001401 BEQ 023536 + 023534: 104000 EMT 000 ;ERROR: XOR TESTS ABOVE FAILED [They use HLT instead of EMT here] + +In my case, the failure was caused by the XOR instruction: it hadn't been updated along with the rest of the +PDPjs instructions to defer evaluating register operands until BOTH *src* and *dst* operands have been decoded. +So at this point: + + R0=154343 R1=154112 R2=023422 R3=154501 R4=100000 R5=155066 + SP=000700 PC=023502 PS=000010 IR=000000 SL=000377 T0 N1 Z0 V0 C0 + 023502: 074767 000032 XOR PC,023540 ;XOR PC WITH XOR6A (177777) + +the XOR instruction was using 23504 for the *src* operand instead of 23506. + +Interestingly, this test PASSES in SIMH, even though it *also* incorrectly uses 23504 for XOR's *src* operand. +Why? Because when SIMH gets to this instruction: + + 023506: 010767 000030 MOV PC,023542 ;FORM PC AS USED IN XOR ABOVE + +it incorrectly stores 023510 instead of 023512 into memory, and both mistakes end up canceling each other out. + +In fact, I'm less impressed with the accuracy of [SIMH's](https://github.com/simh/simh) PDP-11 emulation +than I used to be, because in the process of getting to this particular test within the "11/70 CPU EXERCISER" +(MAINDEC-11-DEQKC-B1-PB) diagnostic, there were numerous other failures as well (eg, TEST 41 and TEST 45). +However, SIMH is not my baby, so delving into those failures is an exercise for another day. diff --git a/modules/pdp11/lib/cpuops.js b/modules/pdp11/lib/cpuops.js index 6f9e0ae92..069785f56 100644 --- a/modules/pdp11/lib/cpuops.js +++ b/modules/pdp11/lib/cpuops.js @@ -1243,15 +1243,15 @@ PDP11.JSR_CYCLES = [ PDP11.opJSR = function(opCode) { /* - * Since JMP and JSR opcodes have their own unique timings for the various dst modes, we must snapshot - * nStepCycles before decoding the mode, and then use that to update nStepCycles. + * Since JMP and JSR opcodes have their own unique timings for the various dst modes, we must + * snapshot nStepCycles before decoding the mode, and then use that to update nStepCycles. */ this.nSnapCycles = this.nStepCycles; - /* - * TODO: Determine whether or not the SRCMODE operand (regsGen[reg]) should be snapped BEFORE or AFTER we - * decode the DSTMODE operand. Doing it AFTER seems a bit risky. - */ var addr = this.readDstAddr(opCode); + /* + * As per the WARNING in readSrcWord(), reading the SRC register AFTER decoding the DST operand + * is entirely appropriate. + */ var reg = (opCode >> PDP11.SRCMODE.SHIFT) & PDP11.OPREG.MASK; this.pushWord(this.regsGen[reg]); this.regsGen[reg] = this.getPC(); @@ -1883,7 +1883,11 @@ PDP11.opWAIT = function(opCode) PDP11.opXOR = function(opCode) { var reg = (opCode >> PDP11.SRCMODE.SHIFT) & PDP11.OPREG.MASK; - this.updateDstWord(opCode, this.regsGen[reg], PDP11.fnXOR); + /* + * As per the WARNING in readSrcWord(), we must supply a register number rather than a register value, + * which will then be evaluated by updateDstWord() after both the SRC and DST operands have been decoded. + */ + this.updateDstWord(opCode, -reg-1 /* this.regsGen[reg] */, PDP11.fnXOR); this.nStepCycles -= (this.dstMode? (8 + 1) : (2 + 1) + (this.dstReg == 7? 2 : 0)); }; diff --git a/modules/pdp11/lib/cpustate.js b/modules/pdp11/lib/cpustate.js index 208a6b2cf..93ceab729 100644 --- a/modules/pdp11/lib/cpustate.js +++ b/modules/pdp11/lib/cpustate.js @@ -1352,9 +1352,10 @@ CPUStatePDP11.prototype.trap = function(vector, flag, reason) this.setPC(newPC); /* - * DEC's "TRAP TEST" triggers a RESERVED trap with an invalid opcode and the stack deliberately - * set too low, and expects the stack overflow trap to be "sprung" immediately afterward, so we - * only want to "lose interest" in the TRAP flag(s) that were set on entry, not ALL of them. + * DEC's "TRAP TEST" (MAINDEC-11-D0NA-PB) triggers a RESERVED trap with an invalid opcode and the + * stack deliberately set too low, and expects the stack overflow trap to be "sprung" immediately + * afterward, so we only want to "lose interest" in the TRAP flag(s) that were set on entry, not ALL + * of them. * * this.opFlags &= ~PDP11.OPFLAG.TRAP_MASK; // lose interest in traps after an abort * @@ -2227,7 +2228,7 @@ CPUStatePDP11.prototype.readSrcByte = function(opCode) * been decoded and any pre-decrement or post-increment operations affecting the SRC register have * been completed. * - * Here's an example from DEC's "TRAP TEST": + * Here's an example from DEC's "TRAP TEST" (MAINDEC-11-D0NA-PB): * * 007200: 012700 006340 MOV #6340,R0 * 007204: 010020 MOV R0,(R0)+ diff --git a/versions/pdpjs/1.30.3/pdp11-dbg.js b/versions/pdpjs/1.30.3/pdp11-dbg.js index 2670f8785..89f5264f3 100644 --- a/versions/pdpjs/1.30.3/pdp11-dbg.js +++ b/versions/pdpjs/1.30.3/pdp11-dbg.js @@ -153,7 +153,7 @@ function Mf(){this.N&49152?(this.va|=128,this.qa(4,0,-7)):(this.w&&1120==this.fb var Tf=[3,9,9,13,10,14,12,16,4,9,9,13,10,14,13,17];function Uf(a){var b=Ae(this,a);this.I=this.b;S(this,Fe(this,a,b));this.b=this.I-Tf[(this.D?8:0)+this.g]+(7!=this.f||this.g?0:2)}function Vf(a){var b=ze(this,a);S(this,Ee(this,a,b,65535)<<8);this.b-=this.g?9+(this.H&&6<=this.f?1:0):(this.D?5:3)+(7==this.f?2:0)}var Wf=[7,13,13,17,14,18,17,21]; function Xf(a){var b=De(this,a);a=a>>6&7;b&32768&&(b|=-65536);var c=this.u[a];c&32768&&(c|=-65536);b=~~(b*c);this.u[a]=b>>16&65535;this.u[a|1]=b&65535;this.J&128||(this.Z=b>>16,this.$=this.Z|b,this.V=0,this.U=-32768>b||32767>6;if(this.u[b]=this.u[b]-1&65535)R(this,this.u[7]-((a&63)<<1)),this.b+=1;this.b-=6}function dg(a){U(this,a,Ae(this,a),df);this.b-=this.g?9+(this.H&&6<=this.f?1:0):(this.D?5:3)+(7==this.f?2:0)}function eg(a){U(this,a,0,ff);this.b-=this.g?9:3+(7==this.f?2:0)}function fg(){this.qa(28,0,-8);this.b-=5} -function gg(){this.w&&(this.w.oc(this.u[7],!0),this.w.setData(this.u[0],!0));this.J|=4;Qd(this,-2);this.b-=3}function hg(a){U(this,a,this.u[a>>6&7],gf);this.b-=this.g?9:3+(7==this.f?2:0)}function W(a){var b;if(b=this.j)b=this.j,H(b,1)?(F(b,"undefined opcode "+K(b,a),!0,!0),b=Nf(b)):b=!1;b||this.qa(8,0,-8)}function Hd(a){ig[a>>12].call(this,a)}function jg(a){kg[a>>6&3].call(this,a)}function lg(a){mg[a>>6&3].call(this,a)}function ng(a){og[a>>6&3].call(this,a)}function pg(a){qg[a&15].call(this,a)} +function gg(){this.w&&(this.w.oc(this.u[7],!0),this.w.setData(this.u[0],!0));this.J|=4;Qd(this,-2);this.b-=3}function hg(a){U(this,a,-(a>>6&7)-1,gf);this.b-=this.g?9:3+(7==this.f?2:0)}function W(a){var b;if(b=this.j)b=this.j,H(b,1)?(F(b,"undefined opcode "+K(b,a),!0,!0),b=Nf(b)):b=!1;b||this.qa(8,0,-8)}function Hd(a){ig[a>>12].call(this,a)}function jg(a){kg[a>>6&3].call(this,a)}function lg(a){mg[a>>6&3].call(this,a)}function ng(a){og[a>>6&3].call(this,a)}function pg(a){qg[a&15].call(this,a)} function rg(a){sg[a&15].call(this,a)}function tg(a){ug[a>>6&3].call(this,a)}function vg(a){wg[a>>6&3].call(this,a)}function xg(a){yg[a>>6&3].call(this,a)} var ig=[function(a){zg[a>>8&15].call(this,a)},Uf,If,rf,nf,pf,hf,W,function(a){Ag[a>>8&15].call(this,a)},Vf,Jf,sf,of,qf,dg,W],zg=[function(a){Bg[a>>4&15].call(this,a)},Ef,Bf,tf,uf,zf,vf,xf,Sf,Sf,jg,lg,ng,W,W,W],kg=[function(a){ne(this,Fe(this,a,0));this.b-=this.g?9:3+(7==this.f?2:0)},function(a){U(this,a,0,Se);this.b-=this.g?9:3+(7==this.f?2:0)},function(a){U(this,a,1,We);this.b-=this.g?9:3+(7==this.f?2:0)},function(a){U(this,a,1,Ue);this.b-=this.g?9:3+(7==this.f?2:0)}],mg=[function(a){U(this,a,0, Ye);this.b-=this.g?11:6},function(a){U(this,a,Ld(this)?1:0,Ie);this.b-=this.g?9:3+(7==this.f?2:0)},function(a){U(this,a,Ld(this)?1:0,df);this.b-=this.g?9:3+(7==this.f?2:0)},function(a){a=De(this,a);ne(this,a);this.b-=this.g?4:3+(7==this.f?2:0)}],og=[function(a){U(this,a,0,bf);this.b-=this.g?9:3+(7==this.f?2:0)},function(a){U(this,a,0,$e);this.b-=this.g?9:3+(7==this.f?2:0)},function(a){U(this,a,0,Me);this.b-=this.g?9:3+(7==this.f?2:0)},function(a){U(this,a,0,Ke);this.b-=this.g?9:3+(7==this.f?2:0)}], diff --git a/versions/pdpjs/1.30.3/pdp11.js b/versions/pdpjs/1.30.3/pdp11.js index 9fd4f3c15..0f9ada30d 100644 --- a/versions/pdpjs/1.30.3/pdp11.js +++ b/versions/pdpjs/1.30.3/pdp11.js @@ -140,7 +140,7 @@ function de(){this.C&49152?(this.fa|=128,K(this,4,0,-7)):(this.w&&1120==this.La& function ke(a){var b=Wc(this,a);this.I=this.a;R(this,ad(this,a,b));this.a=this.I-je[(this.B?8:0)+this.c]+(7!=this.b||this.c?0:2)}function le(a){var b=Vc(this,a);R(this,$c(this,a,b,65535)<<8);this.a-=this.c?9+(this.D&&6<=this.b?1:0):(this.B?5:3)+(7==this.b?2:0)}var me=[7,13,13,17,14,18,17,21]; function ne(a){var b=Zc(this,a);a=a>>6&7;b&32768&&(b|=-65536);var c=this.g[a];c&32768&&(c|=-65536);b=~~(b*c);this.g[a]=b>>16&65535;this.g[a|1]=b&65535;this.u&128||(this.s=b>>16,this.i=this.s|b,this.f=0,this.m=-32768>b||32767>6;if(this.g[b]=this.g[b]-1&65535)Q(this,this.g[7]-((a&63)<<1)),this.a+=1;this.a-=6}function te(a){U(this,a,Wc(this,a),xd);this.a-=this.c?9+(this.D&&6<=this.b?1:0):(this.B?5:3)+(7==this.b?2:0)}function ue(a){U(this,a,0,zd);this.a-=this.c?9:3+(7==this.b?2:0)}function ve(){K(this,28,0,-8);this.a-=5} -function we(){this.w&&(this.w.ea=this.g[7],this.w.setData(this.g[0],!0));this.u|=4;Hc(this,-2);this.a-=3}function xe(a){U(this,a,this.g[a>>6&7],Ad);this.a-=this.c?9:3+(7==this.b?2:0)}function Y(){K(this,8,0,-8)}function Cc(a){ye[a>>12].call(this,a)}function ze(a){Ae[a>>6&3].call(this,a)}function Be(a){Ce[a>>6&3].call(this,a)}function De(a){Ee[a>>6&3].call(this,a)}function Fe(a){Ge[a&15].call(this,a)}function He(a){Ie[a&15].call(this,a)}function Je(a){Ke[a>>6&3].call(this,a)} +function we(){this.w&&(this.w.ea=this.g[7],this.w.setData(this.g[0],!0));this.u|=4;Hc(this,-2);this.a-=3}function xe(a){U(this,a,-(a>>6&7)-1,Ad);this.a-=this.c?9:3+(7==this.b?2:0)}function Y(){K(this,8,0,-8)}function Cc(a){ye[a>>12].call(this,a)}function ze(a){Ae[a>>6&3].call(this,a)}function Be(a){Ce[a>>6&3].call(this,a)}function De(a){Ee[a>>6&3].call(this,a)}function Fe(a){Ge[a&15].call(this,a)}function He(a){Ie[a&15].call(this,a)}function Je(a){Ke[a>>6&3].call(this,a)} function Le(a){Me[a>>6&3].call(this,a)}function Ne(a){Oe[a>>6&3].call(this,a)} var ye=[function(a){Pe[a>>8&15].call(this,a)},ke,$d,Kd,Gd,Id,Bd,Y,function(a){Qe[a>>8&15].call(this,a)},le,ae,Ld,Hd,Jd,te,Y],Pe=[function(a){Re[a>>4&15].call(this,a)},Xd,Ud,Md,Nd,Sd,Od,Qd,ie,ie,ze,Be,De,Y,Y,Y],Ae=[function(a){Kc(this,ad(this,a,0));this.a-=this.c?9:3+(7==this.b?2:0)},function(a){U(this,a,0,ld);this.a-=this.c?9:3+(7==this.b?2:0)},function(a){U(this,a,1,pd);this.a-=this.c?9:3+(7==this.b?2:0)},function(a){U(this,a,1,nd);this.a-=this.c?9:3+(7==this.b?2:0)}],Ce=[function(a){U(this,a,0, rd);this.a-=this.c?11:6},function(a){U(this,a,P(this)?1:0,bd);this.a-=this.c?9:3+(7==this.b?2:0)},function(a){U(this,a,P(this)?1:0,xd);this.a-=this.c?9:3+(7==this.b?2:0)},function(a){a=Zc(this,a);Kc(this,a);this.a-=this.c?4:3+(7==this.b?2:0)}],Ee=[function(a){U(this,a,0,vd);this.a-=this.c?9:3+(7==this.b?2:0)},function(a){U(this,a,0,td);this.a-=this.c?9:3+(7==this.b?2:0)},function(a){U(this,a,0,fd);this.a-=this.c?9:3+(7==this.b?2:0)},function(a){U(this,a,0,dd);this.a-=this.c?9:3+(7==this.b?2:0)}],