Merge branch 'next-release'
This commit is contained in:
commit
6b204b1747
18 changed files with 964 additions and 652 deletions
|
|
@ -10,4 +10,8 @@ PDP-11 Software
|
|||
We have archived the following selected PDP-11 software:
|
||||
|
||||
- [Boot Software](boot/)
|
||||
- [DEC PDP-11 Tapes](tapes/), including [Paper Tape Diagnostics](tapes/diags/)
|
||||
- [DEC PDP-11 Paper Tapes](tapes/), including [Paper Tape Diagnostics](tapes/diags/)
|
||||
|
||||
Additional DEC diagnostics are also available on disk, such as:
|
||||
|
||||
- [XXDP+ Diagnostics](/disks/dec/rl02k/xxdp/)
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
---
|
||||
layout: page
|
||||
title: DEC PDP-11 Tape Images
|
||||
title: DEC PDP-11 Paper Tape Images
|
||||
permalink: /apps/pdp11/tapes/
|
||||
---
|
||||
|
||||
DEC PDP-11 Tape Images
|
||||
----------------------
|
||||
DEC PDP-11 Paper Tape Images
|
||||
----------------------------
|
||||
|
||||
To load any of the DEC PDP-11 tapes listed below, you will need to use a [PDPjs Machine](/devices/pdp11/machine/)
|
||||
To load any of the DEC PDP-11 paper tapes listed below, you will need to use a [PDPjs Machine](/devices/pdp11/machine/)
|
||||
with a [PC11 High-Speed Paper Tape Reader/Punch](/devices/pdp11/pc11/), such as this
|
||||
[PDP-11/20](/devices/pdp11/machine/1120/basic/debugger), and then follow this three-step process:
|
||||
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@ into RAM using the machine's "Load" button instead of "Attach", allowing you to
|
|||
loading the [Bootstrap Loader](/apps/pdp11/boot/bootstrap/) in order to load the [Absolute Loader](/apps/pdp11/tapes/absloader/)
|
||||
in order to load the desired tape.
|
||||
|
||||
Additional (non-paper tape) diagnostics are available on the [XXDP+ Diagnostics](/disks/dec/rl02k/xxdp/) disk.
|
||||
|
||||
[](#tests-1-12)
|
||||
[](#tests-1-12)
|
||||
[](#tests-1-12)
|
||||
|
|
@ -35,13 +37,13 @@ in order to load the desired tape.
|
|||
[](#test-14)
|
||||
[](#test-15)
|
||||
|
||||
Instructions for Running Diagnostics
|
||||
------------------------------------
|
||||
Running Paper Tape Diagnostics
|
||||
------------------------------
|
||||
|
||||
### Tests 1-12
|
||||
|
||||
Instructions for running "TEST 1 - BRANCH" through "TEST 12 - JUMP" come from the
|
||||
[MAINDEC USER REFERENCE MANUAL, OCTOBER 1973](http://archive.pcjs.org/pubs/dec/pdp11/diags/MAINDEC_User_Reference_Manual_Oct73.pdf),
|
||||
[MAINDEC USER REFERENCE MANUAL, OCTOBER 1973](http://archive.pcjs.org/pubs/dec/pdp11/diags/MAINDEC-11-DZQAB-B-D_User_Reference_Manual_Oct73.pdf),
|
||||
p. 19:
|
||||
|
||||
MAINDEC-11-D0AA to D0LA (NEW NUMBER - DZKAA to DZKAL)
|
||||
|
|
@ -138,6 +140,22 @@ instruction, and the test will halt.
|
|||
Note that, as the instructions indicate, this test will fail on a PDP-11/45 or newer machine, because
|
||||
it expects the MUL instruction to trap.
|
||||
|
||||
This test is also noteworthy because it uncovered a bug involving instructions like this:
|
||||
|
||||
MOV R0,(R0)+
|
||||
|
||||
Imagine that R0 contains 1000. PDPjs (as well as several other emulators I tested) would write the value 1000
|
||||
to address 1000 after auto-incrementing R0 to 1002.
|
||||
|
||||
Unfortunately, that’s wrong. Apparently, the PDP-11 performs both source and destination address calculations
|
||||
before reading and writing the source and destination values. So, in the above example, the value 1002 must be
|
||||
written to address 1000.
|
||||
|
||||
I should add that not all emulators get this wrong: [SimH](https://github.com/simh/simh), the gold standard of
|
||||
PDP-11 emulators, handles it correctly. However, it's unclear how much confidence one should place in SimH,
|
||||
because as I discovered later when running the [11/70 CPU EXERCISER](#md-11-1170-cpu-exerciser) diagnostic,
|
||||
SimH is not infallible.
|
||||
|
||||
MAINDEC-11-D0NA (NEW NUMBER - DAKAA)
|
||||
|
||||
T14 TRAPS
|
||||
|
|
@ -179,6 +197,9 @@ it expects the MUL instruction to trap.
|
|||
Before loading and running this test on the [PDP-11/20 with Front Panel and Debugger](/devices/pdp11/machine/1120/panel/debugger/),
|
||||
it's recommended that you also toggle SW-15, as described below, so that the test will HALT on any failure.
|
||||
|
||||
Also, this test, like [Test 14](#test-14), fails on a PDP-11/45 or newer machine. In this case, it's because DEC
|
||||
changed the behavior of the RTI instruction on newer models.
|
||||
|
||||
MAINDEC-11-D0OA (NEW NUMBER - DZQKA)
|
||||
|
||||
T15 Instruction Exerciser
|
||||
|
|
@ -234,4 +255,175 @@ so for now, I'm just going to pick out selected tapes and archive them here.
|
|||
|
||||
From [bitsavers.org](http://bitsavers.trailing-edge.com/bits/DEC/pdp11/papertapeimages/20040101/), Tray 02:
|
||||
|
||||
* Tape 04: [MAINDEC-11-DEQKC-B1-PB 06/12/78; 11/70 cpu instruction; exerciser; (c)1975,76](MAINDEC-11-DEQKC-B1-PB.json)
|
||||
* Tape 04: [MAINDEC-11-DEQKC-B1-PB 06/12/78; 11/70 cpu instruction exerciser; (c)1975,76](#md-11-1170-cpu-exerciser)
|
||||
|
||||
MD-11 11/70 CPU EXERCISER
|
||||
-------------------------
|
||||
|
||||
When started in a [PDP-11/70](/devices/pdp11/machine/1170/panel/debugger/cpuexer/) with no disk drives attached,
|
||||
this diagnostic displays the following startup information:
|
||||
|
||||
MAINDEC-11-DEQKC-B...PDP 11/70 CPU EXERCISOR
|
||||
OPT.CP=145406
|
||||
OPERATIONAL SWITCH SETTINGS
|
||||
SWITCH USE
|
||||
15 HALT ON ERROR
|
||||
14 LOOP ON TEST
|
||||
13 INHIBIT ERROR TYPEOUTS
|
||||
12 INHIBIT UBE
|
||||
11 INHIBIT ITTERATIONS
|
||||
10 BELL ON ERROR
|
||||
9 LOOP ON ERROR
|
||||
8 INHIBIT RELOCATION VIA I/O DEVICE
|
||||
7 INHIBIT TYPEOUT OF THIS TEXT AND SYS SIZE
|
||||
6 INHIBIT RELOCATION
|
||||
5 INHIBIT ROUND ROBIN RELOCATION
|
||||
4 INHIBIT RANDOM DISK ADDRESS
|
||||
3 INHIBIT MBT
|
||||
2 THESE THREE SWITCHES
|
||||
1 ARE ENCODED TO SELECT RELOCATION
|
||||
0 ON THE FOLLOWING DEVICES:
|
||||
0...RP11/RP03
|
||||
1...RK11/RK05
|
||||
2...NOT USED
|
||||
3...NOT USED
|
||||
4...RH70/RP04
|
||||
5...RH70/RS04 OR RS03
|
||||
6...NOT USED
|
||||
7...NOT USED
|
||||
THE FOLLOWING DEVICES AND DRIVES WILL BE USED FOR RELOCATION:
|
||||
DEVICE DRIVES
|
||||
TYPE A CHARACTER TO CONTINUE
|
||||
|
||||
The first time I tried this test, there were a number of failures. Here's one example:
|
||||
|
||||
000:00:00
|
||||
ERRORPC PHYSC PC PSW MAINT TEST NO SUB-PASS CNT
|
||||
022024 00022024 000340 000000 000041 000000 000000
|
||||
|
||||
I disassembled the code responsible for that failure:
|
||||
|
||||
021632: 052737 000340 177776 BIS #340,@#177776
|
||||
021640: 010746 MOV PC,-(SP)
|
||||
021642: 062716 000152 ADD #152,@SP
|
||||
021646: 011637 000004 MOV @SP,@#4
|
||||
021652: 012737 000340 000006 MOV #340,@#6
|
||||
021660: 013727 000014 001472 MOV @#14,#1472
|
||||
021666: 062716 000100 ADD #100,@SP
|
||||
021672: 012637 000014 MOV (SP)+,@#14
|
||||
021676: 012737 000340 000016 MOV #340,@#16
|
||||
021704: 012703 000376 MOV #376,R3
|
||||
021710: 010313 MOV R3,@R3
|
||||
021712: 010306 MOV R3,SP
|
||||
021714: 032767 140000 000234 BIT #140000,022156
|
||||
021722: 001015 BNE 021756
|
||||
021724: 005716 TST @SP
|
||||
021726: 021666 177776 CMP @SP,177776(SP)
|
||||
021732: 012656 MOV (SP)+,@-(SP)
|
||||
021734: 057636 000000 BIS @000000(SP),@(SP)+
|
||||
021740: 054676 000000 BIS -(SP),@000000(SP)
|
||||
021744: 005006 CLR SP
|
||||
021746: 013766 020000 020000 MOV @#20000,20000(SP)
|
||||
021754: 000425 BR 022030
|
||||
021756: 156737 000175 177777 BISB 022157,@#177777
|
||||
021764: 012706 000376 MOV #376,SP
|
||||
021770: 016646 177776 MOV 177776(SP),-(SP)
|
||||
021774: 051616 BIS @SP,@SP
|
||||
021776: 061666 177776 ADD @SP,177776(SP)
|
||||
022002: 105037 177777 CLRB @#177777
|
||||
022006: 012706 000700 MOV #700,SP
|
||||
022012: 000451 BR 022136
|
||||
022014: 012600 MOV (SP)+,R0
|
||||
022016: 012602 MOV (SP)+,R2
|
||||
022020: 012706 000700 MOV #700,SP
|
||||
022024: 104000 EMT 000
|
||||
|
||||
I wasn't able to locate a source code listing for this particular (1978) version of the 11/70 CPU Instruction Exerciser,
|
||||
but I did find another useful document,
|
||||
[11/40 and 11/45 INSTRUCTION EXERCISER (Sep, 1974)](http://archive.pcjs.org/pubs/dec/pdp11/diags/MAINDEC-11-DCQKC-D-D_1140_1145_INSTRUCTION_EXERCISER_Sep74.pdf),
|
||||
where I discovered a matching test sequence on p. 86.
|
||||
|
||||
Here's an excerpt:
|
||||
|
||||
; THE BELOW INSTRUCTIONS SHOULD NOT CAUSE AN OVERFLOW TRAP
|
||||
TST (SP) ; BECAUSE TST IS A NON MODIFYING INST
|
||||
CMP (SP),-2(SP) ; SO IS COMPARE
|
||||
MOV (SP)+,@-(SP) ; BECAUSE OF ADDRESS MODE 5
|
||||
BIS @(SP),@(SP)+ ; BECAUSE OF ADDRESS MODE 3
|
||||
BIS -(SP),@(SP) ; BECAUSE OF ADDRESS MODE 7
|
||||
CLR SP
|
||||
MOV @#20000,20000(SP)
|
||||
BR 3$ ; BRANCH OVER NON KERNEL MODE TESTS
|
||||
|
||||
This test uncovered a number of stack overflow-related issues that PDPjs needed to resolve,
|
||||
including the fact that none of the "deferred" addressing modes (3, 5, or 7) apparently trigger a
|
||||
stack overflow; eg:
|
||||
|
||||
R0=000000 R1=154112 R2=155754 R3=000376 R4=160316 R5=153412
|
||||
SP=000376 PC=021734 PS=000340 IR=000000 SL=000377 T0 N0 Z0 V0 C0
|
||||
021734: 057636 000000 BIS @000000(SP),@(SP)+
|
||||
|
||||
whereas addressing mode 6 *does*; eg:
|
||||
|
||||
R0=001437 R1=154112 R2=000040 R3=000376 R4=160316 R5=153412
|
||||
SP=000376 PC=022074 PS=000340 IR=000000 SL=000377 T0 N0 Z0 V0 C0
|
||||
022074: 050666 177776 BIS SP,177776(SP)
|
||||
|
||||
I have to assume that the idiosyncrasies of stack overflow checking changed over time, because [Test 14](#test-14),
|
||||
which predates the 11/45 and 11/70, expects this instruction:
|
||||
|
||||
TST -(SP)
|
||||
|
||||
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.
|
||||
|
||||
DISCLAIMER: Until I can run this test on real PDP-11 hardware, I have to acknowledge another possible
|
||||
explanation for the above failure: that SimH is *correct*, and that unlike all other registers, the value
|
||||
of the PC register is always used immediately, rather than after both *src* and *dst* have been decoded.
|
||||
But that strikes me as a strange architectural inconsistency, and given SimH's other "11/70 CPU EXERCISER"
|
||||
test failures, I'm going to stick with my original assumption -- for now.
|
||||
|
|
|
|||
14
devices/pdp11/machine/1170/panel/debugger/cpuexer/README.md
Normal file
14
devices/pdp11/machine/1170/panel/debugger/cpuexer/README.md
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
---
|
||||
layout: page
|
||||
title: PDP-11/70 with Front Panel and Debugger
|
||||
permalink: /devices/pdp11/machine/1170/panel/debugger/cpuexer/
|
||||
machines:
|
||||
- id: test1170
|
||||
type: pdp11
|
||||
debugger: true
|
||||
autoStart: true
|
||||
---
|
||||
|
||||
This machine runs the [11/70 CPU Instruction Exerciser](/apps/pdp11/tapes/diags/#md-11-1170-cpu-exerciser): MAINDEC-11-DEQKC-B1-PB.
|
||||
|
||||
{% include machine.html id="test1170" %}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<?xml-stylesheet type="text/xsl" href="/versions/pdpjs/1.30.3/machine.xsl"?>
|
||||
<machine id="test1170" class="pdp11" border="1" pos="center" background="#FAEBD7">
|
||||
<name pos="center">PDP-11/70 with 256Kb, Front Panel and Debugger</name>
|
||||
<computer id="computer" busWidth="22"/>
|
||||
<cpu id="cpu" model="1170" cycles="6666667"/>
|
||||
<ram id="ram" addr="0x00000" size="0x40000" file="/apps/pdp11/tapes/diags/MAINDEC-11-DEQKC-B1-PB.json"/>
|
||||
<device id="default" type="default"/>
|
||||
<serial id="dl11" adapter="0" binding="print" upperCase="true"/>
|
||||
<panel ref="/devices/pdp11/panel/1170/debugger/front.xml"/>
|
||||
<debugger id="debugger" base="8" messages="" commands=""/>
|
||||
<device ref="/devices/pdp11/pc11/default.xml"/>
|
||||
</machine>
|
||||
|
|
@ -12,6 +12,9 @@ machines:
|
|||
path: http://archive.pcjs.org/disks/dec/rl02k/RL02K-XXDP.json
|
||||
---
|
||||
|
||||
This machine is ready to boot the [XXDP+ Diagnostics](/disks/dec/rl02k/xxdp/) ("BOOT RL0") and run the
|
||||
[EKBEE1](/disks/dec/rl02k/xxdp/ekbee1/) diagnostic ("R EKBEE1").
|
||||
|
||||
{% include machine.html id="test1170" %}
|
||||
|
||||
Other interesting things to know about this machine:
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
<device id="default" type="default"/>
|
||||
<serial id="dl11" adapter="0" binding="print" upperCase="true"/>
|
||||
<panel ref="/devices/pdp11/panel/1170/debugger/front.xml"/>
|
||||
<debugger id="debugger" base="8" messages="" commands=""/>
|
||||
<debugger id="debugger" base="8" messages="" commands="bp 076060"/>
|
||||
<device ref="/devices/pdp11/pc11/default.xml"/>
|
||||
<device ref="/devices/pdp11/rl11/default.xml"/>
|
||||
</machine>
|
||||
|
|
|
|||
|
|
@ -1,14 +1,23 @@
|
|||
---
|
||||
layout: page
|
||||
title: "DEC RL02K Disk Images: XXDP+"
|
||||
title: "XXDP+ Diagnostics Disk"
|
||||
permalink: /disks/dec/rl02k/xxdp/
|
||||
---
|
||||
|
||||
DEC RL02K Disk Images: XXDP+
|
||||
----------------------------
|
||||
XXDP+ Diagnostics Disk
|
||||
----------------------
|
||||
|
||||
To boot the "XXDP+ Diagnostics" disk, start a [PDP-11/70](devices/pdp11/machine/1170/panel/debugger/) and select
|
||||
the "XXDP+ Diagnostics" disk from the "Disk Drive Controls", click "Load", and wait for the message:
|
||||
The XXDP+ Diagnostics disk described below contains a wealth of PDP-11 tests. Tests that PDPjs has exercised
|
||||
include:
|
||||
|
||||
- [EKBEE1: 11/70 MEMORY MANAGEMENT DIAGNOSTIC](ekbee1/) ([Demo](/devices/pdp11/machine/1170/panel/debugger/xxdp/))
|
||||
|
||||
Running XXDP+ Diagnostics
|
||||
-------------------------
|
||||
|
||||
To boot the "XXDP+ Diagnostics" disk, start a [PDP-11/70](devices/pdp11/machine/1170/panel/debugger/) with an
|
||||
[RL11 Disk Controller](/devices/pdp11/rl11/), select the "XXDP+ Diagnostics" disk from the "Disk Drive Controls",
|
||||
click "Load", and wait for the message:
|
||||
|
||||
Mounted disk "XXDP+ Diagnostics" in drive RL0
|
||||
|
||||
|
|
@ -1180,4 +1189,3 @@ The "D" (DIRECTORY) command displays the following information:
|
|||
948 DXDPU .CMD 25-MAR-82 1 037331
|
||||
949 SXDP26.BAK 25-MAR-82 1 037332
|
||||
950 SXDP27.BAK 25-MAR-82 1 037333
|
||||
|
||||
|
|
|
|||
73
disks/dec/rl02k/xxdp/ekbee1/README.md
Normal file
73
disks/dec/rl02k/xxdp/ekbee1/README.md
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
---
|
||||
layout: page
|
||||
title: "EKBEE1: 11/70 MEMORY MANAGEMENT DIAGNOSTIC"
|
||||
permalink: /disks/dec/rl02k/xxdp/ekbee1/
|
||||
---
|
||||
|
||||
EKBEE1: 11/70 MEMORY MANAGEMENT DIAGNOSTIC
|
||||
------------------------------------------
|
||||
|
||||
From the
|
||||
[PDP-11 Diagnostic Handbook (1988)](http://archive.pcjs.org/pubs/dec/pdp11/diags/PDP11_DiagnosticHandbook_1988.pdf),
|
||||
p. 1-10:
|
||||
|
||||
11/70 MEMORY MANAGEMENT TEST
|
||||
|
||||
ABSTRACT:
|
||||
|
||||
This program was designed using a "BOTTOM UP" approach starting with the smallest segment of MEMORY
|
||||
MANAGEMENT logic and building up to cover all the logic. The program begins by testing some of the
|
||||
internal CPU data and address path and address detection logic, then works outward through the MEM.
|
||||
MANAGEMENT registers. It is assumed that both the CPU and the CACHE have been tested, or are known
|
||||
to be good.
|
||||
|
||||
OPERATING PROCEDURES:
|
||||
Set the switch register by <CONTROL P>
|
||||
CON = xxxxxxWZ
|
||||
(W = deposit xxxxxx into console switch register)
|
||||
(R = read and type console switch settings / CON = R)
|
||||
(Z = switch console terminal back to program)
|
||||
|
||||
.R EKBEEI
|
||||
|
||||
SWITCH SETTINGS
|
||||
SW15 = 1 halt on error
|
||||
SW14 = 1 loop on current test
|
||||
SW13 = 1 inhibit all error typeouts
|
||||
SW12 = 1 inhibit trace trapping
|
||||
SW11 = 1 inhibit iterations
|
||||
SW10 = 1 ring bell on error
|
||||
SW09 = 1 loop on error
|
||||
SW08 = 1 loop on test in SWR < 06:00>
|
||||
SW07 = 1 inhibit multiple error typeouts
|
||||
SW06 = 1 selects subtest
|
||||
...
|
||||
|
||||
The closest we have to a source code listing of the EKBEE1 diagnostic comes from some
|
||||
[scanned microfiche](http://archive.pcjs.org/pubs/dec/pdp11/diags/AC-7975E-MC_CEKBEE0_1170_MEM_MGMT_May80.pdf)
|
||||
on [bitsavers.org](http://bitsavers.trailing-edge.com/pdf/dec/pdp11/microfiche/ftp.j-hoppe.de/bw/gh/) for:
|
||||
|
||||
PRODUCT CODE: AC-7975E-MC
|
||||
PRODUCT NAME: CEKBEE0 11/70 MEM MGMT
|
||||
DATE CREATED: MAY, 1980
|
||||
MAINTAINER: DIAGNOSTIC ENGINEERING
|
||||
|
||||
One of the last tests within the diagnostic to pass in PDPjs was "TEST 122: KT BEND", which appears on p. 208 of
|
||||
the PDF (p. 199 of the source code listing):
|
||||
|
||||
;;***************************************************************
|
||||
;*TEST 122 KT BEND
|
||||
;*
|
||||
;* THIS TEST ENSURES THAT TMCE KT BEND GOES LOW ON AN ODD
|
||||
;* ADDRESS ERROR, SL RED, AND NEXM. THIS IS DONE BY EXECUTING
|
||||
;* AN INSTRUCTION FOR EACH Of THESE THESE CASES THAT ALSO
|
||||
;* CAUSES A KT ABORT. THE ABORT SHOULD NOT BE HONORED.
|
||||
;*
|
||||
;* NOTE: ON A KB11-E OR KB11-EM THE KT ABORT SHOULD BE HONORED OVER A
|
||||
;* NEXM TRAP. IF THIS IS A KB11-E/EM THEN THIS FEATURE IS TESTED.
|
||||
;****************************************************************
|
||||
|
||||
As noted in the *mapVirtualToPhysical()* function in [cpustate.js](/modules/pdp11/lib/cpustate.js), this test
|
||||
begins at PC 076060.
|
||||
|
||||
This diagnostic should run in any [PDP-11/70](/devices/pdp11/machine/1170/) ([Demo](/devices/pdp11/machine/1170/panel/debugger/xxdp/)).
|
||||
|
|
@ -211,7 +211,7 @@ BusPDP11.IOHANDLER = {
|
|||
*
|
||||
* Those fallbacks may not always be appropriate; for example, byte writes to some device registers
|
||||
* must be zero-extended to update the entire word. For those cases, the fallback's "preliminary" read
|
||||
* is issued with a zero address so that the handler can distinguish a normal read from one of these
|
||||
* is issued with a fPreWrite flag so that the handler can distinguish a normal read from one of these
|
||||
* preliminary reads (aka read-before-write), and return an appropriate value for the update (eg, zero).
|
||||
*
|
||||
* If none of these fallback behaviors are appropriate, the device has a simple recourse: register
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -125,8 +125,10 @@ CPUStatePDP11.prototype.initProcessor = function()
|
|||
{
|
||||
if (this.model == PDP11.MODEL_1120) {
|
||||
this.decode = PDP11.op1120.bind(this);
|
||||
this.checkStackLimit = this.checkStackLimit1120;
|
||||
} else {
|
||||
this.decode = PDP11.op1145.bind(this);
|
||||
this.checkStackLimit = this.checkStackLimit1145;
|
||||
}
|
||||
|
||||
this.initRegs();
|
||||
|
|
@ -247,7 +249,7 @@ CPUStatePDP11.prototype.resetMMU = function()
|
|||
this.regMMR1 = 0; // 177574
|
||||
this.regMMR2 = 0; // 177576
|
||||
this.regMMR3 = 0; // 172516
|
||||
this.regErr = 0; // 177766
|
||||
this.regErr = 0; // 177766 TODO: Do we ever need to clear this, because it appears to accumulate errors...
|
||||
this.regPIR = 0; // 177772
|
||||
this.regSL = 0xff; // 177774
|
||||
this.mmuEnable = 0; // MMU enabled for PDP11.ACCESS.READ or PDP11.ACCESS.WRITE
|
||||
|
|
@ -1304,9 +1306,11 @@ CPUStatePDP11.prototype.trap = function(vector, flag, reason)
|
|||
if (this.trapPSW < 0) {
|
||||
this.trapPSW = this.getPSW();
|
||||
} else if (!this.mmuMode) {
|
||||
/*
|
||||
* Double-fault (nested trap) condition detected.
|
||||
*/
|
||||
reason = PDP11.REASON.RED; // double-fault (nested trap) forces a RED condition
|
||||
}
|
||||
|
||||
var fRed = false;
|
||||
if (reason == PDP11.REASON.RED) {
|
||||
vector = 4;
|
||||
/*
|
||||
* The next two lines used to be deferred until after the setPSW() below, but
|
||||
|
|
@ -1314,7 +1318,7 @@ CPUStatePDP11.prototype.trap = function(vector, flag, reason)
|
|||
*/
|
||||
this.regErr |= PDP11.CPUERR.RED;
|
||||
this.regsGen[6] = 4;
|
||||
reason = PDP11.REASON.RED;
|
||||
fRed = true;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -1343,14 +1347,15 @@ CPUStatePDP11.prototype.trap = function(vector, flag, reason)
|
|||
*/
|
||||
this.setPSW((newPSW & ~PDP11.PSW.PMODE) | ((this.trapPSW >> 2) & PDP11.PSW.PMODE));
|
||||
|
||||
this.pushWord(this.trapPSW);
|
||||
this.pushWord(this.regsGen[7]);
|
||||
this.pushWord(this.trapPSW, fRed);
|
||||
this.pushWord(this.regsGen[7], fRed);
|
||||
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
|
||||
*
|
||||
|
|
@ -1488,12 +1493,12 @@ CPUStatePDP11.prototype.mapUnibus = function(addr)
|
|||
};
|
||||
|
||||
/**
|
||||
* mapVirtualToPhysical(virtualAddress, accessFlags)
|
||||
* mapVirtualToPhysical(virtualAddress, access)
|
||||
*
|
||||
* mapVirtualToPhysical() does memory management. It converts a 17-bit I/D virtual address to a
|
||||
* 22-bit physical address. A real PDP 11/70 memory management unit can be enabled separately
|
||||
* for read and write for diagnostic purposes. This is handled here by having an enable mask
|
||||
* (mmuEnable) which is tested against the operation access mask (accessFlags). If there is no
|
||||
* (mmuEnable) which is tested against the operation access mask (access). If there is no
|
||||
* match, then the virtual address is simply mapped as a 16 bit physical address with the upper
|
||||
* page going to the IO address space. Significant access mask values used are PDP11.ACCESS.READ
|
||||
* and PDP11.ACCESS.WRITE.
|
||||
|
|
@ -1539,19 +1544,19 @@ CPUStatePDP11.prototype.mapUnibus = function(addr)
|
|||
*
|
||||
* @this {CPUStatePDP11}
|
||||
* @param {number} virtualAddress
|
||||
* @param {number} accessFlags
|
||||
* @param {number} access
|
||||
* @return {number}
|
||||
*/
|
||||
CPUStatePDP11.prototype.mapVirtualToPhysical = function(virtualAddress, accessFlags)
|
||||
CPUStatePDP11.prototype.mapVirtualToPhysical = function(virtualAddress, access)
|
||||
{
|
||||
var page, pdr, physicalAddress;
|
||||
|
||||
this.assert(!(virtualAddress & ~0x1ffff) && accessFlags);
|
||||
this.assert(!(virtualAddress & ~0x1ffff) && access);
|
||||
|
||||
/*
|
||||
* This can happen when the DSTMODE (MAINT) bit of MMR0 is set but *not* the ENABLED bit.
|
||||
*/
|
||||
if (!(accessFlags & this.mmuEnable)) {
|
||||
if (!(access & this.mmuEnable)) {
|
||||
physicalAddress = virtualAddress & 0xffff;
|
||||
if (physicalAddress >= BusPDP11.IOPAGE_16BIT) {
|
||||
physicalAddress |= this.addrIOPage;
|
||||
|
|
@ -1572,13 +1577,24 @@ CPUStatePDP11.prototype.mapVirtualToPhysical = function(virtualAddress, accessFl
|
|||
* As an aside it turns out that it is the memory management unit that does odd address and
|
||||
* non-existent memory trapping: who knew? :-) I thought these would have been handled at access time.
|
||||
*
|
||||
* I haven't imported the non-existent memory checks he performed (yet); I would like to see the problems
|
||||
* in action first.
|
||||
* TEST #122 ("KT BEND") in the "EKBEE1" diagnostic (PC 076060) triggers an ODDADDR error using this
|
||||
* instruction:
|
||||
*
|
||||
* As for the ODDADDR error that's supposed to generate a BUS error rather than an MMU error, this happens
|
||||
* in TEST #122 ("KT BEND") in the "EKBEE1" diagnostic (PC 076456).
|
||||
* 076356: 005037 140001 CLR @#140001
|
||||
*
|
||||
* and it expects that instruction to generate a BUS error rather than an MMU error, so we deal with that
|
||||
* next. However, the test also claims to check for an NEXM (non-existent memory) error, using this
|
||||
* instruction:
|
||||
*
|
||||
* 076170: 005037 140100 CLR @#140100
|
||||
*
|
||||
* but that error never materializes, so I've NOT included any NEXM checks yet. I assume that any such
|
||||
* checks would be limited to whatever's recorded in the Memory Size registers (177760), because actually
|
||||
* checking every physical address at this stage -- even in a real MMU -- seems prohibitively expensive.
|
||||
*
|
||||
* TODO: Investigate why the test's NEXM error doesn't occur.
|
||||
*/
|
||||
if ((physicalAddress & 0x1) && !(accessFlags & PDP11.ACCESS.BYTE)) {
|
||||
if ((physicalAddress & 0x1) && !(access & PDP11.ACCESS.BYTE)) {
|
||||
this.regErr |= PDP11.CPUERR.ODDADDR;
|
||||
this.trap(PDP11.TRAP.BUS, 0, physicalAddress);
|
||||
}
|
||||
|
|
@ -1592,7 +1608,7 @@ CPUStatePDP11.prototype.mapVirtualToPhysical = function(virtualAddress, accessFl
|
|||
|
||||
case PDP11.PDR.ACF.RO: // 0x2: read-only, abort on write attempt
|
||||
pdr |= PDP11.PDR.ACCESSED;
|
||||
if (accessFlags & PDP11.ACCESS.WRITE) {
|
||||
if (access & PDP11.ACCESS.WRITE) {
|
||||
newMMR0 = PDP11.MMR0.ABORT_RO;
|
||||
}
|
||||
break;
|
||||
|
|
@ -1602,13 +1618,13 @@ CPUStatePDP11.prototype.mapVirtualToPhysical = function(virtualAddress, accessFl
|
|||
/* falls through */
|
||||
|
||||
case PDP11.PDR.ACF.RW2: // 0x5: read/write, memory management trap upon completion of a write (11/70 only)
|
||||
if (accessFlags & PDP11.ACCESS.WRITE) {
|
||||
if (access & PDP11.ACCESS.WRITE) {
|
||||
newMMR0 = PDP11.MMR0.TRAP_MMU;
|
||||
}
|
||||
/* falls through */
|
||||
|
||||
case PDP11.PDR.ACF.RW: // 0x6: read/write, no system trap/abort action
|
||||
pdr |= ((accessFlags & PDP11.ACCESS.WRITE) ? (PDP11.PDR.ACCESSED | PDP11.PDR.MODIFIED) : PDP11.PDR.ACCESSED);
|
||||
pdr |= ((access & PDP11.ACCESS.WRITE) ? (PDP11.PDR.ACCESSED | PDP11.PDR.MODIFIED) : PDP11.PDR.ACCESSED);
|
||||
break;
|
||||
|
||||
default: // 0x0 (non-resident, abort all accesses) or 0x3 or 0x7 (unused, abort all accesses)
|
||||
|
|
@ -1717,61 +1733,23 @@ CPUStatePDP11.prototype.popWord = function()
|
|||
};
|
||||
|
||||
/**
|
||||
* pushWord(data)
|
||||
* pushWord(data, fRed)
|
||||
*
|
||||
* @this {CPUStatePDP11}
|
||||
* @param {number} data
|
||||
* @param {boolean} [fRed]
|
||||
*/
|
||||
CPUStatePDP11.prototype.pushWord = function(data)
|
||||
CPUStatePDP11.prototype.pushWord = function(data, fRed)
|
||||
{
|
||||
var virtualAddress = (this.regsGen[6] - 2) & 0xffff;
|
||||
this.regsGen[6] = virtualAddress; // BSD needs SP updated before any fault :-(
|
||||
this.lastOp = (this.lastOp & 0xffff) | ((this.lastOp & ~0xffff) << 8) | (0x00f6 << 16);
|
||||
this.checkStackLimit(0, virtualAddress);
|
||||
if (!fRed) this.checkStackLimit(PDP11.ACCESS.WRITE_WORD, -2, virtualAddress);
|
||||
this.writeWord(virtualAddress, data);
|
||||
};
|
||||
|
||||
/**
|
||||
* checkStackLimit(mode, addr)
|
||||
*
|
||||
* The special "mode 0" case used by pushWord() ignores addr <= 4, because pushWord() is used by trap(),
|
||||
* and if the trap() was generated by a RED error (below) or generated a RED error itself, then we need to
|
||||
* avoid triggering another (nested) RED error.
|
||||
*
|
||||
* TODO: pushWord() is not used exclusively by trap(); it's also used by a few instructions, like opJSR(),
|
||||
* so we might need to do some additional factoring of this function's logic.
|
||||
*
|
||||
* @this {CPUStatePDP11}
|
||||
* @param {number} mode (ie, the addressing mode, or 0 if pushWord() is checking)
|
||||
* @param {number} addr
|
||||
*/
|
||||
CPUStatePDP11.prototype.checkStackLimit = function(mode, addr)
|
||||
{
|
||||
if (!this.mmuMode) {
|
||||
/*
|
||||
* NOTE: I've removed the tests below for addr >= 0xFFFE, which were ported from the original code,
|
||||
* because while it's definitely a bad physical stack address, I'm not sure it rises to the level of
|
||||
* a trap, and this code is already expensive enough as it is.
|
||||
*/
|
||||
if (addr <= this.regSL && (mode || addr > 4) /* || mode && addr >= 0xFFFE */) {
|
||||
if (this.model >= PDP11.MODEL_1145 && (addr <= this.regSL - 32 /* || mode == 1 && addr >= 0xFFFE */)) {
|
||||
this.regErr |= PDP11.CPUERR.RED;
|
||||
this.regsGen[6] = 4;
|
||||
this.trap(PDP11.TRAP.BUS, 0, PDP11.REASON.RED);
|
||||
} else {
|
||||
/*
|
||||
* On older machines (eg, the PDP-11/20), the instruction is always allowed to complete,
|
||||
* so the trap must always be issued in this fashion.
|
||||
*/
|
||||
this.regErr |= PDP11.CPUERR.YELLOW;
|
||||
this.opFlags |= PDP11.OPFLAG.TRAP_SP;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* getAddrByMode(mode, reg, accessFlags)
|
||||
* getAddrByMode(mode, reg, access)
|
||||
*
|
||||
* getAddrByMode() maps a six bit operand to a 17 bit I/D virtual address space.
|
||||
*
|
||||
|
|
@ -1798,7 +1776,14 @@ CPUStatePDP11.prototype.checkStackLimit = function(mode, addr)
|
|||
* --------------------
|
||||
* On the PDP-11/20, stack overflow traps occur when an address below 400 is referenced
|
||||
* by SP in either mode 4 (auto-decrement) or 5 (auto-decrement deferred). The instruction
|
||||
* is allowed to complete before the trap is issued.
|
||||
* is allowed to complete before the trap is issued. NOTE: This information comes
|
||||
* directly from the PDP-11/20 Handbook (1971), but the 11/20 diagnostics apparently only
|
||||
* test mode 4, not mode 5, because when I later removed stack limit checks for mode 5 on
|
||||
* the 11/70, none of the 11/20 tests complained.
|
||||
*
|
||||
* TODO: Find some independent confirmation as to whether ANY PDP-11 models check for
|
||||
* stack overflow on mode 5 (auto-decrement deferred); if they do, then further tweaks to
|
||||
* checkStackLimit functions may be required.
|
||||
*
|
||||
* On the PDP-11/70, the stack limit register (177774) allows a variable boundary for the
|
||||
* kernel stack.
|
||||
|
|
@ -1806,13 +1791,13 @@ CPUStatePDP11.prototype.checkStackLimit = function(mode, addr)
|
|||
* @this {CPUStatePDP11}
|
||||
* @param {number} mode
|
||||
* @param {number} reg
|
||||
* @param {number} accessFlags
|
||||
* @param {number} access
|
||||
* @return {number}
|
||||
*/
|
||||
CPUStatePDP11.prototype.getAddrByMode = function(mode, reg, accessFlags)
|
||||
CPUStatePDP11.prototype.getAddrByMode = function(mode, reg, access)
|
||||
{
|
||||
var virtualAddress, step;
|
||||
var addrDSpace = (accessFlags & PDP11.ACCESS.VIRT)? 0 : this.addrDSpace;
|
||||
var addrDSpace = (access & PDP11.ACCESS.VIRT)? 0 : this.addrDSpace;
|
||||
|
||||
/*
|
||||
* Modes that need to auto-increment or auto-decrement will break, in order to perform
|
||||
|
|
@ -1835,9 +1820,7 @@ CPUStatePDP11.prototype.getAddrByMode = function(mode, reg, accessFlags)
|
|||
* Mode 1: (R)
|
||||
*/
|
||||
case 1:
|
||||
if (reg == 6 && (accessFlags & PDP11.ACCESS.WRITE)) {
|
||||
this.checkStackLimit(mode, this.regsGen[6]);
|
||||
}
|
||||
if (reg == 6) this.checkStackLimit(access, 0, this.regsGen[6]);
|
||||
this.nStepCycles -= (2 + 1);
|
||||
return (reg == 7? this.regsGen[reg] : (this.regsGen[reg] | addrDSpace));
|
||||
|
||||
|
|
@ -1847,11 +1830,10 @@ CPUStatePDP11.prototype.getAddrByMode = function(mode, reg, accessFlags)
|
|||
case 2:
|
||||
step = 2;
|
||||
virtualAddress = this.regsGen[reg];
|
||||
if (reg == 6) this.checkStackLimit(access, step, virtualAddress);
|
||||
if (reg != 7) {
|
||||
virtualAddress |= addrDSpace;
|
||||
if (reg < 6 && (accessFlags & PDP11.ACCESS.BYTE)) {
|
||||
step = 1;
|
||||
}
|
||||
if (reg < 6 && (access & PDP11.ACCESS.BYTE)) step = 1;
|
||||
}
|
||||
this.nStepCycles -= (2 + 1);
|
||||
break;
|
||||
|
|
@ -1873,8 +1855,9 @@ CPUStatePDP11.prototype.getAddrByMode = function(mode, reg, accessFlags)
|
|||
*/
|
||||
case 4:
|
||||
step = -2;
|
||||
if (reg < 6 && (accessFlags & PDP11.ACCESS.BYTE)) step = -1;
|
||||
if (reg < 6 && (access & PDP11.ACCESS.BYTE)) step = -1;
|
||||
virtualAddress = (this.regsGen[reg] + step) & 0xffff;
|
||||
if (reg == 6) this.checkStackLimit(access, step, virtualAddress);
|
||||
if (reg != 7) virtualAddress |= addrDSpace;
|
||||
this.nStepCycles -= (3 + 1);
|
||||
break;
|
||||
|
|
@ -1895,9 +1878,10 @@ CPUStatePDP11.prototype.getAddrByMode = function(mode, reg, accessFlags)
|
|||
*/
|
||||
case 6:
|
||||
virtualAddress = this.readWord(this.advancePC(2));
|
||||
virtualAddress = ((virtualAddress + this.regsGen[reg]) & 0xffff) | addrDSpace;
|
||||
virtualAddress = (virtualAddress + this.regsGen[reg]) & 0xffff;
|
||||
if (reg == 6) this.checkStackLimit(access, 0, virtualAddress);
|
||||
this.nStepCycles -= (4 + 2);
|
||||
return virtualAddress;
|
||||
return virtualAddress | addrDSpace;
|
||||
|
||||
/*
|
||||
* Mode 7: @d(R)
|
||||
|
|
@ -1905,24 +1889,73 @@ CPUStatePDP11.prototype.getAddrByMode = function(mode, reg, accessFlags)
|
|||
case 7:
|
||||
virtualAddress = this.readWord(this.advancePC(2));
|
||||
virtualAddress = (virtualAddress + this.regsGen[reg]) & 0xffff;
|
||||
virtualAddress = this.readWord(virtualAddress | this.addrDSpace) | addrDSpace;
|
||||
virtualAddress = this.readWord(virtualAddress | this.addrDSpace);
|
||||
this.nStepCycles -= (7 + 3);
|
||||
return virtualAddress;
|
||||
return virtualAddress | addrDSpace;
|
||||
}
|
||||
|
||||
this.regsGen[reg] = (this.regsGen[reg] + step) & 0xffff;
|
||||
this.lastOp = (this.lastOp & 0xffff) | ((this.lastOp & ~0xffff) << 8) | ((((step << 3) & 0xf8) | reg) << 16);
|
||||
|
||||
/*
|
||||
* NOTE: We had to eliminate the test for "(accessFlags & PDP11.ACCESS.WRITE)", because DEC's
|
||||
* "TRAP TEST" expects "TST -(SP)" to trap when SP is 150.
|
||||
*/
|
||||
if (reg == 6 && step < 0) {
|
||||
this.checkStackLimit(mode, this.regsGen[6]);
|
||||
}
|
||||
return virtualAddress;
|
||||
};
|
||||
|
||||
/**
|
||||
* checkStackLimit1120(access, step, addr)
|
||||
*
|
||||
* @this {CPUStatePDP11}
|
||||
* @param {number} access
|
||||
* @param {number} step
|
||||
* @param {number} addr
|
||||
*/
|
||||
CPUStatePDP11.prototype.checkStackLimit1120 = function(access, step, addr)
|
||||
{
|
||||
/*
|
||||
* NOTE: DEC's "TRAP TEST" (MAINDEC-11-D0NA-PB) expects "TST -(SP)" to trap when SP is 150,
|
||||
* so we ignore the access parameter. Also, strangely, it does NOT expect this instruction
|
||||
* to trap:
|
||||
*
|
||||
* R0=006302 R1=000000 R2=000000 R3=000000 R4=000000 R5=000776
|
||||
* SP=000000 PC=006346 PS=000344 IR=000000 SL=000377 T0 N0 Z1 V0 C0
|
||||
* 006346: 112667 171426 MOVB (SP)+,000000
|
||||
*
|
||||
* so if the step parameter is positive, we let it go.
|
||||
*/
|
||||
if (!this.mmuMode && step <= 0 && addr <= this.regSL) {
|
||||
/*
|
||||
* On older machines (eg, the PDP-11/20), there is no "YELLOW" and "RED" distinction, and the
|
||||
* instruction is always allowed to complete, so the trap must always be issued in this fashion.
|
||||
*/
|
||||
this.opFlags |= PDP11.OPFLAG.TRAP_SP;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* checkStackLimit1145(access, step, addr)
|
||||
*
|
||||
* @this {CPUStatePDP11}
|
||||
* @param {number} access
|
||||
* @param {number} step
|
||||
* @param {number} addr
|
||||
*/
|
||||
CPUStatePDP11.prototype.checkStackLimit1145 = function(access, step, addr)
|
||||
{
|
||||
if (!this.mmuMode) {
|
||||
/*
|
||||
* NOTE: The 11/70 CPU Instruction Exerciser does NOT expect reads to trigger a stack overflow,
|
||||
* so we check the access parameter.
|
||||
*/
|
||||
if ((access & PDP11.ACCESS.WRITE) && addr <= this.regSL) {
|
||||
if (addr <= this.regSL - 32) {
|
||||
this.trap(PDP11.TRAP.BUS, 0, PDP11.REASON.RED);
|
||||
} else {
|
||||
this.regErr |= PDP11.CPUERR.YELLOW;
|
||||
this.opFlags |= PDP11.OPFLAG.TRAP_SP;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* getByteDirect(addr)
|
||||
*
|
||||
|
|
@ -2008,35 +2041,35 @@ CPUStatePDP11.prototype.setWordDirect = function(addr, data)
|
|||
};
|
||||
|
||||
/**
|
||||
* getPhysicalAddrByMode(mode, reg, accessFlags)
|
||||
* getPhysicalAddrByMode(mode, reg, access)
|
||||
*
|
||||
* This is a handler set up by setMemoryAccess(). All calls should go through getAddr().
|
||||
*
|
||||
* @this {CPUStatePDP11}
|
||||
* @param {number} mode
|
||||
* @param {number} reg
|
||||
* @param {number} accessFlags
|
||||
* @param {number} access
|
||||
* @return {number}
|
||||
*/
|
||||
CPUStatePDP11.prototype.getPhysicalAddrByMode = function(mode, reg, accessFlags)
|
||||
CPUStatePDP11.prototype.getPhysicalAddrByMode = function(mode, reg, access)
|
||||
{
|
||||
return this.getAddrByMode(mode, reg, accessFlags);
|
||||
return this.getAddrByMode(mode, reg, access);
|
||||
};
|
||||
|
||||
/**
|
||||
* getVirtualAddrByMode(mode, reg, accessFlags)
|
||||
* getVirtualAddrByMode(mode, reg, access)
|
||||
*
|
||||
* This is a handler set up by setMemoryAccess(). All calls should go through getAddr().
|
||||
*
|
||||
* @this {CPUStatePDP11}
|
||||
* @param {number} mode
|
||||
* @param {number} reg
|
||||
* @param {number} accessFlags
|
||||
* @param {number} access
|
||||
* @return {number}
|
||||
*/
|
||||
CPUStatePDP11.prototype.getVirtualAddrByMode = function(mode, reg, accessFlags)
|
||||
CPUStatePDP11.prototype.getVirtualAddrByMode = function(mode, reg, access)
|
||||
{
|
||||
return this.mapVirtualToPhysical(this.getAddrByMode(mode, reg, accessFlags), accessFlags);
|
||||
return this.mapVirtualToPhysical(this.getAddrByMode(mode, reg, access), access);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -2096,14 +2129,14 @@ CPUStatePDP11.prototype.writeWordToVirtual = function(virtualAddress, data)
|
|||
};
|
||||
|
||||
/**
|
||||
* readWordFromPrevSpace(opCode, accessFlags)
|
||||
* readWordFromPrevSpace(opCode, access)
|
||||
*
|
||||
* @this {CPUStatePDP11}
|
||||
* @param {number} opCode
|
||||
* @param {number} accessFlags (really just PDP11.ACCESS.DSPACE or PDP11.ACCESS.ISPACE)
|
||||
* @param {number} access (really just PDP11.ACCESS.DSPACE or PDP11.ACCESS.ISPACE)
|
||||
* @return {number}
|
||||
*/
|
||||
CPUStatePDP11.prototype.readWordFromPrevSpace = function(opCode, accessFlags)
|
||||
CPUStatePDP11.prototype.readWordFromPrevSpace = function(opCode, access)
|
||||
{
|
||||
var data;
|
||||
var reg = this.dstReg = opCode & PDP11.OPREG.MASK;
|
||||
|
|
@ -2116,25 +2149,25 @@ CPUStatePDP11.prototype.readWordFromPrevSpace = function(opCode, accessFlags)
|
|||
}
|
||||
} else {
|
||||
var addr = this.getAddrByMode(mode, reg, PDP11.ACCESS.READ_WORD);
|
||||
if (!(accessFlags & PDP11.ACCESS.DSPACE)) {
|
||||
if (!(access & PDP11.ACCESS.DSPACE)) {
|
||||
if ((this.regPSW & 0xf000) !== 0xf000) addr &= 0xffff;
|
||||
}
|
||||
this.mmuMode = (this.regPSW >> 12) & 3;
|
||||
data = this.readWord(addr | (accessFlags & this.addrDSpace));
|
||||
data = this.readWord(addr | (access & this.addrDSpace));
|
||||
this.mmuMode = (this.regPSW >> 14) & 3;
|
||||
}
|
||||
return data;
|
||||
};
|
||||
|
||||
/**
|
||||
* writeWordToPrevSpace(opCode, accessFlags, data)
|
||||
* writeWordToPrevSpace(opCode, access, data)
|
||||
*
|
||||
* @this {CPUStatePDP11}
|
||||
* @param {number} opCode
|
||||
* @param {number} accessFlags (really just PDP11.ACCESS.DSPACE or PDP11.ACCESS.ISPACE)
|
||||
* @param {number} access (really just PDP11.ACCESS.DSPACE or PDP11.ACCESS.ISPACE)
|
||||
* @param {number} data
|
||||
*/
|
||||
CPUStatePDP11.prototype.writeWordToPrevSpace = function(opCode, accessFlags, data)
|
||||
CPUStatePDP11.prototype.writeWordToPrevSpace = function(opCode, access, data)
|
||||
{
|
||||
this.lastOp = (this.lastOp & 0xffff) | (0x0016 << 16);
|
||||
var reg = this.dstReg = opCode & PDP11.OPREG.MASK;
|
||||
|
|
@ -2147,7 +2180,7 @@ CPUStatePDP11.prototype.writeWordToPrevSpace = function(opCode, accessFlags, dat
|
|||
}
|
||||
} else {
|
||||
var addr = this.getAddrByMode(mode, reg, PDP11.ACCESS.WRITE_WORD);
|
||||
if (!(accessFlags & PDP11.ACCESS.DSPACE)) addr &= 0xffff;
|
||||
if (!(access & PDP11.ACCESS.DSPACE)) addr &= 0xffff;
|
||||
/*
|
||||
* TODO: Consider replacing the following code with writeWord(), by adding optional mmuMode
|
||||
* parameters for each of the discrete mapVirtualToPhysical() and bus.setWord() operations, because
|
||||
|
|
@ -2155,7 +2188,7 @@ CPUStatePDP11.prototype.writeWordToPrevSpace = function(opCode, accessFlags, dat
|
|||
* setMemoryAccess() handlers.
|
||||
*/
|
||||
this.mmuMode = (this.regPSW >> 12) & 3;
|
||||
addr = this.mapVirtualToPhysical(addr | (accessFlags & PDP11.ACCESS.DSPACE), PDP11.ACCESS.WRITE);
|
||||
addr = this.mapVirtualToPhysical(addr | (access & PDP11.ACCESS.DSPACE), PDP11.ACCESS.WRITE);
|
||||
this.mmuMode = (this.regPSW >> 14) & 3;
|
||||
this.bus.setWord(addr, data);
|
||||
}
|
||||
|
|
@ -2195,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)+
|
||||
|
|
|
|||
|
|
@ -69,41 +69,6 @@ function DevicePDP11(parmsDevice)
|
|||
|
||||
Component.subclass(DevicePDP11);
|
||||
|
||||
DevicePDP11.M9312 = [
|
||||
0x101F, /*0010037*/ 0x01C0, /*0000700*/ 0x105F, /*0010137*/ 0x01C2, /*0000702*/ 0x111F, /*0010437*/ 0x01C4, /*0000704*/ 0x0A1F, /*0005037*/ 0x01C6, /*0000706*/
|
||||
0x55DF, /*0052737*/ 0x8000, /*0100000*/ 0xFFFE, /*0177776*/ 0x35DF, /*0032737*/ 0x4000, /*0040000*/ 0xFFFE, /*0177776*/ 0x0302, /*0001402*/ 0x0A9F, /*0005237*/
|
||||
0x01C6, /*0000706*/ 0x0A1F, /*0005037*/ 0xFFFE, /*0177776*/ 0x0101, /*0000401*/ 0x0000, /*0000000*/ 0x0A06, /*0005006*/ 0x8104, /*0100404*/ 0x8503, /*0102403*/
|
||||
0x8202, /*0101002*/ 0x0501, /*0002401*/ 0x8301, /*0101401*/ 0x0000, /*0000000*/ 0x0AC6, /*0005306*/ 0x8003, /*0100003*/ 0x0302, /*0001402*/ 0x0401, /*0002001*/
|
||||
0x0701, /*0003401*/ 0x0000, /*0000000*/ 0x0C06, /*0006006*/ 0x8402, /*0102002*/ 0x8601, /*0103001*/ 0x0201, /*0001001*/ 0x0000, /*0000000*/ 0x15C6, /*0012706*/
|
||||
0xAAAA, /*0125252*/ 0x1180, /*0010600*/ 0x1001, /*0010001*/ 0x1042, /*0010102*/ 0x1083, /*0010203*/ 0x10C4, /*0010304*/ 0x1105, /*0010405*/ 0xE141, /*0160501*/
|
||||
0x0501, /*0002401*/ 0x0301, /*0001401*/ 0x0000, /*0000000*/ 0x0C42, /*0006102*/ 0x8601, /*0103001*/ 0x0501, /*0002401*/ 0x0000, /*0000000*/ 0x6083, /*0060203*/
|
||||
0x0A83, /*0005203*/ 0x0A43, /*0005103*/ 0x60C1, /*0060301*/ 0x8701, /*0103401*/ 0x0701, /*0003401*/ 0x0000, /*0000000*/ 0x0C04, /*0006004*/ 0x5103, /*0050403*/
|
||||
0x6143, /*0060503*/ 0x0A83, /*0005203*/ 0x8702, /*0103402*/ 0x0AC1, /*0005301*/ 0x0501, /*0002401*/ 0x0000, /*0000000*/ 0x0A40, /*0005100*/ 0x8301, /*0101401*/
|
||||
0x0000, /*0000000*/ 0x4001, /*0040001*/ 0x6041, /*0060101*/ 0x0601, /*0003001*/ 0x0701, /*0003401*/ 0x0000, /*0000000*/ 0x00C1, /*0000301*/ 0x2057, /*0020127*/
|
||||
0x5455, /*0052125*/ 0x0204, /*0001004*/ 0x3105, /*0030405*/ 0x0602, /*0003002*/ 0x0A45, /*0005105*/ 0x0201, /*0001001*/ 0x0000, /*0000000*/ 0x95C0, /*0112700*/
|
||||
0xFF01, /*0177401*/ 0x8001, /*0100001*/ 0x0000, /*0000000*/ 0x7E02, /*0077002*/ 0x0A01, /*0005001*/ 0x0A81, /*0005201*/ 0x7E02, /*0077002*/ 0x0BC0, /*0005700*/
|
||||
0x0202, /*0001002*/ 0x0BC1, /*0005701*/ 0x0301, /*0001401*/ 0x0000, /*0000000*/ 0x15C6, /*0012706*/ 0x01FE, /*0000776*/ 0x09F7, /*0004767*/ 0x0002, /*0000002*/
|
||||
0x0000, /*0000000*/ 0x25CE, /*0022716*/ 0x00D0, /*0000320*/ 0x0301, /*0001401*/ 0x0000, /*0000000*/ 0x15CE, /*0012716*/ 0x00E2, /*0000342*/ 0x0087, /*0000207*/
|
||||
0x0000, /*0000000*/ 0x0A26, /*0005046*/ 0x15E6, /*0012746*/ 0x00EC, /*0000354*/ 0x0002, /*0000002*/ 0x0000, /*0000000*/ 0x005F, /*0000137*/ 0x00F2, /*0000362*/
|
||||
0x0080, /*0000200*/ 0x15C5, /*0012705*/ 0xE000, /*0160000*/ 0x0A1F, /*0005037*/ 0x0006, /*0000006*/ 0x15DF, /*0012737*/ 0x0100, /*0000400*/ 0x0004, /*0000004*/
|
||||
0x15C6, /*0012706*/ 0x01FE, /*0000776*/ 0x0BE5, /*0005745*/ 0x15DF, /*0012737*/ 0x01CC, /*0000714*/ 0x004C, /*0000114*/ 0x0A1F, /*0005037*/ 0x004E, /*0000116*/
|
||||
0x15C3, /*0012703*/ 0xFFE6, /*0177746*/ 0x15CB, /*0012713*/ 0x000C, /*0000014*/ 0x15C2, /*0012702*/ 0x0200, /*0001000*/ 0x1080, /*0010200*/ 0x1008, /*0010010*/
|
||||
0x0BD0, /*0005720*/ 0x2005, /*0020005*/ 0x83FC, /*0101774*/ 0x1080, /*0010200*/ 0x1201, /*0011001*/ 0x2001, /*0020001*/ 0x0301, /*0001401*/ 0x0000, /*0000000*/
|
||||
0x0A50, /*0005120*/ 0x2005, /*0020005*/ 0x83F9, /*0101771*/ 0x1801, /*0014001*/ 0x0A41, /*0005101*/ 0x2001, /*0020001*/ 0x0301, /*0001401*/ 0x0000, /*0000000*/
|
||||
0x2002, /*0020002*/ 0x02F9, /*0001371*/ 0x0A0E, /*0005016*/ 0x15C4, /*0012704*/ 0xAAAA, /*0125252*/ 0x15CB, /*0012713*/ 0x0018, /*0000030*/ 0x15C0, /*0012700*/
|
||||
0x0800, /*0004000*/ 0x15C2, /*0012702*/ 0x0200, /*0001000*/ 0x0A44, /*0005104*/ 0x1108, /*0010410*/ 0x0A48, /*0005110*/ 0x0A48, /*0005110*/ 0x2204, /*0021004*/
|
||||
0x0301, /*0001401*/ 0x0000, /*0000000*/ 0x0C1F, /*0006037*/ 0xFFEA, /*0177752*/ 0x8702, /*0103402*/ 0x0000, /*0000000*/ 0x0131, /*0000461*/ 0x8A4E, /*0105116*/
|
||||
0x02F2, /*0001362*/ 0x0102, /*0000402*/ 0x0077, /*0000167*/ 0xFE88, /*0177210*/ 0x0BD0, /*0005720*/ 0x7E93, /*0077223*/ 0x15CB, /*0012713*/ 0x0024, /*0000044*/
|
||||
0x15C0, /*0012700*/ 0x0C00, /*0006000*/ 0x8A76, /*0105166*/ 0x0001, /*0000001*/ 0x02E4, /*0001344*/ 0x15C2, /*0012702*/ 0x0200, /*0001000*/ 0x1080, /*0010200*/
|
||||
0x1008, /*0010010*/ 0x0BD0, /*0005720*/ 0x2005, /*0020005*/ 0x83FC, /*0101774*/ 0x15C1, /*0012701*/ 0x0003, /*0000003*/ 0x0A0E, /*0005016*/ 0x0BDF, /*0005737*/
|
||||
0x01C6, /*0000706*/ 0x0210, /*0001020*/ 0x15CE, /*0012716*/ 0x0018, /*0000030*/ 0x1080, /*0010200*/ 0x0A48, /*0005110*/ 0x0A48, /*0005110*/ 0x2008, /*0020010*/
|
||||
0x0301, /*0001401*/ 0x0000, /*0000000*/ 0x0BD0, /*0005720*/ 0x0C1F, /*0006037*/ 0xFFEA, /*0177752*/ 0x8702, /*0103402*/ 0x0000, /*0000000*/ 0x0108, /*0000410*/
|
||||
0x2005, /*0020005*/ 0x83F3, /*0101763*/ 0x138B, /*0011613*/ 0x0A0E, /*0005016*/ 0x7E51, /*0077121*/ 0x0104, /*0000404*/ 0x0000, /*0000000*/ 0x0102, /*0000402*/
|
||||
0x15CB, /*0012713*/ 0x000C, /*0000014*/ 0x17C0, /*0013700*/ 0x01C0, /*0000700*/ 0x17C1, /*0013701*/ 0x01C2, /*0000702*/ 0x17C4, /*0013704*/ 0x01C4, /*0000704*/
|
||||
0x0074, /*0000164*/ 0x0002, /*0000002*/ 0x17C4, /*0013704*/ 0xFF78, /*0177570*/ 0x45C4, /*0042704*/ 0xFE00, /*0177000*/ 0x55C4, /*0052704*/ 0xF600, /*0173000*/
|
||||
0x97C0, /*0113700*/ 0xFF79, /*0177571*/ 0x0C80, /*0006200*/ 0x00A1, /*0000241*/ 0x004C, /*0000114*/ 0x0000, /*0000000*/ 0x4230, /*0041060*/ 0x2A2D /*0025055*/
|
||||
];
|
||||
|
||||
/**
|
||||
* initBus(cmp, bus, cpu, dbg)
|
||||
*
|
||||
|
|
|
|||
|
|
@ -46,7 +46,8 @@ var MessagesPDP11 = {
|
|||
KEYS: 0x00020000,
|
||||
PAPER: 0x00100000,
|
||||
DISK: 0x00400000,
|
||||
SERIAL: 0x00800000,
|
||||
RL11: 0x00800000,
|
||||
SERIAL: 0x01000000,
|
||||
SPEAKER: 0x02000000,
|
||||
COMPUTER: 0x04000000,
|
||||
LOG: 0x10000000,
|
||||
|
|
@ -83,6 +84,7 @@ MessagesPDP11.CATEGORIES = {
|
|||
"key": MessagesPDP11.KEYS, // using "key" instead of "keys", since the latter is a method on JavasScript objects
|
||||
"paper": MessagesPDP11.PAPER,
|
||||
"disk": MessagesPDP11.DISK,
|
||||
"rl11": MessagesPDP11.RL11,
|
||||
"serial": MessagesPDP11.SERIAL,
|
||||
"speaker": MessagesPDP11.SPEAKER,
|
||||
"computer": MessagesPDP11.COMPUTER,
|
||||
|
|
|
|||
|
|
@ -308,11 +308,11 @@ RAMPDP11.prototype.loadImage = function(aBytes, addrLoad, addrExec, addrInit, fR
|
|||
}
|
||||
var offBlock = off;
|
||||
if (w != 0x0001) {
|
||||
if (MAXDEBUG) this.println("invalid signature (" + str.toHexWord(w) + ") at offset " + str.toHexWord(offBlock));
|
||||
this.printMessage("invalid signature (" + str.toHexWord(w) + ") at offset " + str.toHexWord(offBlock), MessagesPDP11.PAPER);
|
||||
break;
|
||||
}
|
||||
if (off + 6 >= aBytes.length) {
|
||||
if (MAXDEBUG) this.println("invalid block at offset " + str.toHexWord(offBlock));
|
||||
this.printMessage("invalid block at offset " + str.toHexWord(offBlock), MessagesPDP11.PAPER);
|
||||
break;
|
||||
}
|
||||
off += 2;
|
||||
|
|
@ -326,12 +326,12 @@ RAMPDP11.prototype.loadImage = function(aBytes, addrLoad, addrExec, addrInit, fR
|
|||
len--;
|
||||
}
|
||||
if (len != 0 || off >= aBytes.length) {
|
||||
if (MAXDEBUG) this.println("insufficient data for block at offset " + str.toHexWord(offBlock));
|
||||
this.printMessage("insufficient data for block at offset " + str.toHexWord(offBlock), MessagesPDP11.PAPER);
|
||||
break;
|
||||
}
|
||||
checksum += aBytes[off++] & 0xff;
|
||||
if (checksum & 0xff) {
|
||||
if (MAXDEBUG) this.println("invalid checksum (" + str.toHexByte(checksum) + ") for block at offset " + str.toHexWord(offBlock));
|
||||
this.printMessage("invalid checksum (" + str.toHexByte(checksum) + ") for block at offset " + str.toHexWord(offBlock), MessagesPDP11.PAPER);
|
||||
break;
|
||||
}
|
||||
if (!cbData) {
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ if (NODE) {
|
|||
*/
|
||||
function RL11(parms)
|
||||
{
|
||||
Component.call(this, "RL11", parms, RL11, MessagesPDP11.DISK);
|
||||
Component.call(this, "RL11", parms, RL11, MessagesPDP11.RL11);
|
||||
|
||||
/*
|
||||
* We record any 'autoMount' object now, but we no longer parse it until initBus(),
|
||||
|
|
@ -963,6 +963,8 @@ RL11.prototype.processCommand = function()
|
|||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* endReadWrite(err, iCylinder, iHead, iSector, nWords, addr)
|
||||
*
|
||||
|
|
@ -1027,7 +1029,7 @@ RL11.prototype.readData = function(drive, iCylinder, iHead, iSector, nWords, add
|
|||
err = PDP11.RL11.ERRC.HNF;
|
||||
break;
|
||||
}
|
||||
var data = this.bus.setWord(addr, b0 | (b1 << 8));
|
||||
var data = this.bus.setWordDirect(addr, b0 | (b1 << 8));
|
||||
if (this.bus.checkFault()) {
|
||||
err = PDP11.RL11.ERRC.NXM;
|
||||
break;
|
||||
|
|
@ -1075,7 +1077,7 @@ RL11.prototype.writeData = function(drive, iCylinder, iHead, iSector, nWords, ad
|
|||
}
|
||||
|
||||
while (nWords--) {
|
||||
var data = this.bus.getWord(addr);
|
||||
var data = this.bus.getWordDirect(addr);
|
||||
if (this.bus.checkFault()) {
|
||||
err = PDP11.RL11.ERRC.NXM;
|
||||
break;
|
||||
|
|
|
|||
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