diff --git a/.gitignore b/.gitignore new file mode 100755 index 0000000..d893ce6 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +os-images/* diff --git a/RSTSv06c.html b/RSTSv06c.html new file mode 100755 index 0000000..4dca806 --- /dev/null +++ b/RSTSv06c.html @@ -0,0 +1,1970 @@ + + + +
++ +Most of my experiences with RSTS were with versions 5 and 6 on a PDP 11/45. So I thought that it would be nice if I could set up a RSTS V5 or V6 system for my PDP 11/70 emulator. I assumed that this would be a simple enough task. +
+I did some reading of release notes and discovered that V6 was the first RSTS version to support the PDP 11/70, and also to be able to boot from a non-zero drive unit - ie DK2 instead of DK0. I didn't really need full PDP 11/70 support but I have found that many PDP operating systems prefer or require a zero disk unit number. As a result zero drive numbers have become a premium on my emulated system - so V6 seemed preferable to V5. +
+I looked around and managed to find a V6C installation tape for download at RSTS.ORG. Interestingly I could find no other V5 or V6 distributions anywhere. I did find some old postings from other people who expressed a similar desire to find one. +
+I grabbed the one V6C tape I could find and began an install to an RK05 disk image using SIMH. It let me initialize the disk but when I went to copy files onto it is failed with a disk index error. +
+
+Option: COpy +INIT bug - bad disk index +Fatal RSTS/E system initialization error! +Option: ++
+Hmmm I thought, I'm doing something wrong. So I checked the manual and the examples there showed nothing different to what I was doing. I experimented with doing things in a different order, different types of output disk, and different SIMH machine configurations. None of the things I tried seemed to work. +
+I contacted Brett Bump from RSTS.ORG and asked about the error. He advised that the tape copy I was using had always had this problem... as far back as 1984 when it generated the same error on a real PDP 11/45. Ouch! +
+So I repeated my earlier web searches for a RSTS V5 or V6 distribution or disk image. Nothing. In deperation I posted to alt.sys.pdp11 and asked if anyone out there had one. I have not yet received any response to that. I also noticed a photo of a DECTape V6 distribution at the Wolford Witch Project so I fired off an email to ask about obtaining a copy of that, but sadly no response. Even if there was still an intact tape somewhere there was probably going to be a real battle to find hardware to read it. +
+Reconsidering the one distribution tape I had I wondered if the tape problems could be minor - perhaps with just a bit or two flipped from what they should be. I thought if I looked at the code maybe I could see if there was an obvious problem, and even if not perhaps I could work around the fault and manually cause it to copy the files I needed. After all it only had to successfully install RSTS once. +
+Using SIMH I examined the failing installation code and could see that it was indeed checking some sort of device index at the point of the error message, but because the value was negative it was triggering the error that I was seeing. I examined the code further and determined that for an RK05 the device index should be 6, which I validated after some experimentation. However even by patching in the correct index value other types of strange errors resulted. +
+I wondered if I could work around the problem by using a RSTS V7 installation to read the RSTS V6 tape, after all RSTS V7 is the closest thing I could find to the failing code. I tried a number of test set ups but this approach never looked like working. +
+Going back to debugging the failing V6 code I managed to find the command dispatch routine - this was important for understanding what the code was trying to do. Here I could see where a different routine was being called for each of the possible installation commands. Using this it was possible to trace and compare the operation of each individual command. Most worked by calling a common subroutine with a differnt parameter block. Oddly the failing copy routine did not use the common subroutine. The command dispatch is implemented as a JMP instruction to index into a jump table with R0 as an offset for each different command: +
+
+Option: COpy +Breakpoint, PC: 031176 (JMP @36370(R0)) +sim> ++
+I wondered if I could somehow use the V7 code to understand more deeply what was going on - so I looked in the V7 installation for a similar command dispatch routine. After much searching I determined that V7 is structured completely differently and has no equivalent dispatch or jump table. Further it appeared that the V7 copy routine is rolled into a common subroutine with other commands unlike the V6 code. Perhaps this meant that the V6 command table was simply corrupt? However my exploration down that path yielded nothing useful. +
+During my code exploration I had disassembled a lot of code and it occured to me to try searching for a subroutine which seemed to have similar input registers and do similar initialization to the working disk initization routine. I found one - which was similar enough that it even called some of the same subroutines, including one to get the date and time. This is something that some of the commands like disk INitialise and COpy do. However there was nothing to reference this orphaned code, and in any case it would not run successfully when I tried to manually execute it. +
+A few days later I was tracing the code again and determined that the disk COpy routine really started to go wrong when it jumps to an address at 124000. The code there immediately expects to use register R4 which has not previously been initialized. I experimented with various options, maybe R4 was supposed to be a different register, or perhaps address 124000 was corrupted with the lower byte being zeroed. After playing around with various permutations I realized that the code I had earlier suspected of being the COpy code was located at address 123000, exactly one tape block in length away. I immediately grabbed a binary file editor and inserted a dummy tape block before the code at 123000 - relocating it to address 124000. +
+Start of code for disk INitialise which works and which is similar in function to disk Copy: +
+sim> ex -m 104002-104100 +104002: MOV #12,R0 +104006: MOV #113210,R2 +104012: MOVB (R1)+,(R2)+ +104014: BEQ 104024 +104016: SOB R0,104012 +104020: CLRB 113211 +104024: JSR PC,@#43620 +104030: CLRB 42677 ++
+Similar orphaned code elswhere in memory at address 123000: +
+sim> ex -m 123000-123100 +123000: MOV #12,R0 +123004: MOV #135432,R2 +123010: MOVB (R1)+,(R2)+ +123012: BEQ 123022 +123014: SOB R0,123010 +123016: CLRB 134433 +123022: JSR PC,@#43620 ++
+Failing code called by disk COpy at address 124000 (uses R4 which has not been set) +
+sim> ex -m 124000-12410 +124000: MOV R2,52(R4) +124004: JSR PC,@#47726 +124010: MOV R0,54(R4) +124014: JSR PC,126104 +124020: JSR PC,125462 +124024: JSR PC,@#46774 ++
+Success! Adding the dummy tape block relocated the above orphaned code to a memory location now invoked by the COpy command, which worked and copied files without error. Yipee! As a minor downside the previously working disk INitialise command now failed with errors about a file called BADB.SYS missing. I used the V7 installation tape to initialize a disk and then the V6 COpy command to put files onto it. It looked like I might actually be able to get an installation working. +
+But alas the next step to build the key RSTS.SIL operating system load image failed. When reviewing the installation log it appeared that the Macro assembler used to build the image had simply not produced any output object files. No errors or messages were reported. One possibility was that the disk initialized by V7 was causing some sort of problem for the V6 system. +
+So it was time to go back and find out what I had broken with the disk INitialise routine. By reviewing disassembly listings I determined that when I had added my dummy tape block I had inadvertantly relocated other code which had previously worked. By moving my inserted dummy tape block to a different position I was get both disk INitialise and disk COpy to work. +
+So back to building my RSTS V6 system files. Still no success, the Macro assembler would not produce any files required for the build of RSTS.SIL - even though my own test Macro files all worked perfectly. +
+Figuring that perhaps the MACRO assembler had been corrupted I took all of the V6 source files to a RSTS V7 system and tried to build the files there. Unfortunately this had exactly the same problem - no output files and no error messages. How could that be? +
+I checked that my RSTS V7 system could properly build itself from its own source files. It did at least confirming that the Macro assembler there was fine. I began comparing the source macro files from V6 and V7. They have some parts in common but others are very different - as you would expect with files from different versions. +
+By experimenting with dummy input files I got to a point where I convinced myself that the problem was in a particular source file called KERNEL.MAC. When comparing V6 file differences with the V7 KERNEL.MAC I noticed that V6 contained an incomplete defintion for a macro - essentially the last part of the multi-line definition was missing. When I corrected that by including content borrowewd from the V7 source the V6 modules built successfully. I was able to generate the V6 RSTS.SIL image file and go on to complete the installation of RSTS V06C-03. +
+It appears that when the RSTS macro assembler encounters a multi-line macro without an end it assumes all of the remaining source file is part of the definition. At least until something overflows and it crashes without displaying any indication of error. My V6 Macro assembler was not corrupt - just the macro in the file KERNEL.MAC. +
+I decided to see if I could edit the missing section of the KERNEL.MAC macro directly into the installation tape. I doubt it is a coincidence that the section I had to insert was at the end of a tape block, and was exactly one tape block in length. +
+I think we can safely say that the installation tape has at least two completely missing blocks. +
+While comparing files I noticed another difference - the heading of another file called COMMON.MAC is missing it's copyright statement - I'm going to guess it is about one block in length and appears at the start of a tape block. I didn't bother correcting this as so far it hasn't caused any problems. +
+With just two insertions of tape blocks the V6 installation tape now allows me to completely install and configure a working RSTS V06C-03 system. It runs like a charm even though I will never completely trust it, as there is no realistic way to detect when random bits are missing. If any vital part not recoverable from another source had been zapped then I suspect RSTS V6 would have been lost forever. +
+As it stands I am unaware of any other working RSTS V5 or V6 systems. Oddly most of the V5 and V6 documentation is still available on bitsavers, just none of the distribution tapes. +
+It is nostalgic to look at this system and consider the different era which it is from. Lots of programmers cut their teeth in similar enviroments. I am hopeful that I can set the system up so that the system utilities written which are written in Basic can be viewed to give a greater understanding of how it was all put together. +
+I hope that you take the time to boot up and run a copy of RSTS V06C-03 (boot rk2 at http://skn.noip.me/pdp11/pdp11.html). Maybe even log into 11,70 with a password of PDP and play a game of ACEY.BAS to see how long it takes to win $500. +
+Paul Nankervis
+paulnank@hotmail.com
+http://skn.noip.me/pdp11/
+
+
+
+BOOT> boot tm1 + +Enabling only console, disks, and tapes. + + +RSTS V06C (MT1) + + +Option: DS + +13-Mar-76? +06:42 AM? + +Disk? DK0 +Pack ID? SYSGEN +Pack cluster size? 1 +SATT.SYS base? 0 +MFD password? SYSTEM +MFD cluster size? 8 +PUB, PRI, or SYS? SYS +Library password? SYSTEM +Library UFD cluster size? 8 +Date last modified? Y +New files first? N +Use previous bad block info? N +Format? N +Patterns? 1 +Proceed (Y or N)? Y + +Pattern # 1 + +Option: DS + +13-Mar-76? +06:42 AM? + +Disk? DK2 +Pack ID? VIXEN +Pack cluster size? 1 +SATT.SYS base? 0 +MFD password? SYSTEM +MFD cluster size? 8 +PUB, PRI, or SYS? SYS +Library password? SYSTEM +Library UFD cluster size? 8 +Date last modified? Y +New files first? N +Use previous bad block info? N +Format? N +Patterns? 1 +Proceed (Y or N)? Y + +Pattern # 1 + +Option: CO + +13-Mar-76? +06:43 AM? + +To which disk? DK0 + +Enabling only console, disks, and tapes. + + +RSTS V06C (DK0) + + +Option: IN + + SIL? SYSGEN + Rebooting . . . + + + + +RSTS V06C (DK0) + + +Option: HA + + HARDWR suboption? HE + New AC line hertz? 50 + + HARDWR suboption? + + 1 change being made. + + Rebooting . . . + + + + +RSTS V06C (DK0) + + +Option: RE + +13-Mar-76? +06:43 AM? + +Disk? DK0 + +Clean? Y + +Disk is being cleaned - wait ... + + + REFRESH suboption? CH + + SWAP.SYS changes? Y + + Size? 228 + + Base? 0 + + SWAP0.SYS changes? N + + SWAP1.SYS changes? N + + SWAP3.SYS changes? N + + OVR.SYS changes? N + + ERR.SYS changes? N + + BUFF.SYS changes? N + + CRASH.SYS changes? N + + Other files? N + + REFRESH suboption? + + +Option: DE + +No defaults are currently set + +You currently have: JOB MAX = 2, SWAP MAX = 16K. + +JOB MAX or SWAP MAX changes? Y + + New JOB MAX? 2 + + New SWAP MAX? 28 + +You currently have: JOB MAX = 2, SWAP MAX = 28K. + +JOB MAX or SWAP MAX changes? N + +Run Time System? RT11 + +Error message file? ERR + +Installation name? Vixen + + Memory allocation table: + + 0K: 00000000 - 00117777 ( 20K) : EXEC + 20K: 00120000 - 00137777 ( 4K) : RTS (RT11) + 24K: 00140000 - 03777777 ( 488K) : USER + 512K: 04000000 - End : NXM + + + Table suboption? + +You currently have crash dump disabled. +Warning - CRASH.SYS file of 24 blocks is not available + +Crash dump? N + +Magtape labelling default (none)? DOS + +Preferred clock (L)? + +Date format (NUMERIC)? AL + +Time format (24-HOUR)? AM + +Power fail delay (1)? + + +Option: ST + +You currently have: JOB MAX = 2, SWAP MAX = 28K. + +JOB MAX or SWAP MAX changes? N + +Any memory allocation changes? N + +You currently have crash dump disabled. + +13-Mar-76? +06:47 AM? 6:42 +BUFF.SYS not found or too small - DECtape disabled +DF0: disabled - no RF: controller +DS0: disabled - no RS: controller +DS1: disabled - no RS: controller +DM0: disabled - no RM: controller +DM1: disabled - no RM: controller +DP0: disabled - no RP: controller +DP1: disabled - no RP: controller +DR0: disabled - no RR: controller +DR1: disabled - no RR: controller +DB0: disabled - no RB: controller +DB1: disabled - no RB: controller +LP0: disabled - no LP0: controller +MM0: disabled - no TU: controller +MM1: disabled - no TU: controller +DT0: disabled - no TC: controller +DT1: disabled - no TC: controller +DX0: disabled - no RX0: controller +DX1: disabled - no RX0: controller + +18 devices disabled + +?Can't find file or account +.R MT1:CREATE + + + + + + ^C + HELLO 1/2 + Password: + 1 other user is logged in under this account + + . + .ASSIGN MT1: .DOS + + .ASSIGN MT1: IN + + .R IN:PIP.SAV + *SY:$*.*<232>=IN:$LOGIN.SAV,$LOGOUT.SAV,$PIP.SAV + *SY:$*.*<104>=IN:$UTILTY.SAV + *SY:$*.*<104>=IN:$MACRO.SAV,$CREF.SAV,$LINK.SAV + *SY:$*.*<104>=IN:$SILUS.SAV,$HOOK.SAV,$SYSGEN.SAV/NOREW + *SY:$*.*<124>=IN:$SYSBAT.SAV + *SY:$*.*<104>=IN:$ONLPAT.SAV + *DK:$*.*<40>=IN:$ERR.STB,$PIPSAV.TXT/NOREW + *^C + + .DEASSIGN IN + + .DEASSIGN MT1: + + .R LOGOUT + Confirm: Y + Saved all disk files; 357 blocks in use + Job 2 User 1,2 logged off KB1 at 13-Mar-76 06:42 AM + 1 other user still logged in under this account + System RSTS V06C-03 Vixen + Run time was 0 seconds + Elapsed time was 0 minutes + Good morning + + + + + + + + + + + +**13-Mar-76** + + +Beginning of RSTS/E system generation. + + +Questions come in long and short forms. +If you are familiar with them, answer +"S" for short; otherwise, answer "L" for +long form. + +Form ? #S # + +Same system ? #Y # + +Distribution medium ? #MT# + +Output medium ? #MT# DK + +Pack ID ? #??# VIXEN + +Delete files ? #NO# + +LP for SYSGEN ? *NO* + +Generate monitor ? #Y # Y/L + +Monitor name ? #RSTS# + +Monitor patching ? #??# N + +Save RT11.RTS and PIP.SAV ? #Y # + +Generate BASIC-PLUS ? #Y # + +BASIC-PLUS RTS name ? #BASIC# + +BASIC-PLUS patching ? #??# N + + +Now you must specify the hardware con- +figuration on which this RSTS/E system +will run. + +KL11,LC11,DL11A,DL11B's ? *01* + +DL11C, DL11D's ? *00* + +DC11's ? *00* + +DL11E's ? *00* + +DJ11's ? *00* + +DH11's ? *00* + +DZ11's ? *00* + +Pseudo keyboards ? #04# + +Multi-terminal service ? #Y # + +Echo control ? #Y # + +RC11/RS64's ? *NO* + +RF/RS11's ? *NO* + +RS03/RS04's ? *00* + +RK05's ? #08# + +Overlapped seek ? *Y * N + +RK06's ? *00* + +RP02/RP03's ? *00* + +RM03's ? *00* + +RP04/RP05/RP06's ? *00* + +TU16/TE16/TU45's ? *00* + +TU10/TE10/TS03's ? #08# + +DECtapes ? *00* + +Printers ? *00* + +RX01's ? *00* + +CR11/CM11 card reader ? *NO* + +CD11 card reader ? *NO* + +P.T. reader ? *NO* + +P.T. punch ? *NO* + +DMC11's ? *00* + +2780 support ? #NO# + +Maximum jobs ? #10# 31 + +Small buffers ? #359# 400 + +System wide logicals ? #10# + +Monitor statistics ? #NO# + +FIP buffering ? #Y # + +Resident disk handling ? #Y # + +Resident send/receive ? #NO# Y + +Resident simple SYS calls ? #NO# Y + +Resident file delete/rename ? #NO# Y + +Res. login/attach/attribute ? #NO# Y + +Resident catalog/lookup ? #NO# Y + + +The following questions deal with the +BASIC-PLUS run-time system + +FPP ? *NO* + +FIS ? *NO* + +Math precision ? #02# 4 + +Log functions ? #Y # + +Trig functions ? #Y # + +Print using ? #Y # + +Matrices ? #NO# Y + +String arithmetic ? #NO# + + +The system generation dialog is finish- +ed. If you have any special requirements +which require editing the generated file +CONFIG.MAC(system configuration file) or +SYSGEN.CTL(batch control file) you may +do it now. When ready type "R SYSBAT". + +.R SYSBAT + + + +SYSGEN batch processing has started. +If any problems develop during the batch +process it may be aborted by typing +"Control/C". To restart type "R SYSBAT". + + + + + + ^C + HELLO 1/2 + Password: + 1 other user is logged in under this account + + . + .SIZE 24 + + +MOUNT AP-2773F-BC OR AP-2772F-BC ON A MAGTAPE DRIVE + +WITH NO "WRITE RING" AND SET TO "ON LINE" + +Mount MT1:"SYSGNF"-write locked +Unit ? 0 + .ASSIGN MT1: .DOS + + .ASSIGN MT1: TAPE + + .R PIP.SAV + **.*<40>=TAPE:$COMMON.MAC + **.*=TAPE:$*.MAC/HALT/NOREW + **.*=TAPE:$*.OBJ/HALT/NOREW + **.*=TAPE:$*.SAV/HALT/NOREW + *ODT.SAV<60>/RE + *DEFALT.SAV<60>/RE + **.*=TAPE:$*.STB/HALT/NOREW + *^C + + +Dismount MT1: + .DEASSIGN TAPE + + .DEASSIGN MT1: + + + .ASSIGN SY: IN + + .R PIP.SAV + *TBL.OBJ,TTDINT.OBJ,TTDVR.OBJ/DE + ?Can't find file or account - file TBL .OBJ - continuing + ?Can't find file or account - file TTDINT.OBJ - continuing + ?Can't find file or account - file TTDVR .OBJ - continuing + *TBL.LST,TTDINT.LST,TTDVR.LST/DE + ?Can't find file or account - file TBL .LST - continuing + ?Can't find file or account - file TTDINT.LST - continuing + ?Can't find file or account - file TTDVR .LST - continuing + *RSTS.SAV,TER.SAV,EMT.SAV,FIP.SAV,OVR.SAV/DE + ?Can't find file or account - file RSTS .SAV - continuing + ?Can't find file or account - file TER .SAV - continuing + ?Can't find file or account - file EMT .SAV - continuing + ?Can't find file or account - file FIP .SAV - continuing + ?Can't find file or account - file OVR .SAV - continuing + *RSTS.MAP,TER.MAP,EMT.MAP,FIP.MAP,OVR.MAP/DE + ?Can't find file or account - file RSTS .MAP - continuing + ?Can't find file or account - file TER .MAP - continuing + ?Can't find file or account - file EMT .MAP - continuing + ?Can't find file or account - file FIP .MAP - continuing + ?Can't find file or account - file OVR .MAP - continuing + *RSTS.STB,TER.STB,EMT.STB,FIP.STB,OVR.STB/DE + ?Can't find file or account - file RSTS .STB - continuing + ?Can't find file or account - file TER .STB - continuing + ?Can't find file or account - file EMT .STB - continuing + ?Can't find file or account - file FIP .STB - continuing + ?Can't find file or account - file OVR .STB - continuing + *NSP.SAV,RJ2780.SAV/DE + ?Can't find file or account - file NSP .SAV - continuing + ?Can't find file or account - file RJ2780.SAV - continuing + *NSP.MAP,RJ2780.MAP/DE + ?Can't find file or account - file NSP .MAP - continuing + ?Can't find file or account - file RJ2780.MAP - continuing + *NSP.STB,RJ2780.STB/DE + ?Can't find file or account - file NSP .STB - continuing + ?Can't find file or account - file RJ2780.STB - continuing + *^C + + .R MACRO.SAV + *TTDVR,TTDVR/C=IN:COMMON,KERNEL,DK:CONFIG,IN:CHECK,KBDEF,TTDVR + ERRORS DETECTED: 0 + *^C + + .R MACRO.SAV + *TTDINT,TTDINT/C=IN:COMMON,KERNEL,DK:CONFIG,IN:CHECK,KBDEF,TTDINT + ERRORS DETECTED: 0 + *^C + + .R MACRO.SAV + *TBL,TBL/C=IN:COMMON,KERNEL,DK:CONFIG,IN:CHECK,TBL + ERRORS DETECTED: 0 + *^C + + .R LINK.SAV + *RSTS/Z,RSTS/A/W,RSTS=TBL,$ERR.STB/X/B:0/U:#1000/I/C + *TTDINT/C + *IN:RSTS + Round section? MORBUF + Library search? BUF + Library search? LIGHTS + Library search? + *^C + + .R LINK.SAV + *TER/Z,TER/A/W,TER=IN:TER,DK:RSTS.STB/X/B:#117000/U:#1000/C + *TTDVR + Round section? TERPAT + *^C + + .R LINK.SAV + *EMT/Z,EMT/A/W,EMT=IN:EMT,DK:RSTS.STB/X/B:#117000/U:#1000/C + *IN:RSTS + Round section? EMTPAT + *^C + + .R LINK.SAV + *FIP/Z,FIP/A/W,FIP=IN:FIP,DK:RSTS.STB/X/B:#117000/U:#1000/I/C + *IN:RSTS + Round section? FIPPAT + Library search? OPN + Library search? UUO + Library search? SND + Library search? LIN + Library search? DLN + Library search? DIR + Library search? + *^C + + .R LINK.SAV + *OVR/Z,OVR/A/W,OVR=IN:OVR,DK:FIP.STB/X/B:#1000/C + *IN:RSTS + *^C + + .R SILUS.SAV + *RSTS.SIL=RSTS,TER/M,EMT/M,FIP/M/C + *OVR/M/C + *IN:ODT,DEFALT + *^C + + .R PIP.SAV + *BASIC.SAV,BASIC.STB/DE + ?Can't find file or account - file BASIC .SAV - continuing + ?Can't find file or account - file BASIC .STB - continuing + +MOUNT AP-2773F-BC OR AP-2772F-BC ON A MAGTAPE DRIVE + +WITH NO "WRITE RING" AND SET TO "ON LINE" + +Mount MT1:"SYSGNF"-write locked +Unit ? 0 + *^C + + .ASSIGN MT1: .DOS + + .ASSIGN MT1: TAPE + + .R PIP.SAV + **.*=TAPE:$RTS.OBJ + **.*=TAPE:$*.OBJ/NOREW + *^C + + +Dismount MT1: + .DEASSIGN TAPE + + .DEASSIGN MT1: + + .R LINK.SAV + *BASIC/Z,BASIC/A/W,BASIC=IN:RTS,DK:$ERR.STB/X/H:#177776/U:#4000/C + *IN:MA4/C + *IN:XL4/C + *IN:XT4/C + *IN:IO/C + *IN:PU/C + *IN:MX/C + *IN:SN/C + *IN:VE + Round section? PA + *^C + + .R SILUS.SAV + *BASIC.RTS=BASIC + *^C + + .DEASSIGN IN + + +MOUNT THE NEWLY INITIALIZED DISK "VIXEN" ON A DISK DRIVE + +READY AND WRITE ENABLE THE DRIVE + +Mount DK:"VIXEN "-write enabled +Unit ? 2 + .ASSIGN DK2: VIXEN + + .R PIP.SAV + *VIXEN:[0,1]*.*=SY0:[0,1]INIT.SYS + *^C + + .R HOOK.SAV + *VIXEN:[0,1]INIT.SYS + Hook complete + *^C + + .R PIP.SAV + *VIXEN:[0,1]*.*/CLU:-8./MODE:16.=SY0:[0,1]ERR.ERR + *VIXEN:[0,1]*.*=RSTS.SIL + *^C + + .R PIP.SAV + *VIXEN:[0,1]*.*/MODE:16.=BASIC.RTS + *VIXEN:[0,1]*.*/MODE:16.=SY0:[0,1]RT11.RTS + *VIXEN:$*.*=$PIP.SAV,$PIPSAV.TXT + *^C + + +Dismount DK2: + .DEASSIGN VIXEN + + .R LOGOUT + Confirm: Y + Saved all disk files; 3977 blocks in use + Job 2 User 1,2 logged off KB1 at 13-Mar-76 06:44 AM + 1 other user still logged in under this account + System RSTS V06C-03 Vixen + Run time was 8.1 seconds + Elapsed time was 0 minutes + Good morning + + + + + + + + + + +Batch job completed. + +. +BOOT> boot rk2 + +Enabling only console, disks, and tapes. + + +RSTS V06C (DK2) + + +Option: INS + + SIL? RSTS + Rebooting . . . + + + + +RSTS V06C (DK2) + + +Option: HA + + HARDWR suboption? HE + New AC line hertz? 50 + + HARDWR suboption? + + 1 change being made. + + Rebooting . . . + + + + +RSTS V06C (DK2) + + +Option: RE + +DD-MMM-YY? 13-MAR-76 +12:00 PM? 6:42 + +Disk? DK2 + +Clean? Y + +Disk is being cleaned - wait ... + + + REFRESH suboption? CH + + SWAP.SYS changes? Y + + Size? 228 + + Base? 0 + + SWAP0.SYS changes? N + + SWAP1.SYS changes? N + + SWAP3.SYS changes? N + + OVR.SYS changes? N + + ERR.SYS changes? N + + BUFF.SYS changes? N + + CRASH.SYS changes? N + + Other files? N + + REFRESH suboption? + + +Option: DE + +No defaults are currently set + +You currently have: JOB MAX = 31, SWAP MAX = 16K. + +JOB MAX or SWAP MAX changes? Y + + New JOB MAX? 31 + + New SWAP MAX? 28 + +You currently have: JOB MAX = 31, SWAP MAX = 28K. + +JOB MAX or SWAP MAX changes? N + +Run Time System? BASIC + +Error message file? ERR + +Installation name? Vixen + + Memory allocation table: + + 0K: 00000000 - 00147777 ( 26K) : EXEC + 26K: 00150000 - 00247777 ( 16K) : RTS (BASIC) + 42K: 00250000 - 03777777 ( 470K) : USER + 512K: 04000000 - End : NXM + + + Table suboption? + +You currently have crash dump disabled. +Warning - CRASH.SYS file of 42 blocks is not available + +Crash dump? N + +Magtape labelling default (none)? DOS + +Preferred clock (L)? + +Date format (NUMERIC)? AL + +Time format (24-HOUR)? AM + +Power fail delay (1)? + + +Option: + +You currently have: JOB MAX = 31, SWAP MAX = 28K. + +You currently have crash dump disabled. + +13-Mar-76? +06:43 AM? +?Can't find file or account +?Program lost-Sorry + +Ready + +RUN MT1:BUILD +BUILD V06C-03 RSTS V06C-03 Vixen +System Build <No>? Y +Source Input Device <SY:>? MT1: +Library Output Device <SY:>? +Target System Device <SY0:>? +Library Account <[1,2]>? + *** Copying file MT1:[1,2]BUILD.CTL to SY:BUILD.TMP +ASSIGN SY0:SYSDSK +ASSIGN [1,2] +ASSIGN MT1:INPUT +OLD INPUT:$LOGIN +COMPILE SYSDSK:@LOGIN +CHAIN 'INPUT:$BUILD' 31000 + +Ready + + +Ready + + +Ready + + +Ready + + +Ready + + +Ready + +BUILD Detaching... + + + + +^C +HELLO + +RSTS V06C-03 Vixen Job 2 KB0 13-Mar-76 06:43 AM +#1/2 +Password: +Job 1 is detached under this account +Job number to attach to? +1 other user is logged in under this account + + + + + +Ready + +ASSIGN SY0:SYSDSK + +Ready + +ASSIGN SY:SYSTEM + +Ready + +ASSIGN [1,2] + +Ready + +ASSIGN MT1:INPUT + +Ready + + +!********** BUILD.CTL - STANDARD LIBRARY PROGRAMS + +OLD INPUT:$PATCPY + +Ready + +COMPILE SYSTEM:@PATCPY + +Ready + +OLD INPUT:$PBUILD + +Ready + +COMPILE SYSTEM:@PBUILD + +Ready + +OLD INPUT:$CPATCH + +Ready + +COMPILE SYSTEM:@CPATCH + +Ready + +OLD INPUT:$AUTOED + +Ready + +COMPILE SYSTEM:@AUTOED + +Ready + +OLD INPUT:$LOGOUT + +Ready + +COMPILE SYSDSK:@LOGOUT + +Ready + +OLD INPUT:$UTILTY + +Ready + +COMPILE SYSTEM:@UTILTY + +Ready + +OLD INPUT:$UTILT1 + +Ready + +COMPILE SYSTEM:@UTILT1 + +Ready + +OLD INPUT:$INIT + +Ready + +COMPILE SYSDSK:@INIT + +Ready + +OLD INPUT:$SHUTUP + +Ready + +COMPILE SYSTEM:@SHUTUP + +Ready + +OLD INPUT:$ERRBLD + +Ready + +COMPILE SYSTEM:@ERRBLD + +Ready + +RUN SYSTEM:@ERRBLD +ERRBLD V06C-03 RSTS V06C-03 Vixen + +Ready + +OLD INPUT:$ERRINT + +Ready + +COMPILE SYSTEM:@ERRINT + +Ready + +OLD INPUT:$ERRCPY + +Ready + +COMPILE SYSTEM:@ERRCPY + +Ready + +OLD INPUT:$PIPSML + +Ready + +COMPILE SYSTEM:@PIPSML<40> + +Ready + +OLD INPUT:$DIRECT + +Ready + +COMPILE SYSTEM:@DIRECT + +Ready + +OLD INPUT:$TTYSET + +Ready + +COMPILE SYSTEM:@TTYSET + +Ready + +OLD INPUT:$SYSTAT + +Ready + +COMPILE SYSTEM:@SYSTAT + +Ready + +OLD INPUT:$EDIT + +Ready + +COMPILE SYSTEM:@EDIT<40> + +Ready + +OLD INPUT:$EDITCH + +Ready + +COMPILE SYSTEM:@EDITCH<40> + +Ready + +OLD INPUT:$BUILD + +Ready + +COMPILE SYSTEM:@BUILD + +Ready + +OLD INPUT:$ERRDIS + +Ready + +COMPILE SYSTEM:@ERRDIS + +Ready + +OLD INPUT:$ERRDET + +Ready + +COMPILE SYSTEM:@ERRDET + +Ready + +OLD INPUT:$ANALYS + +Ready + +COMPILE SYSTEM:@ANALYS + +Ready + +OLD INPUT:$ANALY1 + +Ready + +COMPILE SYSTEM:@ANALY1 + +Ready + +OLD INPUT:$SYSCAT + +Ready + +COMPILE SYSTEM:@SYSCAT + +Ready + +OLD INPUT:$PRIOR + +Ready + +COMPILE SYSTEM:@PRIOR + +Ready + +OLD INPUT:$ODT + +Ready + +COMPILE SYSTEM:@ODT + +Ready + +OLD INPUT:$REACT + +Ready + +COMPILE SYSTEM:@REACT + +Ready + +OLD INPUT:$REORDR + +Ready + +COMPILE SYSTEM:@REORDR + +Ready + +OLD INPUT:$DSKINT + +Ready + +COMPILE SYSTEM:@DSKINT + +Ready + +OLD INPUT:$UMOUNT + +Ready + +COMPILE SYSTEM:@UMOUNT + +Ready + +OLD INPUT:$COPY + +Ready + +COMPILE SYSTEM:@COPY<40> + +Ready + +OLD INPUT:$FILCOM + +Ready + +COMPILE SYSTEM:@FILCOM<40> + +Ready + +OLD INPUT:$QUOLST + +Ready + +COMPILE SYSTEM:@QUOLST + +Ready + +OLD INPUT:$MONEY + +Ready + +COMPILE SYSTEM:@MONEY<40> + +Ready + +OLD INPUT:$GRIPE + +Ready + +COMPILE SYSTEM:@GRIPE + +Ready + +OLD INPUT:$TALK + +Ready + +COMPILE SYSTEM:@TALK + +Ready + +OLD INPUT:$PLEASE + +Ready + +COMPILE SYSTEM:@PLEASE + +Ready + +OLD INPUT:$INUSE + +Ready + +COMPILE SYSTEM:@INUSE<40> + +Ready + +OLD INPUT:$SWITCH + +Ready + +COMPILE SYSTEM:@SWITCH + +Ready + +RUN SYSTEM:@PIPSML +PIPSML V06C-03 - RSTS V06C-03 Vixen +#SYSTEM:@NOTICE.TXT<40>=INPUT:$NOTICE.TXT/FA +#SYSTEM:@HELP .TXT<40>=INPUT:$HELP .TXT/FA +#SYSDSK:@START .CTL =INPUT:$START .CTL/FA +#SYSTEM:@TTY .CMD =INPUT:$TTY .CMD/FA +#SYSTEM:@SPOOL .CMD =INPUT:$SPOOL .CMD/FA +#SYSTEM:@RTS .CMD =INPUT:$RTS .CMD/FA +#SYSTEM:@CCL .CMD =INPUT:$CCL .CMD/FA +#SYSDSK:@CRASH .CTL =INPUT:$CRASH .CTL/FA +#SYSTEM:@ANALYS.CMD =INPUT:$ANALYS.CMD +#SYSTEM:@UTILTY.TXT =INPUT:$UTILTY.TXT/FA +#SYSTEM:@PIPSML.TXT<40>=INPUT:$PIPSML.TXT/FA +#SYSTEM:@DIRECT.HLP<40>=INPUT:$DIRECT.HLP/FA +#SYSTEM:@ERRDIS.HLP =INPUT:$ERRDIS.HLP/FA +#SYSTEM:@ACCT .SYS =INPUT:$ACCT .SYS/FA +#SYSTEM:@COPY .TXT<40>=INPUT:$COPY .TXT/FA +#=SYSDSK:@LOGIN .BAC<232>/RE +#=SYSDSK:@LOGOUT.BAC<232>/RE +#=SYSTEM:@DIRECT.BAC<232>/RE +#=SYSTEM:@TTYSET.BAC<232>/RE +#=SYSTEM:@SYSTAT.BAC<232>/RE +#=SYSTEM:@UMOUNT.BAC<232>/RE +#=SYSTEM:@QUOLST.BAC<232>/RE +#=SYSTEM:@GRIPE .BAC<232>/RE +#=SYSTEM:@TALK .BAC<232>/RE +#=SYSTEM:@PLEASE.BAC<232>/RE +#=SYSTEM:@SWITCH.BAC<232>/RE +#^Z + + +Ready + +RUN SYSTEM:@UTILTY +UTILTY V06C-03 RSTS V06C-03 Vixen +#LOGINS +#EXIT + +Ready + +^C + +Ready + +HELLO + +RSTS V06C-03 Vixen Job 2 [1,2] KB0 13-Mar-76 06:45 AM +Job 1 is detached under this account +Job number to attach to? 1 +Attaching to job 1 + +BUILD Complete + +Ready + +RUN MT1:BUILD +BUILD V06C-03 RSTS V06C-03 Vixen +System Build <No>? +Source Input Device <SY:>? MT1: +Library Output Device <SY:>? +Library Account <[1,2]>? +Control File is? MT1:RSXBLD.CTL + *** Copying file MT1:RSXBLD.CTL to SY:BUILD.TMP +BUILD Detaching... + + + + +^C +HELLO + +RSTS V06C-03 Vixen Job 2 KB0 13-Mar-76 06:46 AM +#1/2 +Password: +Job 1 is detached under this account +Job number to attach to? +1 other user is logged in under this account + + + + + +Ready + +ASSIGN SY:SYSDSK + +Ready + +ASSIGN SY:SYSTEM + +Ready + +ASSIGN [1,2] + +Ready + +ASSIGN MT1:INPUT + +Ready + +! +! BUILD PROCEDURE FOR RSX RUN-TIME SYSTEM AND UTILITIES +! + +! +! MAKE SURE RT11 RUN-TIME SYSTEM IS ADDED +! +RUN $UTILTY +UTILTY V06C-03 RSTS V06C-03 Vixen +#ADD RT11 +#EXIT + +Ready + + +! +! COPY RUN-TIME SYSTEM FROM DISTRIBUTION KIT +! +RUN $PIP.SAV +*SYSTEM:[0,1]=INPUT:$RSX.RTS/BL +*SYSTEM:[0,1]/MODE:16.=SYSTEM:[0,1]RSX.RTS +*^Z + + +Ready + + +! +! ADD THE RUN-TIME SYSTEM +! +RUN $UTILTY +UTILTY V06C-03 RSTS V06C-03 Vixen +#ADD SYSTEM:RSX +#EXIT + +Ready + + +! +! COPY SYSTEM LIBRARIES AND PROGRAMS TO TARGET SYSTEM +! +ASSIGN <40> + +Ready + +RUN $PIP.SAV +*SYSTEM:[1,1]=INPUT:$SYSLIB.OLB/BL +*SYSTEM:[1,1]=INPUT:$RSXMAC.SML/BL +*SYSTEM:@<104>=INPUT:$TKB.TSK/BL +*SYSTEM:@<104>=INPUT:$MAC.TSK/BL +*SYSTEM:@<104>=INPUT:$LBR.TSK/BL +*SYSTEM:@<104>=INPUT:$PAT.TSK/BL +*SYSTEM:@<104>=INPUT:$EDT.TSK/BL +*SY:=INPUT:$EDTCOM.TXT +*^Z + + +Ready + + +! +! ENSURE RIGHT RTS NAMES ARE ASSOCIATED WITH .TSK FILES +! +RUN $UTILTY +UTILTY V06C-03 RSTS V06C-03 Vixen +#NAME RSX=SYSTEM:@TKB.TSK +#NAME RSX=SYSTEM:@MAC.TSK +#NAME RSX=SYSTEM:@LBR.TSK +#NAME RSX=SYSTEM:@PAT.TSK +#NAME RSX=SYSTEM:@EDT.TSK +#EXIT + +Ready + + +! +! ADD THE FOLLOWING UTILTY COMMANDS TO SYSTEM START-UP FILE +! +RUN $UTILTY +UTILTY V06C-03 RSTS V06C-03 Vixen +#CCL TKB=$TKB.TSK +#CCL MAC=$MAC.TSK +#CCL LBR=$LBR.TSK +#CCL PAT=$PAT.TSK +#CCL EDT=$EDT.TSK +#EXIT + +Ready + + +! +! EXTRACT ODT.OBJ FROM SYSLIB +! +LBR SYSTEM:[1,1]ODT=SYSTEM:[1,1]SYSLIB/EX:ODTRSX + +Ready + + +! +! FORMAT THE EDITOR'S DIAGNOSTIC TEXT FILE +! IF THE FILE DOESN'T EXIST ALREADY, EXPECT AN ERROR MESSAGE +! +RUN SYSTEM:@EDT +EDT>SYSTEM:@EDTCOM.MSG=SY:EDTCOM.TXT +DIAGNOSTIC FILE NOT PRESENT OR INCORRECTLY DEFINED +*EX/DI +162 LINES OUTPUT + +Ready + + +! +! CREATE A PIP.BAC FOR INSTALLATION PROCEDURES DEPENDING ON IT +! +RUN $PIP.SAV +*SYSTEM:@.BAC/PR=$PIP.SAV +*^Z + + +Ready + + +! +! END OF RSX BUILD PROCEDURE +! +DEASSIGN + +Ready + +^C + +Ready + +HELLO + +RSTS V06C-03 Vixen Job 2 [1,2] KB0 13-Mar-76 06:47 AM +Job 1 is detached under this account +Job number to attach to? 1 +Attaching to job 1 + +BUILD Complete + +Ready + +RUN MT1:BUILD +BUILD V06C-03 RSTS V06C-03 Vixen +System Build <No>? +Source Input Device <SY:>? MT1: +Library Output Device <SY:>? +Library Account <[1,2]>? +Control File is? MT1:RMSBLD.CTL + *** Copying file MT1:RMSBLD.CTL to SY:BUILD.TMP +BUILD Detaching... + + + + +^C +HELLO + +RSTS V06C-03 Vixen Job 2 KB0 13-Mar-76 06:48 AM +#1/2 +Password: +Job 1 is detached under this account +Job number to attach to? +1 other user is logged in under this account + + + + + +Ready + +ASSIGN SY:SYSDSK + +Ready + +ASSIGN SY:SYSTEM + +Ready + +ASSIGN [1,2] + +Ready + +ASSIGN MT1:INPUT + +Ready + +! +! BUILD PROCEDURE FOR RMS RUN-TIME SYSTEM AND UTILITIES +! + +! +! MAKE SURE RT11 RUN-TIME SYSTEM IS ADDED +! +RUN $UTILTY +UTILTY V06C-03 RSTS V06C-03 Vixen +#ADD RT11 +?Name or account now exists - in ADD +#EXIT + +Ready + + +! +! COPY RUN-TIME SYSTEM FROM DISTRIBUTION KIT +! +RUN $PIP.SAV +*SYSTEM:[0,1]=INPUT:$RMS11.RTS/BL +*SYSTEM:[0,1]/MODE:16.=SYSTEM:[0,1]RMS11.RTS +*^Z + + +Ready + + +! +! ADD THE RUN-TIME SYSTEM +! +RUN $UTILTY +UTILTY V06C-03 RSTS V06C-03 Vixen +#ADD SYSTEM:RMS11 +#EXIT + +Ready + + +! +! COPY THE RMS COMPONENTS TO THE TARGET SYSTEM +! +ASSIGN <40> + +Ready + +RUN $PIP.SAV +*SYSTEM:[1,1]=INPUT:$RMS11.ODL +*SYSTEM:[1,1]=INPUT:$RMS11.TSK/BL +*SYSTEM:[1,1]=INPUT:$RMS11.STB +*SYSTEM:[1,1]=INPUT:$RMSMAC.MLB/BL +*SYSTEM:[1,1]=INPUT:$RMSLIB.OLB/BL +*^Z + + +Ready + + +! +! COPY THE RMS UTILITIES TO THE TARGET LIBRARY +! +RUN $PIP.SAV +*SYSTEM:@<104>=INPUT:$RMSBCK.TSK/BL +*SYSTEM:@<232>=INPUT:$RMSRST.TSK/BL +*SYSTEM:@<104>=INPUT:$RMSDFN.TSK/BL +*SYSTEM:@<104>=INPUT:$RMSDEF.TSK/BL +*SYSTEM:@<104>=INPUT:$RMSDSP.TSK/BL +*SYSTEM:@<104>=INPUT:$RMSCNV.TSK/BL +*^Z + + +Ready + + +! +! ENSURE CORRECT RTS NAME IS ASSOCIATED WITH .TSK FILES +! +RUN $UTILTY +UTILTY V06C-03 RSTS V06C-03 Vixen +#NAME RSX=SYSTEM:@RMSBCK.TSK +#NAME RSX=SYSTEM:@RMSRST.TSK +#NAME RSX=SYSTEM:@RMSDFN.TSK +#NAME RSX=SYSTEM:@RMSDEF.TSK +#NAME RSX=SYSTEM:@RMSDSP.TSK +#NAME RSX=SYSTEM:@RMSCNV.TSK +#EXIT + +Ready + + +! +! ADD THE FOLLOWING UTILTY COMMANDS TO SYSTEM START-UP FILE +! +RUN $UTILTY +UTILTY V06C-03 RSTS V06C-03 Vixen +#CCL BCK=$RMSBCK.TSK +#CCL RST=$RMSRST.TSK;PRIV +#CCL DFN=$RMSDFN.TSK +#CCL DEF=$RMSDEF.TSK +#CCL DSP=$RMSDSP.TSK +#CCL CNV=$RMSCNV.TSK +#EXIT + +Ready + + +! +! END OF RMS BUILD PROCEDURE +! +DEASSIGN + +Ready + +^C + +Ready + +HELLO + +RSTS V06C-03 Vixen Job 2 [1,2] KB0 13-Mar-76 06:49 AM +Job 1 is detached under this account +Job number to attach to? 1 +Attaching to job 1 + +BUILD Complete + +Ready + +RUN MT1:BUILD +BUILD V06C-03 RSTS V06C-03 Vixen +System Build <No>? +Source Input Device <SY:>? MT1: +Library Output Device <SY:>? +Library Account <[1,2]>? +Control File is? MT1:$TECBLD.CTL + *** Copying file MT1:$TECBLD.CTL to SY:BUILD.TMP +BUILD Detaching... + + + + +^C +HELLO + +RSTS V06C-03 Vixen Job 2 KB0 13-Mar-76 06:50 AM +#1/2 +Password: +Job 1 is detached under this account +Job number to attach to? +1 other user is logged in under this account + + + + + +Ready + +ASSIGN SY:SYSDSK + +Ready + +ASSIGN SY:SYSTEM + +Ready + +ASSIGN [1,2] + +Ready + +ASSIGN MT1:INPUT + +Ready + +! +! BUILD PROCEDURE FOR TECO +! + +! +! MAKE SURE RT11 RUN-TIME SYSTEM IS ADDED +! +RUN $UTILTY +UTILTY V06C-03 RSTS V06C-03 Vixen +#ADD RT11 +?Name or account now exists - in ADD +#EXIT + +Ready + + +! +! COPY ALL TECO COMPONENTS FROM DISTRIBUTION KIT +! +RUN $PIP.SAV +*SYSTEM:[0,1]<60>=INPUT:$TECO.RTS +*SYSTEM:[0,1]<60>/MODE:16.=SYSTEM:[0,1]TECO.RTS +*SYSTEM:@<232>=INPUT:$TECO.TEC +*SYSTEM:@<232>=INPUT:$VT52.TEC +*SYSTEM:@<104>=INPUT:$TYPE.TEC +*SYSTEM:@< 40>=INPUT:$TECO.DOC +*^Z + + +Ready + + +! +! SET RTS NAME TO TECO FOR THE .TEC FILES +! +RUN $UTILTY +UTILTY V06C-03 RSTS V06C-03 Vixen +#NAME TECO=SYSTEM:@TECO.TEC +#NAME TECO=SYSTEM:@VT52.TEC +#NAME TECO=SYSTEM:@TYPE.TEC +#EXIT + +Ready + + +! +! ADD THE FOLLOWING UTILTY COMMANDS TO SYSTEM START-UP FILE +! +RUN $UTILTY +UTILTY V06C-03 RSTS V06C-03 Vixen +#ADD TECO +#CCL MAK-E=$TECO.TEC;0 +#CCL MU-NG=$TECO.TEC;0 +#CCL TE-CO=$TECO.TEC;0 +#CCL TY-PE=$TYPE.TEC;8 +#EXIT + +Ready + + +! +! END OF BUILD PROCEDURE FOR TECO +! +DEASSIGN + +Ready + +^C + +Ready + +HELLO + +RSTS V06C-03 Vixen Job 2 [1,2] KB0 13-Mar-76 06:51 AM +Job 1 is detached under this account +Job number to attach to? 1 +Attaching to job 1 + +BUILD Complete + +Ready + +RUN $PIP.SAV +*$*.*<40>=MT1:$MACRO.SAV,$CREF.SAV,$LINK.SAV,$LIBR.SAV +*^Z + +Ready + +RUN TTYSET +TTYSET V06C-03 RSTS V06C-03 Vixen +Terminal characteristics program +? LC INPUT +? ^Z + +Ready + +run pip +*notice.txt=tt: + +Welcome to RSTS V06C-03 time sharing + +This system was installed from a tape with missing blocks!! +Errors which caused the installation and build to fail have been +manually patched or worked around. The result is a system which +runs but should not be relied upon too much. + +I don't have any optional software tapes so this is a pretty basic +system (pun intended) + +Rather interesting to look back at how things were done in the +good old days. + +Paul Nankervis +paulnank@hotmail.com +http://skn.noip.me/pdp11/pdp11.html + +^Z +*^Z + +Ready + +run react +REACT V06C-03 RSTS V06C-03 Vixen +System Account Manager +Function? s +Account [1,1] being bypassed +Account [1,2] on System Disk being bypassed +All Accounts in Account File are now Entered +Function? e +Proj,Prog? 11,70 +Disk:Password? sy:pdp +Quota? 500 +Cluster Size? 2 +Account Name? PDP 11/70 Emulator +Proj,Prog? ^Z + +Ready + +run $shutup +SHUTUP V06C-03 RSTS V06C-03 Vixen + +######## Set-up Dialogue Phase ######## + +Type 'ESC'('ALT') to any query to backup one (1) step + +'OPSER' not running + +Minutes until system shutdown (0-99) <5>? 0 + +######## Warning Message Phase ######## +Further LOGINs are now disabled + +######## Initial Job Killing Phase ######## + +######## Unload/Remove RTS Phase ######## + +######## SWAP File Removal Phase ######## + +######## Disk DISMOUNT Phase ######## + +######## Final Shutdown Phase ######## + +Please wait for system to re-boot itself + + + + + + + + + + + + + +RSTS V06C-03 Vixen (DK2) + + +Option: ++ + + + diff --git a/RSTSv06c.jpg b/RSTSv06c.jpg new file mode 100755 index 0000000..815a087 Binary files /dev/null and b/RSTSv06c.jpg differ diff --git a/bootcode.js b/bootcode.js new file mode 100755 index 0000000..bf975de --- /dev/null +++ b/bootcode.js @@ -0,0 +1,143 @@ +// Javascript PDP 11/70 Emulator v1.8 +// written by Paul Nankervis +// Please send suggestions, fixes and feedback to paulnank@hotmail.com +// I'm particularly interested in hearing from anyone with real experience on a PDP 11/70 front panel +// +// This code may be used freely provided the original author name is maintained in any modified source code +// +// http://skn.noip.me/pdp11/pdp11.html +// +// +var BOOTBASE=0140000; +var bootcode=[ +0000005,0005037,0177776,0005037,0177772,0005037,0177564,0005037, +0177546,0005067,0000122,0010700,0062700,0177750,0010006,0010700, +0062700,0001227,0004767,0000144,0010700,0062700,0000104,0010037, +0000100,0012737,0000340,0000102,0052737,0000100,0177546,0010700, +0062700,0001241,0004767,0000104,0162706,0000256,0010600,0004767, +0000172,0005000,0000001,0005200,0005767,0000014,0001773,0012746, +0054000,0016746,0000002,0000006,0000000,0000000,0000000,0005267, +0177770,0005367,0177766,0016737,0177762,0177570,0000006,0105737, +0177564,0100375,0110037,0177566,0000207,0000000,0132737,0000100, +0177564,0001374,0010067,0177762,0010700,0062700,0000026,0010037, +0000064,0012737,0000200,0000066,0152737,0000100,0177564,0000207, +0105777,0177726,0001406,0117737,0177720,0177566,0005267,0177712, +0000006,0105037,0177564,0000006,0000000,0000000,0010067,0177770, +0005067,0177766,0010700,0062700,0000026,0010037,0000060,0012737, +0000200,0000062,0152737,0000100,0177560,0000207,0010046,0113700, +0177562,0120027,0000015,0001456,0120027,0000177,0001403,0120027, +0000010,0001013,0005767,0177702,0001407,0005367,0177674,0010700, +0062700,0000534,0004767,0177564,0000455,0120027,0000040,0002452, +0120027,0000177,0002047,0004767,0177524,0120027,0000172,0003005, +0120027,0000141,0002402,0142700,0000040,0026727,0177614,0000254, +0002031,0110046,0016700,0177600,0066700,0177576,0112610,0005267, +0177570,0000420,0016700,0177560,0066700,0177556,0105010,0010700, +0062700,0000030,0010037,0000240,0012737,0000100,0000242,0012737, +0002000,0177772,0012600,0000006,0005037,0177772,0010046,0010146, +0010246,0010346,0010446,0010700,0062700,0000341,0004767,0177374, +0132737,0000100,0177564,0001374,0005004,0012703,0141160,0112300, +0001435,0016702,0177442,0112201,0001436,0120127,0000040,0001773, +0120001,0001010,0112300,0001412,0112201,0001410,0120127,0000040, +0001367,0000404,0112300,0001376,0005204,0000751,0006304,0010700, +0062700,0000060,0060004,0061404,0004714,0000405,0010700,0062700, +0000307,0004767,0177246,0005067,0177340,0010700,0062700,0000365, +0004767,0177230,0012604,0012603,0012602,0012601,0012600,0000006, +0002216,0000444,0001454,0000454,0000556,0000660,0000464,0000474, +0067503,0066555,0067141,0071544,0060440,0062562,0041040,0067557, +0026164,0044040,0066141,0026164,0052040,0071545,0026164,0042040, +0060551,0026147,0046040,0063551,0072150,0026163,0060440,0062156, +0044040,0066145,0006560,0041012,0067557,0020164,0062544,0064566, +0062543,0020163,0071141,0020145,0045522,0051040,0020114,0050122, +0067440,0020162,0046524,0005015,0006400,0000012,0020010,0000010, +0047502,0052117,0044000,0046105,0000120,0044514,0044107,0051524, +0044000,0046101,0000124,0042524,0052123,0042000,0040511,0000107, +0044103,0051501,0051105,0051000,0046105,0041517,0052101,0000105, +0042117,0000124,0052400,0065556,0067556,0067167,0061440,0066557, +0060555,0062156,0006412,0050000,0072541,0020154,0060516,0065556, +0071145,0064566,0020163,0020055,0060560,0066165,0060556,0065556, +0064100,0072157,0060555,0066151,0061456,0066557,0006412,0006412, +0041000,0047517,0037124,0000040,0067125,0067153,0073557,0020156, +0067542,0072157,0062040,0073145,0061551,0005145,0000015,0020040, +0020040,0020040,0066143,0061557,0020153,0064564,0065543,0005163, +0000015,0020040,0020040,0020040,0072151,0071145,0072141,0067551, +0071556,0006412,0000000,0010700,0062700,0177350,0004767,0176534, +0000207,0000000,0010700,0062700,0177463,0004767,0176516,0000207, +0012700,0000001,0006100,0000005,0000775,0000237,0013703,0177570, +0042703,0000001,0020327,0004000,0103402,0012703,0004000,0010302, +0010700,0062700,0176236,0010001,0010700,0062700,0002250,0012122, +0020100,0103775,0000113,0005067,0176360,0012705,0015000,0010504, +0006204,0005000,0010501,0071004,0010002,0070204,0060301,0020105, +0001401,0000000,0077412,0077515,0010700,0062700,0177551,0016703, +0176310,0005002,0071227,0000010,0062703,0000060,0110340,0010203, +0001370,0004767,0176326,0000207,0000000,0005067,0177772,0005067, +0176250,0005005,0100404,0102403,0101002,0002401,0101401,0000000, +0005305,0100003,0001402,0002001,0003401,0000000,0006005,0102002, +0103001,0001001,0000000,0112737,0000017,0177776,0100004,0102003, +0002402,0101001,0100401,0000000,0012703,0077777,0062703,0077777, +0123727,0177776,0000005,0001001,0000000,0112704,0001700,0100003, +0020427,0177700,0001401,0000000,0105004,0020427,0177400,0001401, +0000000,0012705,0125252,0010500,0010001,0010102,0010203,0010304, +0010405,0160501,0002401,0001401,0000000,0006102,0103001,0002401, +0000000,0060203,0005203,0005103,0060301,0103401,0003401,0000000, +0006004,0050403,0060503,0005203,0103402,0005301,0002401,0000000, +0005100,0101401,0000000,0040001,0060101,0003001,0003401,0000000, +0000301,0020127,0052125,0001004,0030405,0003002,0005105,0001001, +0000000,0112700,0177401,0100001,0000000,0077002,0005001,0005201, +0077002,0005700,0001002,0005701,0001401,0000000,0010700,0062700, +0000010,0010007,0000000,0005727,0000000,0010700,0062700,0000012, +0010046,0000207,0000000,0010700,0062700,0000014,0005046,0010046, +0000002,0000000,0000167,0000002,0000000,0012705,0000003,0010700, +0062700,0000020,0005037,0000006,0010037,0000004,0005745,0000000, +0062706,0000004,0005000,0012701,0007777,0010002,0062702,0000005, +0022062,0177773,0001402,0000000,0000000,0077111,0005267,0177270, +0103001,0000000,0026727,0175542,0002200,0101002,0000167,0177262, +0010700,0062700,0177005,0016703,0177236,0005002,0071227,0000010, +0062703,0000060,0110340,0010203,0001370,0004767,0175536,0000207, +0005767,0175464,0001403,0005067,0175456,0000207,0000237,0005037, +0177572,0012704,0077406,0012703,0172300,0005001,0005002,0004767, +0000200,0012737,0177600,0172376,0012703,0177600,0005001,0012702, +0002000,0004767,0000154,0012737,0177600,0177676,0012700,0000020, +0012703,0172200,0010423,0077002,0012700,0141400,0012701,0000067, +0032737,0000010,0177570,0001404,0012700,0006000,0012701,0000007, +0010037,0172244,0010037,0172264,0012737,0177600,0172276,0012737, +0010000,0177776,0010137,0172516,0012737,0000001,0177572,0012703, +0040200,0010700,0062700,0000074,0010002,0010700,0062700,0000322, +0012246,0006623,0020200,0103774,0012767,0040200,0175232,0000230, +0000207,0012700,0000010,0010263,0000060,0010163,0000040,0010463, +0000020,0010423,0062701,0000200,0062702,0000200,0077014,0000207, +0012700,0000037,0012701,0174000,0032737,0000001,0177570,0001414, +0012700,0007417,0010001,0005101,0032737,0000002,0177570,0001404, +0012700,0036163,0012701,0037000,0010102,0162702,0000002,0042702, +0000001,0010203,0042703,0177701,0012763,0000001,0040000,0012763, +0000113,0040002,0010204,0072427,0177772,0042704,0177600,0013703, +0172244,0160403,0010204,0072427,0177764,0042704,0177761,0010364, +0172240,0010105,0072527,0177764,0042705,0177761,0020504,0001402, +0010365,0172240,0012704,0000003,0010703,0062703,0000006,0000112, +0077402,0032737,0000004,0177570,0001005,0010002,0006002,0006101, +0006000,0000712,0010002,0006102,0006001,0006100,0000705,0005003, +0112201,0001560,0120127,0000040,0001773,0120127,0000122,0001415, +0120127,0000124,0001004,0112201,0120127,0000115,0001407,0010700, +0062700,0176070,0004767,0174724,0000207,0112201,0112200,0001416, +0120027,0000040,0001413,0120027,0000067,0003033,0162700,0000060, +0002430,0006303,0006303,0006303,0050003,0000760,0010700,0062700, +0174442,0010037,0000004,0005037,0000006,0120127,0000113,0001502, +0120127,0000114,0001414,0120127,0000120,0001523,0120127,0000115, +0001555,0010700,0062700,0175641,0004767,0174600,0000207,0000005, +0000303,0012701,0174400,0012761,0000013,0000004,0052703,0000004, +0010311,0105711,0100376,0105003,0052703,0000010,0010311,0105711, +0100376,0016102,0000006,0042702,0000077,0005202,0010261,0000004, +0105003,0052703,0000006,0010311,0105711,0100376,0005061,0000002, +0005061,0000004,0012761,0177000,0000006,0105003,0052703,0000014, +0010311,0105711,0100376,0042711,0000377,0005002,0005003,0005004, +0005005,0005007,0000005,0000303,0006303,0006303,0006303,0006303, +0006303,0012701,0177412,0010311,0005041,0012741,0177000,0012741, +0000005,0005002,0005003,0005004,0005005,0105711,0100376,0105011, +0005007,0000005,0012701,0176700,0012761,0000040,0000010,0010361, +0000010,0012711,0000021,0012761,0010000,0000032,0012761,0177000, +0000002,0005061,0000004,0005061,0000006,0005061,0000034,0012711, +0000071,0105711,0100376,0105011,0010300,0005007,0000005,0010300, +0012701,0172526,0005011,0012741,0177777,0010002,0000302,0062702, +0060011,0010241,0105711,0100376,0010002,0000302,0062702,0060003, +0010211,0105711,0100376,0005002,0005003,0012704,0143744,0005005, +0005007 +]; \ No newline at end of file diff --git a/iopage.js b/iopage.js new file mode 100755 index 0000000..fbfacd5 --- /dev/null +++ b/iopage.js @@ -0,0 +1,1489 @@ +// Javascript PDP 11/70 Emulator v1.8 +// written by Paul Nankervis +// Please send suggestions, fixes and feedback to paulnank@hotmail.com +// I'm particularly interested in hearing from anyone with real experience on a PDP 11/70 front panel +// +// This code may be used freely provided the original author name is acknowledged in any modified source code +// +// http://skn.noip.me/pdp11/pdp11.html +// +// Disk routines need a clean up - specifically the RP11 stuff needs rewriting :-( +// +// Map an 18 bit unibus address to a 22 bit memory address via the unibus map (if active) +// +// +function mapUnibus(unibusAddress) { + var idx = (unibusAddress >> 13) & 0x1f; + if (idx < 31) { + if (CPU.MMR3 & 0x20) { + unibusAddress = (CPU.unibusMap[idx] + (unibusAddress & 0x1fff)) & 0x3fffff; + } + } else { + unibusAddress |= IOBASE_22BIT; // top page always maps to unibus i/o page - apparently. + } + return unibusAddress; +} + +// =========== Disk I/O support routines =========== + +// getData() is called at the completion of an XMLHttpRequest request to GET disk data. +// It extracts the received data and stores it in the appropriate disk cache. + +function getData(xhr, operation, meta, position, address, count) { + var arrayBuffer, byteArray, block, word, base; + arrayBuffer = xhr.response; + if ((xhr.status != 0 && xhr.status != 206) || !arrayBuffer) { + meta.postProcess(1, meta, position, address, count); // NXD - read error? + } else { + byteArray = new Uint8Array(arrayBuffer); + block = ~~(position / meta.blockSize); + for (base = 0; base < byteArray.length;) { + if (typeof meta.cache[block] !== "undefined") { + base += meta.blockSize << 1; + } else { + meta.cache[block] = []; + for (word = 0; word < meta.blockSize; word++) { + if (base < byteArray.length) { + meta.cache[block][word] = (byteArray[base] & 0xff) | ((byteArray[base + 1] << 8) & 0xff00); + } else { + meta.cache[block][word] = 0; + } + base += 2; + } + } + block++; + } + diskIO(operation, meta, position, address, count); + } +} + +// diskIO() moves data between memory and the disk cache. If cache blocks are undefined then +// an XMLHttpRequest request is kicked off to get the appropriate disk data from the server. +// Operations supported are: 1: Write, 2: Read, 3: Check (corresponds with RK function codes :-) ) +// position is in words and count in bytes (an allowance for tape which can do byte IO) + +function diskIO(operation, meta, position, address, count) { + var block, word, data, xhr; + block = ~~(position / meta.blockSize); + if (typeof meta.cache[block] !== "undefined") { + word = position % meta.blockSize; + while (count > 0) { + switch (operation) { + case 1: // Write: write from memory to cache + case 3: // Check: compare memory with disk cache + data = readWordByAddr((meta.mapped ? mapUnibus(address) : address)); + if (data < 0) { + meta.postProcess(2, meta, block * meta.blockSize + word, address, count); // NXM + return; + } + if (operation == 1) { // write: put data into disk cache + meta.cache[block][word] = data; + } else { // check: compare memory with disk cache + if (meta.cache[block][word] != data) { + meta.postProcess(3, meta, block * meta.blockSize + word, address, count); // mismatch + return; + } + } + //if (meta.increment) { + address += 2; + //} + count -= 2; // bytes to go.... (currently all write operations are whole words) + break; + case 2: // Read: read to memory from cache + if (count > 1) { // tape can read odd number of bytes - of course it can. :-( + if (writeWordByAddr((meta.mapped ? mapUnibus(address) : address), meta.cache[block][word]) < 0) { + meta.postProcess(2, meta, block * meta.blockSize + word, address, count); // NXM + return; + } + //if (meta.increment) { + address += 2; + //} + count -= 2; // bytes to go.... + } else { + if (writeByteByAddr((meta.mapped ? mapUnibus(address) : address), data) < 0) { + meta.postProcess(2, meta, block * meta.blockSize + word, address, count); // NXM + return; + } + //if (meta.increment) { + address += 1; + //} + --count; // bytes to go.... + } + break; + case 4: // accumulate a record count into the address field for tape operations + address = (meta.cache[block][word] << 16) | (address >> 16); + count -= 2; // bytes to go.... + break; + default: + panic(); // invalid operation - how did we get here? + } + if (++word >= meta.blockSize) { + word = 0; + block++; + if (typeof meta.cache[block] === "undefined") break; + } + } + position = block * meta.blockSize + word; + } + if (count > 0) { // I/O not complete so we need to get some data + word = (~~(position / meta.blockSize)) * meta.blockSize; // Start word + data = (~~((position + (count >> 1) + meta.blockSize - 1) / meta.blockSize)) * meta.blockSize; // End word + xhr = new XMLHttpRequest(); + xhr.open("GET", meta.url, true); + xhr.setRequestHeader("Range", "bytes=" + (word << 1) + "-" + ((data << 1) - 1)); + xhr.responseType = "arraybuffer"; + xhr.onreadystatechange = function() { + if (xhr.readyState == xhr.DONE) { + getData(xhr, operation, meta, position, address, count); + } + }; + xhr.send(null); + return; + } + meta.postProcess(0, meta, position, address, count); // success +} + + + +// =========== RK11 routines =========== + +var rk11 = { + rkds: 04700, // 017777400 Drive Status + rker: 0, // 017777402 Error Register + rkcs: 0200, // 017777404 Control Status + rkwc: 0, // 017777406 Word Count + rkba: 0, // 017777410 Bus Address + rkda: 0, // 017777412 Disk Address + meta: [], + TRACKS: [406, 406, 406, 406, 406, 406, 406, 0], + SECTORS: [12, 12, 12, 12, 12, 12, 12, 12] +}; + +function rk11_seekEnd(drive) { + rk11.rkds = (drive << 13) | (rk11.rkds & 0x1ff0); + rk11.rkcs |= 0x2000; + return rk11.rkcs & 0x40; +} + +function rk11_commandEnd(drive) { + rk11.rkds = (drive << 13) | (rk11.rkds & 0x1ff0); + rk11.rkcs = (rk11.rkcs & 0xfffe) | 0x80; // turn off go & set done + return rk11.rkcs & 0x40; +} + +function rk11_finish(drive) { + if (rk11.rkcs & 0x40) { + interrupt(0, 10, 5 << 5, 0220, rk11_commandEnd, drive); + } else { // if interrupt not enabled just mark completed + rk11_commandEnd(drive); + } +} + +function rk11_init() { + rk11.rkds = 04700; // Set bits 6, 7, 8, 11 + rk11.rker = 0; // + rk11.rkcs = 0200; + rk11.rkwc = 0; + rk11.rkba = 0; + rk11.rkda = 0; +} + +function rk11_go() { + var sector, address, count; + var drive = (rk11.rkda >> 13) & 7; + if (typeof rk11.meta[drive] === "undefined") { + rk11.meta[drive] = { + "cache": [], + "blockSize": 65536, + "postProcess": rk11_end, + "drive": drive, + "mapped": 1, + "maxblock": rk11.TRACKS[drive] * rk11.SECTORS[drive], + "url": "rk" + drive + ".dsk" + }; + } + rk11.rkcs &= ~0x2080; // turn off done bit & search complete + rk11.rker &= ~0x03; // turn off soft errors + if (rk11.TRACKS[drive] == 0) { + rk11.rker |= 0x8080; // NXD + } else { + sector = (((rk11.rkda >> 4) & 0x1ff) * rk11.SECTORS[drive] + (rk11.rkda & 0xf)); + address = (((rk11.rkcs & 0x30)) << 12) | rk11.rkba; + count = (0x10000 - rk11.rkwc) & 0xffff; + switch ((rk11.rkcs >> 1) & 7) { // function code + case 0: // controller reset + interrupt(1, -1, 5 << 5, 0220); + rk11_init(); + break; + case 1: // write + case 2: // read + case 3: // check + if (((rk11.rkda >> 4) & 0x1ff) >= rk11.TRACKS[drive]) { + rk11.rker |= 0x8040; // NXC + break; + } + if ((rk11.rkda & 0xf) >= rk11.SECTORS[drive]) { + rk11.rker |= 0x8020; // NXS + break; + } + sector = (((rk11.rkda >> 4) & 0x1ff) * rk11.SECTORS[drive] + (rk11.rkda & 0xf)); + address = (((rk11.rkcs & 0x30)) << 12) | rk11.rkba; + count = (0x10000 - rk11.rkwc) & 0xffff; + diskIO((rk11.rkcs >> 1) & 7, rk11.meta[drive], sector * 256, address, count << 1); + return; + case 4: // Seek - complete immediately + rk11.rkcs |= 0x2000; // Set search complete + interrupt(0, 20, 5 << 5, 0220, rk11_seekEnd, drive); + break; + case 5: // Read Check + break; + case 6: // Drive Reset + rk11.rkds = 04700 | (drive << 13); + rk11.rker = 0; // + rk11.rkcs = 0200; + rk11.rkda &= 0xe000; // keep drive number + interrupt(0, 20, 5 << 5, 0220, rk11_seekEnd, drive); + //rk11.rkcs |= 0x2000; // Set search complete + break; + case 7: // Write Lock - not implemented :-( + break; + default: + break; + } + } + rk11_finish(drive); +} + + +function rk11_end(err, meta, position, address, count) { + rk11.rkba = address & 0xffff; + rk11.rkcs = (rk11.rkcs & ~0x30) | ((address >> 12) & 0x30); + rk11.rkwc = (0x10000 - (count >> 1)) & 0xffff; + position = ~~(position / 256); + rk11.rkda = (rk11.rkda & 0xe000) | ((~~(position / rk11.SECTORS[meta.drive])) << 4) | (position % rk11.SECTORS[meta.drive]); + switch (err) { + case 1: // read error + rk11.rker |= 0x8100; // Report TE (Timing error) + break; + case 2: // NXM + rk11.rker |= 0x8400; // NXM + break; + case 3: // compare error + rk11.rker |= 0x8001; // Report TE (Write check error) + break; + } + rk11_finish(meta.drive); +} + + +function accessRK11(physicalAddress, data, byteFlag) { + var result; + switch (physicalAddress & ~1) { + case 017777400: // rk11.rkds + result = rk11.rkds; + break; + case 017777402: // rk11.rker + result = rk11.rker; + break; + case 017777404: // rk11.rkcs + rk11.rkcs &= 0x3fff; + if (rk11.rker & 0x7fff) rk11.rkcs |= 0x8000; + if (rk11.rker & 0x7fc0) rk11.rkcs |= 0x4000; + result = insertData(rk11.rkcs, physicalAddress, data, byteFlag); + if (data >= 0 && result >= 0) { + if ((rk11.rkcs ^ result) & 0x40) { // Has IE bit changed? + if (result & 0x40) { + if (!(result & 1)) { + interrupt(1, 0, 5 << 5, 0220); + } + } else { + rk11.rkcs = (rk11.rkcs & 0xfffe) | 0x80; // turn off go & set done + interrupt(1, -1, 5 << 5, 0220); + } + } + rk11.rkcs = (result & ~0xf080) | (rk11.rkcs & 0xf080); // Bits 7 and 12 - 15 are read only + if (rk11.rkcs & 1) { + rk11.rkcs &= ~0x2080; // turn off done bit & search complete + rk11.rker &= ~0x03; // turn off soft errors + rk11_go(); + //setTimeout(rk11_go, 10); + } + } + break; + case 017777406: // rk11.rkwc + result = insertData(rk11.rkwc, physicalAddress, data, byteFlag); + if (result >= 0) rk11.rkwc = result; + break; + case 017777410: // rk11.rkba + result = insertData(rk11.rkba, physicalAddress, data, byteFlag); + if (result >= 0) rk11.rkba = result; + break; + case 017777412: // rk11.rkda + result = insertData(rk11.rkda, physicalAddress, data, byteFlag); + if (result >= 0) rk11.rkda = result; + break; + case 017777414: // rk11.unused + case 017777416: // rk11.rkdb + result = 0; + break; + default: + CPU.CPU_Error |= 0x10; + return trap(4, 134); + } + //console.log("RK11 Access "+physicalAddress.toString(8)+" "+data.toString(8)+" "+byteFlag.toString(8)+" -> "+result.toString(8)); + return result; +} + + +// =========== RL11 routines =========== + +var rl11 = { + csr: 0x81, // 017774400 Control status register + bar: 0, // 017774402 Bus address + dar: 0, // 017774404 Disk address + mpr: 0, // 017774406 Multi purpose + DAR: 0, // internal disk address + meta: [], // sector cache + SECTORS: [40, 40, 40, 40], // sectors per track + TRACKS: [1024, 1024, 512, 512], // First two drives RL02 - last two RL01 - cylinders * 2 + STATUS: [0235, 0235, 035, 035] // First two drives RL02 - last two RL01 +}; + +function rl11_commandEnd() { + rl11.csr |= 0x81; // turn off go & set ready + return rl11.csr & 0x40; +} + +function rl11_finish(drive) { + if (rl11.csr & 0x40) { + interrupt(0, 10, 5 << 5, 0160, rl11_commandEnd); + } else { // if interrupt not enabled just mark completed + rl11_commandEnd(); + } +} + +function rl11_go() { + var sector, address, count; + var drive = (rl11.csr >> 8) & 3; + rl11.csr &= ~0x1; // ready bit (0!) + if (typeof rl11.meta[drive] === "undefined") { + rl11.meta[drive] = { + "cache": [], + "blockSize": 65536, + "postProcess": rl11_end, + "drive": drive, + "mapped": 1, + "maxblock": rl11.TRACKS[drive] * rl11.SECTORS[drive], + "url": "rl" + drive + ".dsk" + }; + } + switch ((rl11.csr >> 1) & 7) { // function code + case 0: // no op + break; + case 1: // write check + break; + case 2: // get status + if (rl11.mpr & 8) rl11.csr &= 0x3f; + rl11.mpr = rl11.STATUS[drive] | (rl11.DAR & 0100); // bit 6 Head Select bit 7 Drive Type 1=rl02 + break; + case 3: // seek + if ((rl11.dar & 3) == 1) { + if (rl11.dar & 4) { + rl11.DAR = ((rl11.DAR + (rl11.dar & 0xff80)) & 0xff80) | ((rl11.dar << 2) & 0x40); + } else { + rl11.DAR = ((rl11.DAR - (rl11.dar & 0xff80)) & 0xff80) | ((rl11.dar << 2) & 0x40); + } + rl11.dar = rl11.DAR; + } + break; + case 4: // read header + rl11.mpr = rl11.DAR; + break; + case 5: // write + if ((rl11.dar >> 6) >= rl11.TRACKS[drive]) { + rl11.csr |= 0x9400; // HNF + break; + } + if ((rl11.dar & 0x3f) >= rl11.SECTORS[drive]) { + rl11.csr |= 0x9400; // HNF + break; + } + sector = ((rl11.dar >> 6) * rl11.SECTORS[drive]) + (rl11.dar & 0x3f); + address = rl11.bar | ((rl11.csr & 0x30) << 12); + count = (0x10000 - rl11.mpr) & 0xffff; + diskIO(1, rl11.meta[drive], sector * 128, address, count << 1); + return; + break; + case 6: // read + case 7: // Read data without header check + if ((rl11.dar >> 6) >= rl11.TRACKS[drive]) { + rl11.csr |= 0x9400; // HNF + break; + } + if ((rl11.dar & 0x3f) >= rl11.SECTORS[drive]) { + rl11.csr |= 0x9400; // HNF + break; + } + sector = ((rl11.dar >> 6) * rl11.SECTORS[drive]) + (rl11.dar & 0x3f); + address = rl11.bar | ((rl11.csr & 0x30) << 12); + count = (0x10000 - rl11.mpr) & 0xffff; + diskIO(2, rl11.meta[drive], sector * 128, address, count << 1); + return; + break; + } + rl11_finish(); + //setTimeout(rl11_finish,0); +} + + +function rl11_end(err, meta, position, address, count) { + var sector = ~~(position / 128); + rl11.bar = address & 0xffff; + rl11.csr = (rl11.csr & ~0x30) | ((address >> 12) & 0x30); + rl11.dar = ((~~(sector / rl11.SECTORS[meta.drive])) << 6) | (sector % rl11.SECTORS[meta.drive]); + rl11.DAR = rl11.dar; + rl11.mpr = (0x10000 - (count >> 1)) & 0xffff; + switch (err) { + case 1: // read error + rl11.csr |= 0x8400; // Report operation incomplete + break; + case 2: // NXM + rl11.csr |= 0xa000; // NXM + break; + } + rl11_finish(); +} + +function accessRL11(physicalAddress, data, byteFlag) { + var result; + switch (physicalAddress & ~1) { + case 017774400: // rl11.csr + result = insertData(rl11.csr, physicalAddress, data, byteFlag); + if (data >= 0 && result >= 0) { + if ((rl11.csr & 0x40) && !(result & 0x40)) { // if IE being reset then kill any pending interrupts + //rl11.csr |= 0x81; // turn off go & set ready + interrupt(1, -1, 5 << 5, 0160); + } + if (!(result & 0x80)) { + rl11.csr = (rl11.csr & ~0x3fe) | (result & 0x3fe); + rl11_go(); + } else { + if ((result & 0x40) && !(rl11.csr & 0x40)) { + interrupt(1, 10, 5 << 5, 0160); + } + rl11.csr = (rl11.csr & ~0x3fe) | (result & 0x3fe); + } + } + break; + case 017774402: // rl11.bar + result = insertData(rl11.bar, physicalAddress, data, byteFlag); + if (result >= 0) { + rl11.bar = result & 0xfffe; + } + break; + case 017774404: // rl11.dar + result = insertData(rl11.dar, physicalAddress, data, byteFlag); + if (result >= 0) rl11.dar = result; + break; + case 017774406: // rl11.mpr + result = insertData(rl11.mpr, physicalAddress, data, byteFlag); + if (result >= 0) rl11.mpr = result; + break; + default: + CPU.CPU_Error |= 0x10; + return trap(4, 134); + } + return result; +} + + +// =========== RP11 routines =========== + +var rp11 = { + DTYPE: [020022, 020022, 020020, 020020, 020022, 020020, 020022, 020042], // Drive type rp06, rp06, rp04, rp04... + SECTORS: [22, 22, 22, 22, 22, 22, 22, 50], // sectors per track + SURFACES: [19, 19, 19, 19, 19, 19, 19, 32], // + CYLINDERS: [815, 815, 815, 815, 815, 411, 815, 630], + meta: [], //meta data for drive + rpcs1: 0x880, // Massbus 00 - actual register is a mix of controller and drive bits :-( + rpwc: 0, + rpba: 0, // rpba & rpbae + rpda: [0, 0, 0, 0, 0, 0, 0, 0], // Massbus 05 + rpcs2: 0, + rpds: [0x1180, 0x1180, 0x1180, 0x1180, 0, 0, 0, 0], // Massbus 01 Read only + rper1: [0, 0, 0, 0, 0, 0, 0, 0], // Massbus 02 + // rpas: 0, // Massbus 04??? + rpla: [0, 0, 0, 0, 0, 0, 0, 0], // Massbus 07 Read only + rpdb: 0, + rpmr: [0, 0, 0, 0, 0, 0, 0, 0], // Massbus 03 + rpdt: [0, 0, 0, 0, 0, 0, 0, 0], // Massbus 06 Read only + rpsn: [1, 2, 3, 4, 5, 6, 7, 8], // Massbus 10 Read only + rpof: [0, 0, 0, 0, 0, 0, 0, 0], // Massbus 11 + rpdc: [0, 0, 0, 0, 0, 0, 0, 0], // Massbus 12 + rpcc: [0, 0, 0, 0, 0, 0, 0, 0], // Massbus 13 Read only + rper2: [0, 0, 0, 0, 0, 0, 0, 0], // Massbus 14 + rper3: [0, 0, 0, 0, 0, 0, 0, 0], // Massbus 15 + rpec1: [0, 0, 0, 0, 0, 0, 0, 0], // Massbus 16 Read only + rpec2: [0, 0, 0, 0, 0, 0, 0, 0], // Massbus 17 Read only + rpcs3: 0 +}; + + +function rp11_init() { + rp11.rpcs1 = 0x880; + rp11.rpcs2 = 0; + rp11.rpds = [0x11c0, 0x11c0, 0x11c0, 0x11c0, 0, 0, 0, 0]; + rp11.rpda = [0, 0, 0, 0, 0, 0, 0, 0]; + rp11.rpdc = [0, 0, 0, 0, 0, 0, 0, 0]; + rp11.rper1 = [0, 0, 0, 0, 0, 0, 0, 0]; + rp11.rper3 = [0, 0, 0, 0, 0, 0, 0, 0]; + rp11.rpas = rp11.rpwc = rp11.rpcs3 = 0; + rp11.rpba = 0; +} + + +function rp11_attention(drive, flags) { + rp11.rpas |= 1 << drive; + rp11.rpds[drive] |= 0x8000; + if (flags) { + rp11.rper1[drive] |= flags; + rp11.rpds[drive] |= 0x4000; + } +} + + +//When a Data Transfer command is successfully initiated both RDY +//and DRY become negated. When a non-data transfer command is +//successfully initiated only DRY bit become negated. +//DVA should be set + +function rp11_go() { + var sector, count, drive = rp11.rpcs2 & 7; + rp11.rpds[drive] &= 0x7fff; // turn off ATA on go bit + if (typeof rp11.meta[drive] === "undefined") { + rp11.meta[drive] = { + "cache": [], + "blockSize": 65536 * 4, //256, // 32768, // 65536 * 4, + "postProcess": rp11_end, + "drive": drive, + "mapped": 0, + "maxblock": rp11.CYLINDERS[drive] * rp11.SURFACES[drive] * rp11.SECTORS[drive], + "url": "rp" + drive + ".dsk" + }; + } + switch (rp11.rpcs1 & 0x3f) { // function code + case 01: // NULL + return; + case 03: // unload + break; + case 05: // seek + break; + case 07: // recalibrate + break; + case 011: // init + rp11.rpds[drive] = 0x11c0; //| 0x8000; + rp11.rpcs1 &= ~0x703f; // Turn off error bits + rp11.rpda[drive] = 0; + rp11.rpdc[drive] = 0; + rp11.rpcs1 = 0x880; // ?? + return; + case 013: // release + return; + case 015: // offset + break; + case 017: // return to centreline + break; + case 021: // read in preset + // Read-in Preset - Sets the VV (volume valid) bit, clears the Desired Sector/Track Address register, clears the Desired Cylinder Address register, and clears the FMT, HCI, and ECI bits in the Offset register. Clearing the FMT bit causes the RP04 to be in IS-bit mode. + rp11.rpdc[drive] = rp11.rpda[drive] = 0; + rp11.rpds[drive] = 0x11c0; // |= 0x40; // set VV + rp11.rpof[drive] = 0; // Turn off FMT 0x1000 + return; + case 023: // pack ack + rp11.rpds[drive] |= 0x40; // set VV + return; + case 031: // search + break; + case 061: // write + if (rp11.rpdc[drive] >= rp11.CYLINDERS[drive] || (rp11.rpda[drive] >> 8) >= rp11.SURFACES[drive] || + (rp11.rpda[drive] & 0xff) >= rp11.SECTORS[drive]) { + rp11.rper1[drive] |= 0x400; // invalid sector address + rp11.rpcs1 |= 0xc000; // set SC & TRE + break; + } + rp11.rpcs1 &= ~0x7000; // Turn error bits + rp11.rpcs1 &= ~0x4080; // Turn TRE & ready off + rp11.rpcs2 &= ~0x800; // Turn off NEM (NXM) + rp11.rpds[drive] &= ~0x480; // Turn off LST & DRY + sector = (rp11.rpdc[drive] * rp11.SURFACES[drive] + (rp11.rpda[drive] >> 8)) * rp11.SECTORS[drive] + (rp11.rpda[drive] & 0xff); + diskIO(1, rp11.meta[drive], sector * 256, rp11.rpba, ((0x10000 - rp11.rpwc) & 0xffff) << 1); + return; + break; + case 071: // read + if (rp11.rpdc[drive] >= rp11.CYLINDERS[drive] || (rp11.rpda[drive] >> 8) >= rp11.SURFACES[drive] || + (rp11.rpda[drive] & 0xff) >= rp11.SECTORS[drive]) { + rp11.rper1[drive] |= 0x400; // invalid sector address + rp11.rpcs1 |= 0xc000; // set SC & TRE + break; + } + rp11.rpcs1 &= ~0x7000; // Turn error bits + rp11.rpcs1 &= ~0x4080; // Turn TRE & ready off + rp11.rpcs2 &= ~0x800; // Turn off NEM (NXM) + rp11.rpds[drive] &= ~0x480; // Turn off LST & DRY + sector = (rp11.rpdc[drive] * rp11.SURFACES[drive] + (rp11.rpda[drive] >> 8)) * rp11.SECTORS[drive] + (rp11.rpda[drive] & 0xff); + diskIO(2, rp11.meta[drive], sector * 256, rp11.rpba, ((0x10000 - rp11.rpwc) & 0xffff) << 1); + return; + break; + default: + panic(); + return; + break; + } + interrupt(1, 12, 5 << 5, 0254, function() { + rp11.rpds[drive] |= 0x8000; // ATA + rp11.rpcs1 |= 0x8000; // SC no + if (rp11.rpcs1 & 0x40) return true; + return false; + }); +} + + +function rp11_end(err, meta, position, address, count) { + var sector, block = ~~((position + 255) / 256); + rp11.rpwc = (0x10000 - (count >> 1)) & 0xffff; + rp11.rpba = address & 0x3fffff; + sector = ~~(block / rp11.SECTORS[meta.drive]); + rp11.rpda[meta.drive] = ((sector % rp11.SURFACES[meta.drive]) << 8) | (block % rp11.SECTORS[meta.drive]); + rp11.rpdc[meta.drive] = ~~(sector / rp11.SURFACES[meta.drive]); + if (block >= meta.maxblock) { + rp11.rpds[meta.drive] |= 0x400; // LST + } + if (err) { + rp11.rpds[meta.drive] |= 0x8000; //ATA + rp11.rpcs1 |= 0xc000; // set SC & TRE + switch (err) { + case 1: // read error + rp11.rpcs2 |= 0x200; // MXF Missed transfer + break; + case 2: // NXM + rp11.rpcs2 |= 0x800; // NEM (NXM) + break; + } + } + interrupt(1, 20, 5 << 5, 0254, function() { + rp11.rpds[meta.drive] |= 0x80; // 0x8080 must be for rp0 boot - but manual indicates no? + rp11.rpcs1 |= 0x80; // set ready + if (rp11.rpcs1 & 0x40) return true; + return false; + }); +} + +function accessRP11(physicalAddress, data, byteFlag) { + var idx, result; + idx = rp11.rpcs2 & 7; + switch (physicalAddress & ~1) { // RH11 always there addresses + case 017776700: // rp11.rpcs1 Control status 1 + result = (rp11.rpcs1 & ~0xb01) | ((rp11.rpba >> 8) & 0x300); + if (rp11.rpds[idx] & 0x100) { + result |= 0x800; // DVA depends on drive number + if (!(rp11.rpcs1 & 0x80)) result |= 1; // go is opposite of rdy + } else { + result &= 0xff7f; // rdy off if no dva + } + rp11.rpcs1 = result; + if (data >= 0) { + result = insertData(result, physicalAddress, data, byteFlag); + if (result >= 0) { + rp11.rpba = (rp11.rpba & 0x3cffff) | ((result << 8) & 0x30000); + result = (result & ~0xb880) | (rp11.rpcs1 & 0xb880); + if (!(result & 0x40)) interrupt(1, -1, 0, 0254); //remove pending interrupt if IE not set + if ((data & 0xc0) == 0xc0) interrupt(1, 8, 5 << 5, 0254); // RB: + rp11.rpcs1 = result; + if (result & 1 && (rp11.rpcs1 & 0x80)) { + rp11_go(); + } + } + } + break; + case 017776702: // rp11.rpwc Word count + result = insertData(rp11.rpwc, physicalAddress, data, byteFlag); + if (result >= 0) rp11.rpwc = result; + break; + case 017776704: // rp11.rpba Memory address + result = rp11.rpba & 0xffff; + if (data >= 0) { + result = insertData(result, physicalAddress, data, byteFlag); + if (result >= 0) { + rp11.rpba = (rp11.rpba & 0x3f0000) | (result & 0xfffe); // must be even + } + } + break; + case 017776710: // rp11.rpcs2 Control status 2 + result = rp11.rpcs2; + if (data >= 0) { + result = insertData(result, physicalAddress, data, byteFlag); + if (result >= 0) { + rp11.rpcs2 = (result & 0x3f) | (rp11.rpcs2 & 0xffc0); + if (result & 0x20) rp11_init(); + } + } + break; + case 017776716: // rp11.rpas Attention summary + result = 0; + for (idx = 0; idx < 8; idx++) { + if (rp11.rpds[idx] & 0x8000) { + if (data >= 0 && (data & (1 << idx))) { + rp11.rpds[idx] &= 0x7fff; + } else { + result |= 1 << idx; + } + } + } + if (data > 0) rp11.rpcs1 &= 0x7fff; // Turn off SC + break; + case 017776722: // rp11.rpdb Data buffer + result = 0; + break; + case 017776750: // rp11.rpbae Bus address extension + result = (rp11.rpba >> 16) & 0x3f; + if (data >= 0) { + result = insertData(result, physicalAddress, data, byteFlag); + if (result >= 0) { + rp11.rpba = ((result & 0x3f) << 16) | (rp11.rpba & 0xffff); + } + } + break; + case 017776752: // rp11.rpcs3 Control status 3 + // result = insertData(rp11.rpcs3, physicalAddress, data, byteFlag); + // if (result >= 0) rp11.rpcs3 = result; + result = 0; + break; + default: + idx = rp11.rpcs2 & 7; // drive number + if (rp11.rpds[idx] & 0x100) { + switch (physicalAddress & ~1) { // Drive registers which may or may not be present + case 017776706: // rp11.rpda Disk address + result = insertData(rp11.rpda[idx], physicalAddress, data, byteFlag); + if (result >= 0) rp11.rpda[idx] = result & 0x1f1f; + break; + case 017776712: // rp11.rpds drive status + result = rp11.rpds[idx]; + break; + case 017776714: // rp11.rper1 Error 1 + result = 0; // rp11.rper1[idx]; + break; + case 017776720: // rp11.rpla Look ahead + result = 0; // rp11.rpla[idx]; + break; + case 017776724: // rp11.rpmr Maintenance + //result = insertData(rp11.rpmr[idx], physicalAddress, data, byteFlag); + //if (result >= 0) rp11.rpmr[idx] = result & 0x3ff; + result = 0; + break; + case 017776726: // rp11.rpdt drive type read only + result = rp11.DTYPE[idx]; // 020022 + break; + case 017776730: // rp11.rpsn Serial number read only - lie and return drive + 1 + result = idx + 1; + break; + case 017776732: // rp11.rpof Offset register + result = insertData(rp11.rpof[idx], physicalAddress, data, byteFlag); + if (result >= 0) rp11.rpof[idx] = result; + //result = 0x1000; + break; + case 017776734: // rp11.rpdc Desired cylinder + result = insertData(rp11.rpdc[idx], physicalAddress, data, byteFlag); + if (result >= 0) rp11.rpdc[idx] = result & 0x1ff; + break; + case 017776736: // rp11.rpcc Current cylinder read only - lie and used desired cylinder + result = rp11.rpdc[idx]; + break; + case 017776740: // rp11.rper2 Error 2 + result = 0; + break; + case 017776742: // rp11.rper3 Error 3 + result = 0; // rp11.rper3[idx]; + break; + case 017776744: // rp11.rpec1 Error correction 1 read only + result = 0; // rp11.rpec1[idx]; + break; + case 017776746: // rp11.rpec2 Error correction 2 read only + result = 0; //rp11.rpec2[idx]; + break; + default: + CPU.CPU_Error |= 0x10; + return trap(4, 132); + } + } else { + rp11.rpcs2 |= 0x1000; // NED + rp11.rpcs1 |= 0xc000; // SC + TRE + if (rp11.rpcs1 & 0x40) { + interrupt(1, 5, 5 << 5, 0254); + } + result = 0; + } + } + return result; +} + + +// =========== TM11 routines =========== + + +var tm11 = { + mts: 0x65, // 17772520 Status Register 6 selr 5 bot 2 wrl 0 tur + mtc: 0x6080, // 17772522 Command Register 14-13 bpi 7 cu rdy + mtbrc: 0, // 17772524 Byte Record Counter + mtcma: 0, // 17772526 Current Memory Address Register + mtd: 0, // 17772530 Data Buffer Register + mtrd: 0, // 17772532 TU10 Read Lines + meta: [] //meta data for drive +}; + +function tm11_commandEnd() { + tm11.mts |= 1; // tape unit ready + tm11.mtc |= 0x80; + return tm11.mtc & 0x40; +} + +function tm11_finish() { + if (tm11.mtc & 0x40) { + interrupt(0, 10, 5 << 5, 0224, tm11_commandEnd); + } else { // if interrupt not enabled just mark completed + tm11_commandEnd(); + } +} + +function tm11_end(err, meta, position, address, count) { + if (err == 0 && meta.command > 0) { + if (address == 0 || address > 0x80000000) { // tape mark + meta.position = position; + tm11.mts |= 0x4000; // set EOF bit + } else { + switch (meta.command) { + case 1: // read + meta.position = position + 2 + ((address + 1) >> 1); + meta.command = 0; + count = (0x10000 - tm11.mtbrc) & 0xffff; + if (count >= address || count == 0) { + count = address; + tm11.mtbrc = (tm11.mtbrc + count) & 0xffff; + } else { + tm11.mts |= 0x200; // RLE + tm11.mtbrc = 0; + } + address = ((tm11.mtc & 0x30) << 12) | tm11.mtcma; + diskIO(2, meta, position, address, count); + // calculate meta.position set count to reduced amount + return; + case 4: // space forward + position = position + 2 + ((address + 1) >> 1); + meta.position = position; + tm11.mtbrc = (tm11.mtbrc + 1) & 0xffff; + if (tm11.mtbrc) { + diskIO(4, meta, position, 0, 4); + return; + } + break; + case 5: // space reverse + position = position - 4 - ((address + 1) >> 1); + meta.position = position; + tm11.mtbrc = (tm11.mtbrc + 1) & 0xffff; + if (tm11.mtbrc) { + if (position > 0) { + diskIO(4, meta, position - 2, 0, 4); + return; + } + } + break; + default: + panic(); + } + } + } + if (meta.command == 0) { + tm11.mtbrc = (tm11.mtbrc - count) & 0xffff; + tm11.mtcma = address & 0xffff; + tm11.mtc = (tm11.mtc & ~0x30) | ((address >> 12) & 0x30); + } + switch (err) { + case 1: // read error + tm11.mts |= 0x100; // Bad tape error + break; + case 2: // NXM + tm11.mts |= 0x80; // NXM + break; + } + tm11_finish(); +} + +function tm11_init() { + var i; + tm11.mts = 0x65; // 6 selr 5 bot 2 wrl 0 tur + tm11.mtc = 0x6080; // 14-13 bpi 7 cu rdy + for (i = 0; i < 8; i++) { + if (typeof tm11.meta[i] !== "undefined") { + tm11.meta[i].position == 0; + } + } +} + +function tm11_go() { + var sector, address, count; + var drive = (tm11.mtc >> 8) & 3; + tm11.mtc &= ~0x81; // ready bit (7!) and go (0) + tm11.mts &= 0x04fe; // turn off tape unit ready + if (typeof tm11.meta[drive] === "undefined") { + tm11.meta[drive] = { + "cache": [], + "blockSize": 65536, + "postProcess": tm11_end, + "drive": drive, + "mapped": 1, + "maxblock": 0, + "position": 0, + "command": 0, + "url": "tm" + drive + ".tap" + }; + } + tm11.meta[drive].command = (tm11.mtc >> 1) & 7; + //console.log("TM11 Function "+(tm11.meta[drive].command).toString(8)+" "+tm11.mtc.toString(8)+" "+tm11.mts.toString(8)+" @ "+tm11.meta[drive].position.toString(8)); + switch (tm11.meta[drive].command) { // function code + case 0: // off-line + break; + case 1: // read + diskIO(4, tm11.meta[drive], tm11.meta[drive].position, 0, 4); + return; + case 2: // write + case 3: // write end of file + case 6: // write with extended IRG + break; + case 4: // space forward + diskIO(4, tm11.meta[drive], tm11.meta[drive].position, 0, 4); + return; + case 5: // space reverse + if (tm11.meta[drive].position > 0) { + diskIO(4, tm11.meta[drive], tm11.meta[drive].position - 2, 0, 4); + return; + } + break; + case 7: // rewind + tm11.meta[drive].position = 0; + tm11.mts |= 0x20; // set BOT + break; + default: + break; + } + tm11_finish(); +} + +function accessTM11(physicalAddress, data, byteFlag) { + var result, drive = (tm11.mtc >> 8) & 3; + switch (physicalAddress & ~1) { + case 017772520: // tm11.mts + tm11.mts &= ~0x20; // turn off BOT + if (typeof tm11.meta[(tm11.mtc >> 8) & 3] !== "undefined") { + if (tm11.meta[(tm11.mtc >> 8) & 3].position == 0) { + tm11.mts |= 0x20; // turn on BOT + } + } + result = tm11.mts; + break; + case 017772522: // tm11.mtc + tm11.mtc &= 0x7fff; // no err bit + if (tm11.mts & 0xff80) tm11.mtc |= 0x8000; + result = insertData(tm11.mtc, physicalAddress, data, byteFlag); + if (data >= 0 && result >= 0) { + if ((tm11.mtc & 0x40) && !(result & 0x40)) { // if IE being reset then kill any pending interrupts + interrupt(1, -1, 5 << 5, 0224); + } + if (result & 0x1000) { //init + tm11.mts = 0x65; // 6 selr 5 bot 2 wrl 0 tur + tm11.mtc = 0x6080; // 14-13 bpi 7 cu rdy + } + if ((tm11.mtc & 0x80) && (result & 0x1)) { + tm11.mtc = (tm11.mtc & 0x80) | (result & 0xff7f); + tm11_go(); + } else { + if ((result & 0x40) && (tm11.mtc & 0xc0) == 0x80) { + interrupt(1, 10, 5 << 5, 0224); + } + tm11.mtc = (tm11.mtc & 0x80) | (result & 0xff7f); + } + } + break; + case 017772524: // tm11.mtbrc + result = insertData(tm11.mtbrc, physicalAddress, data, byteFlag); + if (result >= 0) tm11.mtbrc = result; + break; + case 017772526: // tm11.mtcma + result = insertData(tm11.mtcma, physicalAddress, data, byteFlag); + if (result >= 0) tm11.mtcma = result; + break; + case 017772530: // tm11.mtd + case 017772532: // tm11.mtrd + result = 0; + break; + default: + CPU.CPU_Error |= 0x10; + return trap(4, 134); + } + //console.log("TM11 Access "+physicalAddress.toString(8)+" "+data.toString(8)+" "+byteFlag.toString(8)+" -> "+result.toString(8)); + return result; +} + + +// =========== KW11 routines =========== + +var kw11 = { + init: 0, + csr: 0 +}; + + +function kw11_interrupt() { // Every 20 ms (50 Hz) set clock flag and schedule an interrupt + kw11.csr |= 0x80; // If halted don't interrupt as console commands would always be doing clock stuff + if ((CPU.runState != STATE.HALT) && (kw11.csr & 0x40)) { + interrupt(1, 0, 6 << 5, 0100); + } +} + +// =========== Console (tty) data =========== + +var tty = { + rbufQueue: [], + rbuf: 0, + rcsr: 0, + xbuf: 0, + xcsr: 0200, + delCode: 127, + del: 0 +}; + +function tty_xcsr() { + tty.xcsr |= 0x80; + if (tty.xcsr & 0x40) return true; + return false; +} + +function tty_rbuf() { + if (!(tty.rcsr & 0x80)) { + if (tty.rbufQueue.length > 0) { + tty.rbuf = tty.rbufQueue.shift(); + tty.rcsr |= 0x80; + if (tty.rcsr & 0x40) interrupt(1, 0, 4 << 5, 060); + } + } +} + +// Initialize unibus things for a reset instruction + +function reset_iopage() { + CPU.PIR = 0; + CPU.stackLimit = 0xff; + CPU.CPU_Error = 0; + CPU.interruptQueue = []; + CPU.MMR0 = CPU.MMR3 = CPU.mmuEnable = 0; + CPU.MMR3Mask[0] = CPU.MMR3Mask[1] = CPU.MMR3Mask[3] = 7; + CPU.mmuLastPage = 0; + tty.rcsr = 0; + tty.xcsr = 0200; + tty.rbufQueue = []; + kw11.csr = 0; + rk11_init(); + rl11.csr = 0x80; + rp11_init(); + tm11_init(); + +} + +// Update a word with new byte or word data allowing for odd addressing + +function insertData(original, physicalAddress, data, byteFlag) { + if (physicalAddress & 1) { + if (!byteFlag) { + return trap(4, 122); // trap word access to odd addresses + } + if (data >= 0) { + data = ((data << 8) & 0xff00) | (original & 0xff); + } else { + data = original; + } + } else { + if (data >= 0) { + if (byteFlag) { + data = (original & 0xff00) | (data & 0xff); + } + } else { + data = original; + } + } + return data; +} + +// Access to the unibus page - data is positive for a write or negative for a read + +function access_iopage(physicalAddress, data, byteFlag) { + var result, idx; + switch (physicalAddress & ~077) { + case 017777700: // 017777700 - 017777777 + switch (physicalAddress & ~1) { + case 017777776: // PSW + result = insertData(readPSW(), physicalAddress, data, byteFlag); + if (result >= 0 && data >= 0) { + writePSW(result); + return -1; // Kludge - signals no further processing to prevent changes to PSW + } + break; + case 017777774: // stack limit + result = insertData(CPU.stackLimit, physicalAddress, data, byteFlag); + if (result >= 0) { + if (data >= 0) { + CPU.stackLimit = result | 0xff; // Use stack limit with lower byte bits set + } + result &= 0xff00; + } + break; + case 017777772: // PIR + result = insertData(CPU.PIR, physicalAddress, data, byteFlag); + if (result >= 0 && data >= 0) { + result &= 0xfe00; + if (result) { // Need to calculate priority level from priority mask + idx = result >> 9; + do { + result += 0x22; + } while (idx >>= 1); + } + CPU.PIR = result; + if ((result & 0xe0) > (CPU.PSW & 0xe0)) { + CPU.priorityReview = 1; // Schedule an interrupt priority review if required + } + } + break; + case 017777766: // CPU error + if (CPU.cpuType !== 70) return trap(4, 222); + result = insertData(CPU.CPU_Error, physicalAddress, data, byteFlag); + if (result >= 0 && data >= 0) { + result = CPU.CPU_Error = 0; // Always writes as zero? + } + break; + case 017777764: // System I/D + if (CPU.cpuType !== 70) return trap(4, 224); + result = insertData(1, physicalAddress, data, byteFlag); + break; + case 017777762: // Upper size + if (CPU.cpuType !== 70) return trap(4, 226); + result = insertData(0, physicalAddress, data, byteFlag); + break; + case 017777760: // Lower size + if (CPU.cpuType !== 70) return trap(4, 228); + result = insertData((MAX_MEMORY >> 6) - 1, physicalAddress, data, byteFlag); + break; + case 017777770: // Microprogram break + if (data >= 0 && !(physicalAddress & 1)) data &= 0xff; // Required for KB11-CM without MFPT instruction + case 017777756: // + case 017777754: // + case 017777752: // Hit/miss + case 017777750: // Maintenance + case 017777746: // Cache control + case 017777744: // Memory system error + case 017777742: // High error address + case 017777740: // Low error address + if (CPU.cpuType !== 70) return trap(4, 232); + idx = (physicalAddress - 017777740) >> 1; + result = insertData(CPU.controlReg[idx], physicalAddress, data, byteFlag); + if (result >= 0) { + if ((physicalAddress & 1) == 017777746) result = 017; + if ((physicalAddress & 1) == 017777742) result = 03; + if ((physicalAddress & 1) == 017777740) result = 0177740; + CPU.controlReg[idx] = result; + } + break; + case 017777716: // User and Super SP - note the use of odd word addresses requiring return + if (physicalAddress & 1) { + if ((CPU.PSW >> 14) & 3 == 3) { // User Mode SP + if (data >= 0) CPU.registerVal[6] = data; + result = CPU.registerVal[6]; + } else { + if (data >= 0) CPU.stackPointer[3] = data; + result = CPU.stackPointer[3]; + } + } else { + if ((CPU.PSW >> 14) & 3 == 1) { // Super Mode SP + if (data >= 0) CPU.registerVal[6] = data; + result = CPU.registerVal[6]; + } else { + if (data >= 0) CPU.stackPointer[1] = data; + result = CPU.stackPointer[1]; + } + } + return result; // special exit to allow for odd address word access + case 017777714: + case 017777712: + case 017777710: // Register set 1 + idx = physicalAddress & 7; + if (CPU.PSW & 0x800) { + if (data >= 0) CPU.registerVal[idx] = data; + result = CPU.registerVal[idx]; + } else { + if (data >= 0) CPU.registerAlt[idx] = data; + result = CPU.registerAlt[idx]; + } + return result; // special exit to allow for odd address word access + case 017777706: // Kernel SP & PC + if (physicalAddress & 1) { + if (data >= 0) CPU.registerVal[7] = data; + result = CPU.registerVal[7]; + } else { + if ((CPU.PSW >> 14) & 3 == 0) { // Kernel Mode + if (data >= 0) CPU.registerVal[6] = data; + result = CPU.registerVal[6]; + } else { + if (data >= 0) CPU.stackPointer[0] = data; + result = CPU.stackPointer[0]; + } + } + return result; // special exit to allow for odd address word access + case 017777704: + case 017777702: + case 017777700: // Register set 0 + idx = physicalAddress & 7; + if (CPU.PSW & 0x800) { + if (data >= 0) CPU.registerAlt[idx] = data; + result = CPU.registerAlt[idx]; + } else { + if (data >= 0) CPU.registerVal[idx] = data; + result = CPU.registerVal[idx]; + } + return result; // special exit to allow for odd address word access + default: + CPU.CPU_Error |= 0x10; + return trap(4, 124); + } + break; + case 017777600: // 017777600 - 017777677 MMU user mode 3 Map + idx = (physicalAddress >> 1) & 037; + if (idx <= 15) { + result = insertData(CPU.mmuPDR[48 | idx], physicalAddress, data, byteFlag); + if (result >= 0) { + CPU.mmuPDR[48 | idx] = result & 0xff0f; + } + } else { + idx &= 0xf; + result = insertData(CPU.mmuPAR[48 | idx], physicalAddress, data, byteFlag); + if (result >= 0) { + CPU.mmuPAR[48 | idx] = result; + CPU.mmuPDR[48 | idx] &= 0xff0f; + } + } + break; + case 017777500: // 017777500 - 017777577 MMR0 MMR1 MMR2 Console KW11 + switch (physicalAddress & ~1) { + case 017777576: // MMR2 + result = insertData(CPU.MMR2, physicalAddress, data, byteFlag); + if (result >= 0) { + CPU.MMR2 = result; + } + break; + case 017777574: // MMR1 + result = CPU.MMR1; + if (result & 0xff00) result = ((result << 8) | (result >> 8)) & 0xffff; + break; + case 017777572: // MMR0 + if (!(CPU.MMR0 & 0xe000)) { + CPU.MMR0 = (CPU.MMR0 & 0xf381) | (CPU.mmuLastPage << 1); + } + result = insertData(CPU.MMR0, physicalAddress, data, byteFlag); + if (result >= 0 && data >= 0) { + CPU.MMR0 = result &= 0xf381; + CPU.mmuLastPage = (result >> 1) & 0x3f; + if (result & 0x101) { + if (result & 0x1) { + CPU.mmuEnable = READ_MODE | WRITE_MODE; + } else { + CPU.mmuEnable = WRITE_MODE; + } + } else { + CPU.mmuEnable = 0; + } + } + break; + case 017777570: // console panel display/switch; + if (data < 0) { + result = CPU.switchRegister & 0xffff; + } else { + result = insertData(CPU.displayRegister, physicalAddress, data, byteFlag); + if (result >= 0) CPU.displayRegister = result; + } + break; + case 017777566: // console tty xbuf + result = insertData(tty.xbuf, physicalAddress, data, byteFlag); + if (result >= 0 & data >= 0) { + tty.xbuf = result &= 0x7f; + if (result) { + putchar(result); + } + if (tty.xcsr & 0x40) { + tty.xcsr &= ~0x80; + interrupt(1, 10, 4 << 5, 064, tty_xcsr); + } else { + tty.xcsr |= 0x80; + } + } + break; + case 017777564: // console tty xcsr + result = insertData(tty.xcsr, physicalAddress, data, byteFlag); + if (result >= 0 && data >= 0) { + if (((tty.xcsr ^ result) & 0x40)) { // IE change? + if (result & 0x40) { + if (tty.xcsr & 0x80) { + interrupt(1, 0, 4 << 5, 064); + } + } else { + tty.xcsr |= 0x80; + interrupt(1, -1, 4 << 5, 064); + } + tty.xcsr = (tty.xcsr & 0x80) | (result & 0x40); + } + } + break; + case 017777562: // console tty rbuf + result = insertData(tty.rbuf, physicalAddress, data, byteFlag); + if (result >= 0 && data < 0) { + if (tty.rcsr & 0x80) { + tty.rcsr &= ~0x80; + if (tty.rbufQueue.length > 0) { + setTimeout(tty_rbuf, 55); + } + } + } + break; + case 017777560: // console tty rcsr + result = insertData(tty.rcsr, physicalAddress, data, byteFlag); + if (result >= 0 && data >= 0) { + if (((tty.rcsr ^ result) & 0x40)) { // IE change? + if (!(result & 0x40)) { + interrupt(1, -1, 4 << 5, 060); + } + } + tty.rcsr = (tty.rcsr & 0x80) | (result & 0x40); + } + break; + case 017777546: // kw11.csr + result = insertData(kw11.csr, physicalAddress, data, byteFlag); + if (result >= 0 && data >= 0) { + kw11.csr = result & ~0200; + if (!(kw11.csr & 0x40)) { + interrupt(1, -1, 6 << 5, 0100); + } + if (kw11.init < 1) { + kw11.init = 1; + setInterval(kw11_interrupt, 20); // initialize interrupts for every 20ms (50Hz) + } + } + break; + default: + CPU.CPU_Error |= 0x10; + return trap(4, 126); + } + break; + case 017777400: // 017777400 - 017777477 rk11 controller + result = accessRK11(physicalAddress, data, byteFlag); + break; + case 017776700: // 017777600 - 017777677 rp11 controller + if (physicalAddress <= 017776753) { + result = accessRP11(physicalAddress, data, byteFlag); + } else { + CPU.CPU_Error |= 0x10; + return trap(4, 134); + } + break; + case 017774400: // 017774400 - 017774477 rl11 controller + result = accessRL11(physicalAddress, data, byteFlag); + break; + case 017772500: // 017772500 - 017772577 MMR3 + switch (physicalAddress & ~1) { + case 017772516: // MMR3 - UB 22 x K S U + result = insertData(CPU.MMR3, physicalAddress, data, byteFlag); + if (result >= 0 & data >= 0) { + if (CPU.cpuType != 70) result &= ~0x30; // don't allow 11/45 to do 22 bit or use unibus map + CPU.MMR3 = result; + CPU.MMR3Mode = ((result & 4) >> 2) | (result & 2) | ((result & 1) << 2); // KSU bits in reverse order for ease of access + CPU.MMR3Mask[0] = 7 | ((result & 4) << 1); + CPU.MMR3Mask[1] = 7 | ((result & 2) << 2); + CPU.MMR3Mask[3] = 7 | ((result & 1) << 3); + } + break; + default: + result = accessTM11(physicalAddress, data, byteFlag); + break; + } + break; + case 017772300: // 017772300 - 017772377 MMU kernel mode 0 Map + idx = (physicalAddress >> 1) & 037; + if (idx <= 15) { + result = insertData(CPU.mmuPDR[0 | idx], physicalAddress, data, byteFlag); + if (result >= 0) { + CPU.mmuPDR[0 | idx] = result & 0xff0f; + } + } else { + idx &= 0xf; + result = insertData(CPU.mmuPAR[0 | idx], physicalAddress, data, byteFlag); + if (result >= 0) { + CPU.mmuPAR[0 | idx] = result; + CPU.mmuPDR[0 | idx] &= 0xff0f; + } + } + break; + case 017772200: // 017772200 - 017772277 MMU super mode 1 Map + idx = (physicalAddress >> 1) & 037; + if (idx <= 15) { + result = insertData(CPU.mmuPDR[16 | idx], physicalAddress, data, byteFlag); + if (result >= 0) { + CPU.mmuPDR[16 | idx] = result & 0xff0f; + } + } else { + idx &= 0xf; + result = insertData(CPU.mmuPAR[16 | idx], physicalAddress, data, byteFlag); + if (result >= 0) { + CPU.mmuPAR[16 | idx] = result; + CPU.mmuPDR[16 | idx] &= 0xff0f; + } + } + break; + case 017772000: // 017772000 - 017772006 vt11 display + if (typeof accessVT11 !== 'undefined') { + result = accessVT11(physicalAddress, data, byteFlag); + } else { + CPU.CPU_Error |= 0x10; + return trap(4, 140); + } + break; + case 017770300: // 017770300 - 017770377 Unibus Map + case 017770200: // 017770200 - 017770277 Unibus Map + if (CPU.cpuType != 70) return trap(4, 234); + idx = (physicalAddress >> 2) & 0x1f; + result = CPU.unibusMap[idx]; + if (physicalAddress & 02) result = (result >> 16) & 0x803f; // Low six bits plus top bit (!) + result = insertData(result & 0xffff, physicalAddress, data, byteFlag); + if (result >= 0 && data >= 0) { + if (physicalAddress & 02) { + CPU.unibusMap[idx] = ((result & 0x803f) << 16) | (CPU.unibusMap[idx] & 0xfffe); + } else { + CPU.unibusMap[idx] = (CPU.unibusMap[idx] & 0x803f0000) | (result & 0xfffe); + } + } + break; + default: + CPU.CPU_Error |= 0x10; + return trap(4, 142); + } + if (byteFlag && result >= 0) { // Make any required byte adjustment to the return result + if ((physicalAddress & 1)) { + result = result >> 8; + } else { + result &= 0xff; + } + } + return result; +} \ No newline at end of file diff --git a/macro-asm/boot.mac b/macro-asm/boot.mac new file mode 100755 index 0000000..1f816f9 --- /dev/null +++ b/macro-asm/boot.mac @@ -0,0 +1,747 @@ +; Javascript PDP 11/70 Emulator v1.8 +; written by Paul Nankervis +; Please send suggestions, fixes and feedback to paulnank@hotmail.com +; +; BOOT LOADER CODE +;REBASE HIGHER LINK/BOT:140000 BOOT + + .MACRO LDA LABEL + MOV PC,R0 + ADD LABEL-.,R0 + .ENDM + +START: RESET + CLR @#177776 + CLR @#177772 + CLR @#177564 + CLR @#177546 + CLR LGHTON + LDA #START + MOV R0,SP + + LDA #BANNER + JSR PC,PRINT + LDA #CLKAST + MOV R0,@#100 + MOV #340,@#102 + BIS #100,@#177546 ;SET CLOCK TICKING + + LDA #PROMPT + JSR PC,PRINT + SUB #256,SP + MOV SP,R0 + JSR PC,INPUT + CLR R0 +20$: WAIT + INC R0 + TST LGHTON + BEQ 20$ + MOV #054000,-(SP) ; SUPER PRIORITY 0 ALT REG + MOV LGHTON,-(SP) ; CALL SUPER LOOP START + RTT + +LGHTON: .WORD 0 +CLKTIC: .WORD 0 +DSPREG: .WORD 0 +CLKAST: + INC CLKTIC + DEC DSPREG + MOV DSPREG,@#177570 ; DISPLAY + RTT + +ONECHR: + TSTB @#177564 + BPL ONECHR + MOVB R0,@#177566 + RTS PC + +PRTPTR: .WORD 0 +PRINT: + BITB #100,@#177564 + BNE PRINT + MOV R0,PRTPTR + LDA #PRTAST + MOV R0,@#64 + MOV #200,@#66 + BISB #100,@#177564 + RTS PC + +PRTAST: + TSTB @PRTPTR + BEQ 2$ + MOVB @PRTPTR,@#177566 + INC PRTPTR + RTT +2$: CLRB @#177564 + RTT + +BUFFER: .WORD 0 ;INPUT BUFFER POINTER +LENGTH: .WORD 0 ;INPUT BUFFER LENGTH + +INPUT: + MOV R0,BUFFER + CLR LENGTH + LDA #INPAST + MOV R0,@#60 + MOV #200,@#62 + BISB #100,@#177560 + RTS PC + +INPAST: + MOV R0,-(SP) + MOVB @#177562,R0 + CMPB R0,#15 + BEQ 7$ ; CARRIAGE RETURN + CMPB R0,#127. + BEQ 2$ ;
+This is that PDP 11/45 emulator which is put together from my earlier PDP 11/70 emulator. Because a PDP 11/45 has less memory it cannot run some of the software a PDP 11/70 can.
++The PDP 11/45 was released in 1972 and is an older less powerful version of the PDP 11/70 which was released in 1975. It is the second implementation of the PDP 11 following on from the PDP 11/20, which had an odd coupling of a 16 address bit processor to an 18 address bit bus. Naturally then, the PDP 11/45 had to include some form of memory management!
++The PDP 11/45 has the same processor as the PDP 11/70 but lacks 22 bit addressing, restricting it to 124KW of memory instead of 4MB. It also lacks unibus mapping and a number of the PDP 11/70's special control registers. But still it has three addressing modes (Kernel, Supervisor, and User) and implements split Instruction and Data addressing. Split I&D cause Instruction memory references based on the the use of the program counter to map seperately from general Data addressing. As a result a program can have 32768 words of instruction memory AND 32768 words of data. Some of the newer operating systems such as BSD require this to fit programs into memory. Sadly a PDP 11/45 can't run BSD because the system maximum memory limit of 124KW is not sufficient to load the entire operating system.
++One wonders if these types of memory restrictions, which ultimately killed the PDP 11, influenced the design of Unix to be more modular. Perhaps leading to things like shell piping of data between small programs.
++Note: The boot code in this emulator is a custom PDP 11 program running with it's own set of light patterns. It is initially loaded at address 140000 and the LIGHTS command operates by mapping a WAIT instruction to different addresses within Supervisor mode. The source for this program can be found in the RT11 operating environment as BOOT.MAC You can use this code to boot one of the guest OSes or use the LIGHTS command and DIAG command to experiment with idle light patterns and load test the CPU.
++If you wish to toggle in a simple light chaser to the front panel then here are some switch commands which can be used:
++ +Address Data Code Switch commands + HALT, 001000, LOAD ADDRESS +001000 012700 mov #1,r0 012700, DEPOSIT +001002 000001 000001, DEPOSIT +001004 006100 rol r0 006100, DEPOSIT +001006 000005 reset 000005, DEPOSIT +001010 000775 br .-4 000775, DEPOSIT + 001000, LOAD ADDRESS, ENABLE, START ++To restart the initial boot code (if it has not been overwritten) use the switch commands: +
+ HALT, 140000, LOAD ADDRESS, START ++
+Of course if you want your own PDP 11/70 front panel you might consider +one of these.
++Happy emulating!
++Paul Nankervis
+ +Disk | OS | Comment |
RK0 | Unix V5 | Boot using: unix then login as root |
RK1 | RT11 v4.0 | The lightest/fastest OS here |
RK2 | RSTS V06C-03 | Boot and login as 1,2 with password SYSTEM or as 11,70 using PDP |
RK3 | XXDP | Diagnostic OS and utilities |
RK4 | RT-11 3B | Distribution for RT-11 Version 3B |
RK5 | RT-11 V5.4F | Distribution for RT-11 Version 5.4F |
RL0 | BSD 2.9 | Boot using: rl(0,0)rlunix CTRL/D to get to multiuser |
RL1 | RSX 11M v3.2 | Fails on a PDP 11/45 |
RL2 | RSTS/E v7.0 | Option: <LF> Suboption: <LF> ... Login as 1,2 using SYSTEM or 11,70 using PDP |
RL3 | XXDP | Larger version of diagnostics - including PDP 11/70 utilities |
RP0 | ULTRIX-11 V3.1 | CTRL/D to enter multiuser mode. Login as root with no password |
RP1 | BSD 2.11 | Fails on a PDP 11/45 |
RP2 | RSTS/E v9.6 | Answer boot questions and login as 1,2 (password SYSTEM) or 11,70 (no password) |
RP3 | RSX 11M v4.6 | Starts logged in as 1,2 (password SYSTEM) - user accounts 200,1 (no password) or 11,70 (password PDP) + |
+Note: Disks are shown in approximately order size. The RK05 disks at the top are small and not too bad to use across a network. +The RP06 disks at the bottom can be rather slow. +
Youtube video 2 + + +
+BOOT> boot rk0 +@unix + +login: root +# date +Fri Mar 21 12:09:02 EST 1975 +# chdir /etc +# pwd +../etc +# ls -al +total 40 +drwxr-xr-x 2 bin 240 Mar 21 12:07 . +drwxr-xr-x 9 bin 160 Jan 29 16:14 .. +-rwxr--r-- 1 bin 474 Nov 26 18:13 getty +-rwxr-xr-x 1 bin 1446 Nov 26 18:13 glob +-rwxr--r-- 1 bin 1972 Nov 26 18:13 init +-rwxr-xr-x 1 bin 814 Nov 26 18:13 lpd +-rwxr--r-- 1 bin 4136 Nov 26 18:13 mkfs +-rwxr--r-- 1 bin 1800 Nov 26 18:13 mknod +-rwsr-xr-x 1 root 2078 Nov 26 18:13 mount +-rw-r--r-- 1 bin 49 Nov 26 18:13 passwd +-rw-r--r-- 1 bin 70 Nov 26 18:13 rc +-rw-r--r-- 1 bin 56 Nov 26 18:13 ttys +-rwsr-xr-x 1 root 1990 Nov 26 18:13 umount +-rwxr-xr-x 1 bin 32 Nov 26 18:13 update +-rw-r--r-- 1 root 144 Mar 21 12:09 utmp +# cat /etc/passwd +root::0:1::/: +daemon::1:1::/bin: +bin::3:1::/bin: +# cal 10 1981 + Oct 1981 + S M Tu W Th F S + 1 2 3 + 4 5 6 7 8 9 10 +11 12 13 14 15 16 17 +18 19 20 21 22 23 24 +25 26 27 28 29 30 31 +# ls -al /bin +total 339 +drwxr-xr-x 2 bin 944 Nov 26 18:13 . +drwxr-xr-x 9 bin 160 Jan 29 16:14 .. +-rwxr-xr-x 1 bin 1514 Nov 26 18:13 ar +-rwxr-xr-x 1 bin 7308 Nov 26 18:13 as +-rwxr-xr-x 1 bin 6042 Nov 26 18:13 bas +-rwxr-xr-x 1 bin 152 Nov 26 18:13 cat +-rwxr-xr-x 1 bin 5668 Nov 26 18:13 cc +... ++ +
+BOOT> boot rk1 +RT-11SJ V04.00C + +.D 56=5015 + +.TYPE V4USER.TXT +Welcome to RT-11 Version 4. RT-11 V04 provides new hardware support +and some major enhancements over Version 3B +... +.D 56=0 + +.MAC BOOT +ERRORS DETECTED: 0 + +.LINK BOOT + +.DIR BOOT + +BOOT .MAC 16 BOOT .OBJ 4 +BOOT .SAV 4 + 3 Files, 24 Blocks + 2851 Free blocks + +.DIR + +SWAP .SYS 25 01-Feb-82 RT11BL.SYS 65 01-Feb-82 +RT11SJ.SYS 67 01-Feb-82 RT11FB.SYS 80 01-Feb-82 +TT .SYS 2 01-Feb-82 DT .SYS 3 01-Feb-82 +DP .SYS 3 01-Feb-82 DX .SYS 3 01-Feb-82 +... ++ + +
+BOOT> boot rk2 + +RSTS V06C-03 Vixen (DK2) + +Option: ST + +You currently have: JOB MAX = 31, SWAP MAX = 28K. + +JOB MAX or SWAP MAX changes? N + +Default memory allocation table specifies nonexistent memory. +Table will be reset by RSTS. + + Memory allocation table: + + 0K: 00000000 - 00147777 ( 26K) : EXEC + 26K: 00150000 - 00247777 ( 16K) : RTS (BASIC) + 42K: 00250000 - 00757777 ( 82K) : USER + 124K: 00760000 - End : NXM + + Table suboption? + +You currently have crash dump disabled. + +DD-MMM-YY? 31-OCT-76 +12:00 PM? 11:45 +INIT V06C-03 RSTS V06C-03 Vixen + +Command File Name? +DETACHING... + + +HELLO + +RSTS V06C-03 Vixen Job 2 KB0 31-Oct-76 11:45 AM +#11/70 +Password: PDP + +Ready + +DIR/S + Name .Ext Size Prot Access Date Time Clu RTS Open +SY:[11,70] +ACEY .BAS 5 < 60> 31-Oct-76 31-Oct-76 07:19 AM 1 RT11 0 +TREK .BAS 16 < 60> 31-Oct-76 31-Oct-76 06:58 AM 1 BASIC 0 +TREK .DOC 9 < 60> 31-Oct-76 31-Oct-76 06:58 AM 1 RT11 0 +ANIMAL.BAS 5 < 60> 31-Oct-76 31-Oct-76 06:59 AM 1 BASIC 0 +STRTRK.BAS 27 < 60> 31-Mar-81 31-Mar-81 12:00 M 2 RT11 0 +STRTR1.BAS 9 < 60> 31-Mar-81 31-Mar-81 12:06 PM 2 BASIC 0 +ADVENT.DOC 4 < 60> 13-Mar-77 20-Jul-85 06:50 AM 1 RT11 0 +ADVENT.SAV 93 <124> 13-Mar-77 20-Jul-85 06:50 AM 1 RT11 0 +ADVENT.VAR 22 < 60> 13-Mar-77 20-Jul-85 06:50 AM 1 RT11 0 +ADVTXT.TXT 125 < 60> 13-Mar-77 20-Jul-85 06:50 AM 1 RT11 0 +SYSMAC.SML 42 < 60> 13-Mar-77 13-Mar-77 06:42 AM 1 BASIC 0 +HELLO .MAC 1 < 60> 13-Mar-77 13-Mar-77 06:43 AM 1 RT11 0 +BOOT .MAC 24 < 60> 13-Mar-77 13-Mar-77 06:43 AM 1 RT11 0 + +Total of 382 blocks in 13 files in SY:[11,70] + +Ready + +RUN ADVENT + +WELCOME TO ADVENTURE!! WOULD YOU LIKE INSTRUCTIONS? + +NO +YOU ARE STANDING AT THE END OF A ROAD BEFORE A SMALL BRICK BUILDING. +AROUND YOU IS A FOREST. A SMALL STREAM FLOWS OUT OF THE BUILDING AND +DOWN A GULLY. + +EAST +YOU ARE INSIDE A BUILDING, A WELL HOUSE FOR A LARGE SPRING. + +THERE ARE SOME KEYS ON THE GROUND HERE. + +THERE IS A SHINY BRASS LAMP NEARBY. + +THERE IS FOOD HERE. + +THERE IS A BOTTLE OF WATER HERE. + +TAKE FOOD +OK + +TAKE KEYS +OK + +LIGHT LAMP +YOUR LAMP IS NOW ON. + +QUIT +DO YOU REALLY WANT TO QUIT NOW? + +YES +OK + +YOU SCORED 32 OUT OF A POSSIBLE 350, USING 6 TURNS. + +YOU ARE OBVIOUSLY A RANK AMATEUR. BETTER LUCK NEXT TIME. + +TO ACHIEVE THE NEXT HIGHER RATING, YOU NEED 4 MORE POINTS. + +Ready + +RUN TREK + +ORDERS: STARDATE = 2000 + +AS COMMANDER OF THE UNITED STARSHIP ENTERPRISE, YOUR MISSION +IS TO RID THE GALAXY OF THE DEADLY KLINGON MENACE. TO DO THIS, +YOU MUST DESTROY THE KLINGON INVASION FORCE OF 22 BATTLE +CRUISERS. YOU HAVE 40 SOLAR YEARS TO COMPLETE YOUR MISSION. +( I.E. UNTIL STARDATE 2040 ) + +GIVE COMMAND 'END' TO STOP THE GAME EARLY. +DO YOU REQUIRE FURTHER INSTRUCTIONS? N + +DO YOU WANT A CHART? Y + + 1 2 3 4 5 6 7 8 + --------------------------------- + 1 : : : : : : : : : + --------------------------------- + 2 : : : : : : : : : + --------------------------------- + 3 : : : : : : : : : + --------------------------------- + 4 : : : : : : : : : + --------------------------------- + 5 : : : : : : : : : + --------------------------------- + 6 : : : : : : : : : + --------------------------------- + 7 : : : : : : : : : + --------------------------------- + 8 : : : : : : : : : + --------------------------------- + +YOU ARE CURRENTLY IN QUADRANT 5 - 2 + +--------------- +B . . . . . . . +. * . . . . . . +. . . . . . E . +. . . . . . . . +. . * . . . . . +* . . . . . . . +. . . . . . . . +. . . . . * . . +--------------- + +STARDATE: 2000 CONDITION: YELLOW +QUADRANT: 5 - 2 SECTOR: 7 - 3 +ENERGY: 3000 PHOTON TORPEDOS: 10 +KLINGONS: 22 + +COMMAND? ^C + +Ready + +SYSTAT + +RSTS V06C-03 Vixen status at 31-Oct-76, 11:47 AM Up: 2:08 + +Job Who Where What Size State Run-Time RTS + 1 [OPR] Det ERRCPY 5K SR 3.7 BASIC + 2 [SELF] KB0 SYSTAT 8K RN Lck 4.6 BASIC + +Busy Devices: None + +Disk Structure: +Disk Open Free Cluster Errors Name Comments +DK2 3 315 1 0 VIXEN Pub, DLW + +Small Large Jobs Hung TTY's Errors +385 1 2/2 0 0 + +Run-Time Systems: + Name Ext Size Users Comments +BASIC BAC 16(16)K 2 Perm, Addr:26, KBM, CSZ +RSX TSK 2(28)K 0 Non-Res, KBM +RT11 SAV 4(28)K 0 Temp, Addr:49, KBM, CSZ, EMT:255 +RMS11 TSK 4(28)K 0 Non-Res + +Message Receivers: + Name Job Msgs Max Senders +ERRLOG 1 0 40 Priv + +Ready + +RUN $SWITCH +Run-Time System to switch to? RT11 + +.MACRO HELLO=HELLO +ERRORS DETECTED: 0 + +.LINK HELLO=HELLO + +.RUN HELLO +HELLO, WORLD! +.BYE +Confirm: Y +Saved all disk files; 384 blocks in use, 116 free +Job 2 User 11,70 logged off KB0 at 31-Oct-76 11:47 AM +System RSTS V06C-03 Vixen +Run time was 5.3 seconds +Elapsed time was 2 minutes +Good morning + ++
+BOOT> boot rk3 + +CHMDKB1 XXDP+ DK MONITOR +BOOTED VIA UNIT 2 +28K UNIBUS SYSTEM + +ENTER DATE (DD-MMM-YY): <CR> + +RESTART ADDR: 152010 +THIS IS XXDP+. TYPE "H" OR "H/L" FOR HELP. + +.D + +ENTRY# FILNAM.EXT DATE LENGTH START + + 1 HDDKB0.SYS 2-JAN-70 2 000112 + 2 HMDKB1.SYS 2-JAN-70 17 000113 + 3 HDDKB1.SYS 2-JAN-70 2 000114 + 4 HSAAC4.SYS 8-DEC-82 24 000115 +.... ++ + +
+BOOT> boot rl0 +:boot + +45Boot +: rl(0,0)rlunix + +CONFIGURE SYSTEM: +xp 0 csr 176700 vector 254 attached +rk 0 csr 177400 vector 220 attached +hk ? csr 177440 vector 210 skipped: No CSR +rl 0 csr 174400 vector 160 attached +rp ? csr 176700 vector 254 interrupt vector already in use +ht 0 csr 172440 vector 224 skipped: No CSR +tm 0 csr 172520 vector 224 skipped: No CSR +... +Erase=^?, kill=^U, intr=^C +# ls -al +total 546 +drwxr-xr-x11 root daemon 512 Mar 7 09:00 . +drwxr-xr-x11 root daemon 512 Mar 7 09:00 .. +-rw-rw-r-- 1 root daemon 164 Sep 29 09:20 .cshrc +-rw-rw-r-- 1 root daemon 266 Mar 7 08:43 .login +-rw-rw-r-- 1 root superuse 2 Jul 26 16:00 .msgsrc +-rw-rw-r-- 1 root daemon 116 Mar 30 00:59 .profile +-rw-r--r-- 1 root superuse 56 Nov 20 16:03 2.9stamp +-rw-rw-r-- 1 root superuse 450 Mar 30 00:50 READ_ME +drwxrwxr-x 2 bin bin 1632 Nov 20 16:04 bin +-rwxrwxr-x 1 root superuse 23572 Mar 7 09:05 boot +... +# cat /etc/passwd +root::0:2:The Man:/:/bin/csh +toor::0:2:The Man:/: +daemon:***:1:1:The devil himself:/: +sys:***:2:1::/: +bin:***:3:1::/: +uucp::4:1:UNIX-to-UNIX Copy:/usr/spool/uucppublic:/usr/lib/uucp/uucico +notes:***:5:1:Notesfiles maintainer:/usr/spool/notes: +anon:***:6:1:Notesfiles anonymous user:/usr/spool/notes: +news:***:7:1:News maintainer:/usr/spool/news: +wnj:ZDjXDBwXle2gc:8:2:Bill Joy,457E,7780:/a/guest/wnj:/bin/csh +dmr:AiInt5qKdjmHs:9:2:Dennis Ritchie:/a/guest/dmr: +ken:sq5UDrPlKj1nA:10:2:& Thompson:/a/guest/ken: +mike:KnKNwMkyCt8ZI:11:2:mike karels:/a/guest/mike:/bin/csh +carl:S2KiTfS3pH3kg:12:2:& Smith,508-21E,6258:/a/guest/carl:/bin/csh +joshua::999:2:&:/usr/games:/usr/games/wargames +# CTRL/D +Wed Dec 31 16:04:16 PST 1969 +/etc/fstab: No such file or directory +/usr/sys: No such file or directory +init: /dev/tty07: cannot open +... + +Berkeley Unix 2.9BSD + +:login: root + +Welcome to the 2.9BSD (Berkeley) UNIX system. + +tty: Command not found. +# ls -al /bin +total 1182 +-rwxrwxr-x 1 bin bin 8692 Dec 31 16:59 # +drwxrwxr-x 2 bin bin 1632 Nov 20 16:04 . +drwxr-xr-x11 root daemon 512 Mar 7 09:00 .. +-rwxrwxr-x 2 bin bin 2917 Dec 31 16:59 [ +-rwxrwxr-x 1 bin bin 30340 Mar 24 08:27 adb +-rwxrwxr-x 1 bin bin 9844 Dec 31 16:58 ar +-rwxrwxr-t 1 bin bin 5626 Sep 30 17:39 as +-rwxrwxr-x 1 bin bin 4508 Jan 18 08:22 cat +-rwxrwxr-t 1 bin bin 7314 Oct 9 04:04 cc +-rwxrwxr-x 1 bin bin 5096 Dec 31 16:59 chgrp +-rwxrwxr-x 1 bin bin 3364 Dec 31 16:59 chmod +... ++ + +
+BOOT> boot rl2 + +RSTS V7.0-07 Vixen (DL2) + +Option: <LF> + +You currently have: JOB MAX = 63, SWAP MAX = 31K. + +Default memory allocation table specifies nonexistent memory. +Table will be reset by RSTS. + + Memory allocation table: + + 0K: 00000000 - 00203777 ( 33K) : EXEC + 33K: 00204000 - 00303777 ( 16K) : RTS (BASIC) + 49K: 00304000 - 00757777 ( 75K) : USER + 124K: 00760000 - End : NXM + + Table suboption? <LF> + +You currently have crash dump enabled. +CRASH.SYS file of 50 blocks is not available +Crash dump automatically disabled + +DD-MMM-YY? 31-OCT-81 +12:00 PM? 12:00 +INIT V7.0-07A RSTS V7.0-07 Vixen + +Command File Name? <CR> +DETACHING... +... +HELLO 1,2 +Password: system +Job 1 is detached under this account +Job number to attach to? <CR> +1 other user is logged in under this account + +WELCOME TO RSTS/E V7.0 TIME SHARING + +Ready + +PRINT "Hello world",PI*4 +Hello world 12.5664 + +Ready + +DIR + Name .Ext Size Prot Date SY:[1,2] +CREATE.LOG 3 < 60> 31-Oct-81 +LOGIN .SAV 13 <232> 23-Sep-79 +LOGOUT.SAV 11 <232> 23-Sep-79 +PIP .SAV 50 <232> 23-Sep-79 +... + +Ready + +SYSTAT + +RSTS V7.0-07 Vixen status at 31-Oct-81, 12:01 PM Up: 1:53 + +Job Who Where What Size State Run-Time Pri/RB RTS + 1 [SELF] Det ERRCPY 5/31K SR 7.5 0/6 BASIC + 2 [SELF] KB0 SYSTAT 11/31K RN Lck 9.0 -8/6 BASIC + +Busy Devices: None + +Disk Structure: +Disk Open Free Cluster Errors Name Comments +DL2 2 5568 2 0 VIXEN Pub, DLW + +Small Large Jobs Hung TTY's Errors +478 0 2/8 0 0 + +Run-Time Systems: + Name Ext Size Users Comments +BASIC BAC 16(16)K 2 Perm, Addr:33, KBM, CSZ +RT11 SAV 4(28)K 0 Temp, Addr:65, KBM, CSZ, EMT:255 +TECO TEC 8(24)K 0 Non-Res + +Resident Libraries: None + +Message Receivers: + Name Job Msgs Max Senders +ERRLOG 1 0 40 Priv + +Ready ++ +
+BOOT> boot rl3 + +CHMDLD0 XXDP+ DL MONITOR +BOOTED VIA UNIT 3 +28K UNIBUS SYSTEM + +ENTER DATE (DD-MMM-YY): <CR> + +RESTART ADDR: 152010 +THIS IS XXDP+. TYPE "H" OR "H/L" FOR HELP. + +.D + +ENTRY# FILNAM.EXT DATE LENGTH START + + 1 MMDP .SAV 3-MAR-83C 17 000310 + 2 MTDP .SAV 3-MAR-83C 17 000331 + 3 HSAAD0.SYS 3-MAR-83 24 000352 + 4 HSABC0.SYS 3-MAR-83 28 000402 + 5 HSACC0.SYS 3-MAR-83 27 000436 + 6 HSADB0.SYS 3-MAR-83 25 000471 + 7 HUDIB0.SYS 3-MAR-83 5 000522 + 8 HELP .TXT 3-MAR-83 14 000527 +... ++ + +
+BOOT> boot rp0 + +Sizing Memory... + +Boot: hp(0,0)unix (CTRL/C will abort auto-boot) + +Load device (? for help, <RETURN> if none) < ht tm ts tk rx rl rc > ? <CR> + +hp(0,0)unix: 14784+17024+8192+8000+8064+8000+8064+8128+8000+7808+7936+7936+7680+7360+1344 + +ULTRIX-11 Kernel V3.1 + +realmem = 253952 +buffers = 25600 +clists = 1600 +usermem = 94784 +maxumem = 94784 +erase = delete, kill = ^U, intr = ^C +# ++ +
+BOOT> boot rp2 + + RSX-11M V4.6 BL56 124.K MAPPED +>RED DB1:=SY: +>RED DB1:=LB: +>MOU DB1:RSXM56 +>@DB1:[1,2]STARTUP +>* PLEASE ENTER TIME AND DATE (HR:MN DD-MMM-YY) [S]: <CR> +>TIM +00:00:11 29-APR-74 +>* ENTER LINE WIDTH OF THIS TERMINAL [D D:132.]: <CR> +>SET /BUF=TI:132. +>ACS SY:/BLKS=1024. +>; +>; This system startup command file (LB:[1,2]STARTUP.CMD) contains a +>; template of commands to initialize the queue print spooler and queue +.... +>INS LB:[1,54]INI.TSK +>INS LB:[1,54]MAC.TSK +>@ <EOF> +>BYE +Have a nice day +29-APR-74 00:00 TT0: logged off VIXEN ++ +
+BOOT> boot rp3 + +RSTS V9.6-11 VIXEN (DB3) INIT V9.6-11 + +Today's date? 31-OCT-86 + +Current time? 10:02 + +Start timesharing? <Yes> <CR> + +Cannot use extra 12K of buffers. Reduced to 11K. + +Size of monitor has changed from 73K to 72K. + +Default memory allocation table shows MORE +memory than INIT detects on this machine. + +Adjusting memory table. +Not enough room for current allocation of XBUF. + Resetting memory table. + + Memory allocation table: + + 0K: 00000000 - 00363777 ( 61K) : EXEC + 61K: 00364000 - 00727777 ( 57K) : USER + 118K: 00730000 - 00757777 ( 6K) : XBUF + +Memory available to RSTS/E is 124K words. + +31-Oct-86 10:02 AM + +11 devices disabled + +Proceed with system startup? <YES> <CR> + + Beginning RSTS/E system startup... +31-Oct-86 10:02 AM Installing monitor overlays +31-Oct-86 10:02 AM Mounting disks +31-Oct-86 10:02 AM Assigning logical names +31-Oct-86 10:02 AM Starting error logging +31-Oct-86 10:02 AM Setting system characteristics +31-Oct-86 10:02 AM Installing run-time systems and libraries + install/library/noaddress=unrestricted EDT$:EDT + ^ +?Memory would be fragmented +31-Oct-86 10:02 AM Setting terminal characteristics +31-Oct-86 10:02 AM Defining system commands +31-Oct-86 10:02 AM Setting printer characteristics +31-Oct-86 10:02 AM Starting spoolers + +*** From [1,2] on KB0: at 10:02 AM 31-Oct-86 +** RSTS/E is on the air... + + ++ + +
+Plenty! Especially in the places where I haven't managed to figure out what a real PDP 11/45 should do. :-( Core PDP 11/45 +stuff is well documented but some lesser used system functions require reverse engineering to understand. +
+If you have something you want me to look at let me know and I'll prioritise. However always happy to accept fixes! + +
home + + + + + + + + + + + diff --git a/pdp11-45.svg b/pdp11-45.svg new file mode 100755 index 0000000..e6fff4a --- /dev/null +++ b/pdp11-45.svg @@ -0,0 +1,82 @@ + + diff --git a/pdp11-70.svg b/pdp11-70.svg new file mode 100755 index 0000000..8d86c33 --- /dev/null +++ b/pdp11-70.svg @@ -0,0 +1,23 @@ + + \ No newline at end of file diff --git a/pdp11.html b/pdp11.html new file mode 100755 index 0000000..5583f8b --- /dev/null +++ b/pdp11.html @@ -0,0 +1,2350 @@ + + + +
++This emulator came about because years ago I was a programmer for RSTS/E on a PDP 11/45 and had admired the console idle loop light pattern - but I couldn't quite remember how it looked. Given the unavailability of real systems it became time to write an emulator!
++I was going to start with a PDP 11/45 emulator but the extra memory of a PDP 11/70 became far too attractive (a whole 4MB!). It took some time before I finally produced a PDP 11/45 version.
++I have met my core objective - I can now see the RSTS/E console light pattern that I was looking for, and found that newer versions (eg v9.6) have a different light pattern. Also I have now seen some of the light patterns for other OSes. RSX and BSD 2.11 have their own different patterns and Unix V5 and Ultrix operate with absolute minimum light movement (I'm assuming they operates mostly in WAIT mode).
++Getting all of the operating systems used here presents its own set of challenges - one of which is finding the software in the first place. But one of the most interesting was RSTS/E V06C which has its own story.
++Note: The boot code in this emulator is a custom PDP 11 program running with it's own set of light patterns. It is initially loaded at address 140000 and the LIGHTS command operates by mapping a WAIT instruction to different addresses within Supervisor mode. The source for this program can be found in the RT11 operating environment as BOOT.MAC You can use this code to boot one of the guest OSes or use the LIGHTS command and DIAG command to experiment with idle light patterns and load test the CPU.
++If you wish to toggle in a simple light chaser to the front panel then here are some switch commands which can be used:
++ +Address Data Code Switch commands + HALT, 001000, LOAD ADDRESS +001000 012700 mov #1,r0 012700, DEPOSIT +001002 000001 000001, DEPOSIT +001004 006100 rol r0 006100, DEPOSIT +001006 000005 reset 000005, DEPOSIT +001010 000775 br .-4 000775, DEPOSIT + 001000, LOAD ADDRESS, ENABLE, START ++To restart the initial boot code (if it has not been overwritten) use the switch commands: +
+ HALT, 140000, LOAD ADDRESS, ENABLE, START ++
+If you plan to run the emulator repeatedly or for a project, consider downloading the emulator to your own machine or server. This will significantly speed any of the emulator disk accesses and response times. All files and emulator OS disks can be found in the top level folder of http://skn.noip.me/pdp11/ or in the single zip file http://skn.noip.me/pdp11/pdp11.zip
++This emulator matches approximately the following SIMH configuration:
++ set cpu 11/70 1912K nofpp !1912K is not actually SIMH legal - use 2M instead + set clk 50hz + attach rk0 rk0.dsk !RK05 image of Unix V5 + attach rk1 rk1.dsk !RK05 image of RT11 v4.0 + attach rk2 rk2.dsk !RK05 image of RSTS V06C-03 + attach rk3 rk3.dsk !RK05 image of XXDP + attach rk4 rk4.dsk !RK05 image of RT-11 3B + attach rk5 rk5.dsk !RK05 image of RT-11 V5.4F + set rl0 RL02 + attach rl0 rl0.dsk !RL02 image of BSD 2.9 + set rl1 RL02 + attach rl1 rl1.dsk !RL02 image of RSX 11M v3.2 + set rl2 RL01 + attach rl2 rl2.dsk !RL01 image of RSTS/E v7.0 + set rl3 RL02 + attach rl3 rl3.dsk !RL02 image of XXDP+ + set rp0 RP06 + attach rp0 rp0.dsk !RP06 image of ULTRIX-11 V3.1 + set rp1 RP06 + attach rp1 rp1.dsk !RP06 image of BSD 2.11 + set rp2 RP04 + attach rp2 rp2.dsk !RP04 image of RSTS/E v9.6 + set rp3 RP04 + attach rp3 rp3.dsk !RP04 image of RSX 11M v4.6 + attach tm0 tm0.tap !Backup of RSTS 4B-17 + attach tm1 tm1.tap !Distribution for RSTS V06C-03 + attach tm2 tm2.tap !Distribution for RSTS V7.0 ++
+There are many PDP emulators out there and I have never seen what I consider to be a complete list. Some of the really interesting ones can be found by googling terms such as "vhdl pdp 11". However the gold standard seems to be SIMH at Trailing Edge. A different approach to Javascript PDP 11 emulation can be found at www.pcjs.org.
++I believe that the first PDP 11 emulator would be SIM-11 written in FORTRAN before the first PDP 11/20 hardware was even built - see How the PDP-11 Was Born. There is more PDP 11 history at www.hampage.hu.
++Of course if you want your own PDP 11/70 front panel you might consider one of these.
++Happy emulating!
++Paul Nankervis
+ +Disk | OS | Comment |
RK0 | Unix V5 | Boot using: unix then login as root |
RK1 | RT11 v4.0 | The lightest/fastest OS here |
RK2 | RSTS V06C-03 | Boot and login as 1,2 with password SYSTEM or as 11,70 using PDP |
RK3 | XXDP | Diagnostic OS and utilities |
RK4 | RT-11 3B | Distribution for RT-11 Version 3B |
RK5 | RT-11 V5.4F | Distribution for RT-11 Version 5.4F |
TM0 | RSTS 4B-17 | Boot ROLLIN from TM0 and restore DK0 with "DK:<MT:VIXEN/REW". Reboot from DK0 with "/BO:DK" and login as 1,2 with password SYSTEMor 11,70 using PDP Commands are case sensitive. |
RL0 | BSD 2.9 | Boot using: rl(0,0)rlunix CTRL/D to get to multiuser |
RL1 | RSX 11M v3.2 | Login as 1,2 with password SYSTEM |
RL2 | RSTS/E v7.0 | Option: <LF> Suboption: <LF> ... Login as 1,2 using SYSTEM or 11,70 using PDP |
RL3 | XXDP | Larger version of diagnostics - including PDP 11/70 utilities |
RP0 | ULTRIX-11 V3.1 | CTRL/D to enter multiuser mode. Login as root with no password |
RP1 | BSD 2.11 | Will autoboot and enter multiuser mode. Login as root with no password |
RP2 | RSTS/E v9.6 | Answer boot questions and login as 1,2 (password SYSTEM) or 11,70 (no password) |
RP3 | RSX 11M v4.6 | Starts logged in as 1,2 (password SYSTEM) - user accounts 200,1 (no password) or 11,70 (password PDP) |
+Note: Disks are shown in approximately order size. The RK05 disks at the top are small and not too bad to use across a network. +The RP06 disks at the bottom can be rather slow. +
Youtube video 2 + + +
+BOOT> boot rk0 +@unix + +login: root +# date +Fri Mar 21 12:09:02 EST 1975 +# chdir /etc +# pwd +../etc +# ls -al +total 40 +drwxr-xr-x 2 bin 240 Mar 21 12:07 . +drwxr-xr-x 9 bin 160 Jan 29 16:14 .. +-rwxr--r-- 1 bin 474 Nov 26 18:13 getty +-rwxr-xr-x 1 bin 1446 Nov 26 18:13 glob +-rwxr--r-- 1 bin 1972 Nov 26 18:13 init +-rwxr-xr-x 1 bin 814 Nov 26 18:13 lpd +-rwxr--r-- 1 bin 4136 Nov 26 18:13 mkfs +-rwxr--r-- 1 bin 1800 Nov 26 18:13 mknod +-rwsr-xr-x 1 root 2078 Nov 26 18:13 mount +-rw-r--r-- 1 bin 49 Nov 26 18:13 passwd +-rw-r--r-- 1 bin 70 Nov 26 18:13 rc +-rw-r--r-- 1 bin 56 Nov 26 18:13 ttys +-rwsr-xr-x 1 root 1990 Nov 26 18:13 umount +-rwxr-xr-x 1 bin 32 Nov 26 18:13 update +-rw-r--r-- 1 root 144 Mar 21 12:09 utmp +# cat /etc/passwd +root::0:1::/: +daemon::1:1::/bin: +bin::3:1::/bin: +# cal 10 1981 + Oct 1981 + S M Tu W Th F S + 1 2 3 + 4 5 6 7 8 9 10 +11 12 13 14 15 16 17 +18 19 20 21 22 23 24 +25 26 27 28 29 30 31 +# ls -al /bin +total 339 +drwxr-xr-x 2 bin 944 Nov 26 18:13 . +drwxr-xr-x 9 bin 160 Jan 29 16:14 .. +-rwxr-xr-x 1 bin 1514 Nov 26 18:13 ar +-rwxr-xr-x 1 bin 7308 Nov 26 18:13 as +-rwxr-xr-x 1 bin 6042 Nov 26 18:13 bas +-rwxr-xr-x 1 bin 152 Nov 26 18:13 cat +-rwxr-xr-x 1 bin 5668 Nov 26 18:13 cc +... ++ +
+BOOT> boot rk1 +RT-11SJ V04.00C + +.D 56=5015 + +.TYPE V4USER.TXT +Welcome to RT-11 Version 4. RT-11 V04 provides new hardware support +and some major enhancements over Version 3B +... +.D 56=0 + +.MAC BOOT +ERRORS DETECTED: 0 + +.LINK BOOT + +.DIR BOOT + +BOOT .MAC 16 BOOT .OBJ 4 +BOOT .SAV 4 + 3 Files, 24 Blocks + 2851 Free blocks + +.DIR + +SWAP .SYS 25 01-Feb-82 RT11BL.SYS 65 01-Feb-82 +RT11SJ.SYS 67 01-Feb-82 RT11FB.SYS 80 01-Feb-82 +TT .SYS 2 01-Feb-82 DT .SYS 3 01-Feb-82 +DP .SYS 3 01-Feb-82 DX .SYS 3 01-Feb-82 +... + +.R ADVENT + +WELCOME TO ADVENTURE!! WOULD YOU LIKE INSTRUCTIONS? + +YES +SOMEWHERE NEARBY IS COLOSSAL CAVE, WHERE OTHERS HAVE FOUND FORTUNES IN +TREASURE AND GOLD, THOUGH IT IS RUMORED THAT SOME WHO ENTER ARE NEVER +SEEN AGAIN. MAGIC IS SAID TO WORK IN THE CAVE. I WILL BE YOUR EYES +AND HANDS. DIRECT ME WITH COMMANDS OF 1 OR 2 WORDS. I SHOULD WARN +YOU THAT I LOOK AT ONLY THE FIRST FOUR LETTERS OF EACH WORD, SO YOU'LL +HAVE TO ENTER "NORTHEAST" AS "NE" TO DISTINGUISH IT FROM "NORTH". +(SHOULD YOU GET STUCK, TYPE "HELP" FOR SOME GENERAL HINTS. FOR INFOR- +MATION ON HOW TO END YOUR ADVENTURE, ETC., TYPE "INFO".) + - - - +THIS PROGRAM WAS ORIGINALLY DEVELOPED BY WILLIE CROWTHER. MOST OF THE +FEATURES OF THE CURRENT PROGRAM WERE ADDED BY DON WOODS (DON @ SU-AI). +THE CURRENT VERSION WAS DONE BY MIKE WESTON. + +YOU ARE STANDING AT THE END OF A ROAD BEFORE A SMALL BRICK BUILDING. +AROUND YOU IS A FOREST. A SMALL STREAM FLOWS OUT OF THE BUILDING AND +DOWN A GULLY. + +EAST +YOU ARE INSIDE A BUILDING, A WELL HOUSE FOR A LARGE SPRING. + +THERE ARE SOME KEYS ON THE GROUND HERE. + +THERE IS A SHINY BRASS LAMP NEARBY. + +THERE IS FOOD HERE. + +THERE IS A BOTTLE OF WATER HERE. + +TAKE FOOD +OK +... ++ +
+BOOT> boot rk2 + +RSTS V06C-03 vixen (DK2) + +Option: <LF> + +You currently have: JOB MAX = 32, SWAP MAX = 28K. + +You currently have crash dump disabled. + +DD-MMM-YY? 31-OCT-76 +12:00 PM? 9:03 +INIT V06C-03 RSTS V06C-03 vixen + +Command File Name? <CR> +DETACHING... +... + +I11/70 +Password: PDP + +Ready + +DIR + Name .Ext Size Prot Date SY:[11,70] +ACEY .BAS 5 < 60> 31-Oct-76 +TREK .BAS 16 < 60> 31-Oct-76 +TREK .DOC 9 < 60> 31-Oct-76 +ANIMAL.BAS 5 < 60> 31-Oct-76 +STRTRK.BAS 27 < 60> 31-Mar-81 +STRTR1.BAS 9 < 60> 31-Mar-81 +ADVENT.DOC 4 < 60> 20-Jul-85 +ADVENT.SAV 93 <124> 20-Jul-85 +ADVENT.VAR 22 < 60> 20-Jul-85 +ADVTXT.TXT 125 < 60> 20-Jul-85 +SYSMAC.SML 42 < 60> 13-Mar-77 +HELLO .MAC 1 < 60> 13-Mar-77 +BOOT .MAC 24 < 60> 13-Mar-77 + +Total of 35 blocks in 4 files in SY:[11,70] + +Ready + +SYSTAT + +RSTS V06C-03 vixen status at 31-Oct-76, 09:03 AM Up: 18 + +Job Who Where What Size State Run-Time RTS + 1 [OPR] Det ERRCPY 5K SR 3.4 BASIC + 2 [SELF] KB0 SYSTAT 8K RN Lck 0.3 BASIC + +Busy Devices: None + +Disk Structure: +Disk Open Free Cluster Errors Name Comments +DK2 3 239 1 0 VIXEN Pub, DLW + +Small Large Jobs Hung TTY's Errors +345 0 2/2 0 0 + +Run-Time Systems: + Name Ext Size Users Comments +BASIC BAC 16(16)K 2 Perm, Addr:25, KBM, CSZ +RSX TSK 2(28)K 0 Non-Res, KBM +RT11 SAV 4(28)K 0 Non-Res, KBM, CSZ, EMT:255 +RMS11 TSK 4(28)K 0 Non-Res + +Message Receivers: + Name Job Msgs Max Senders +ERRLOG 1 0 40 Priv + +Ready + +RUN ACEY + ACEY DUCEY CARD GAME + CREATIVE COMPUTING MORRISTOWN, NEW JERSEY + + +ACEY-DUCEY IS PLAYED IN THE FOLLOWING MANNER +THE DEALER (COMPUTER) DEALS TWO CARDS FACE UP +YOU HAVE AN OPTION TO BET OR NOT BET DEPENDING +ON WHETHER OR NOT YOU FEEL THE CARD WILL HAVE +A VALUE BETWEEN THE FIRST TWO. +IF YOU DO NOT WANT TO BET, INPUT A 0 +YOU NOW HAVE 100 DOLLARS. +.... +HERE ARE YOUR NEXT TWO CARDS: + 8 +KING + +WHAT IS YOUR BET? 95 +JACK +YOU WIN!!! +YOU NOW HAVE 190 DOLLARS. + +HERE ARE YOUR NEXT TWO CARDS: + 2 + 10 + +WHAT IS YOUR BET? ^C + +Ready + +BYE +Confirm: Y +Saved all disk files; 35 blocks in use, 65 free +Job 2 User 11,70 logged off KB0 at 31-Oct-76 09:04 AM +System RSTS V06C-03 vixen +Run time was 1.5 seconds +Elapsed time was 1 minute +Good morning + ++ +
+BOOT> boot rk3 + +CHMDKB1 XXDP+ DK MONITOR +BOOTED VIA UNIT 3 +28K UNIBUS SYSTEM + +ENTER DATE (DD-MMM-YY): <CR> + +RESTART ADDR: 152010 +THIS IS XXDP+. TYPE "H" OR "H/L" FOR HELP. + +.D + +ENTRY# FILNAM.EXT DATE LENGTH START + + 1 HDDKB0.SYS 2-JAN-70 2 000112 + 2 HMDKB1.SYS 2-JAN-70 17 000113 + 3 HDDKB1.SYS 2-JAN-70 2 000114 + 4 HSAAC4.SYS 8-DEC-82 24 000115 +.... ++ + +
+BOOT> boot tm0 + +ROLLIN V07 + +#DK:<MT:VIXEN/REW +END-OF-FILE DURING READ, TYPE +M TO MOUNT ANOTHER REEL, OR K TO KILL REQUEST: + +#/BO:DK + +RSTS V04B-17 VIXEN + +OPTION? ST +DD-MON-YY? 31-OCT-71 +HH:MM? 6:42 +VIXEN - SYSTEM PACK MOUNTED +ENABLE CRASH DUMP? N +CHAIN "INIT" +CATASTROPHIC ERROR +PROGRAM LOST-SORRY +I/O CHANNEL NOT OPEN + +Ready + +SYSTEM INITIALIZATION PROGRAM + +END OF FILE ON DEVICE - INIT ASSUMED COMPLETE + +Ready + +CAT +LOGIN .BAS 7 60 31-Oct-71 31-Oct-71 06:42 AM +LOGIN .BAC 15 60 31-Oct-71 31-Oct-71 06:42 AM +LOGOUT.BAS 7 60 31-Oct-71 31-Oct-71 06:42 AM +.... + +Ready + +BYE +CONFIRM: Y +SAVED ALL DISK FILES; 782 BLOCKS IN USE +JOB 1 USER 1,2 LOGGED OFF KB0 AT 31-Oct-71 06:42 AM +SYSTEM RSTS V04B-17 VIXEN +RUN TIME WAS 1.4 SECONDS +ELAPSED TIME WAS 13 SECONDS +GOOD MORNING + +HELLO + +RSTS V04B-17 VIXEN JOB 1 KB0 31-Oct-71 06:42 AM +#11,70 +PASSWORD: +RSTS V4B-17 IS NOW AVAILABLE... + +NEW OR OLD-- +CAT +PRIME .BAS 1 60 31-Oct-71 31-Oct-71 06:43 AM +PI .BAS 1 60 31-Oct-71 31-Oct-71 06:43 AM + +Ready + +RUN PI +3.14159265358979 + +Ready + +RUN $SYSTAT +OUTPUT STATUS TO? + +RSTS V04B-17 VIXEN STATUS ON 31-Oct-71 AT 06:42 AM UP: 42 + +JOB WHO WHERE WHAT SIZE STATE RUN-TIME + 1 11,70 KB0 SYSTAT 6K RN 3.4 + +BUSY DEVICES: NONE + +DISK STRUCTURE: +DISK OPEN FREE CLUSTER ERRORS COMMENTS +DK0 0 3184 1 0 PUBLIC + +SMALL LARGE ERRORS HUNG TTY'S + 69 0 0 0 + +Ready ++ +
+BOOT> boot rl0 +:boot + +70Boot +: rl(0,0)rlunix + +CONFIGURE SYSTEM: +xp 0 csr 176700 vector 254 attached +rk 0 csr 177400 vector 220 attached +hk ? csr 177440 vector 210 skipped: No CSR +rl 0 csr 174400 vector 160 attached +rp ? csr 176700 vector 254 interrupt vector already in use +ht 0 csr 172440 vector 224 skipped: No CSR +tm 0 csr 172520 vector 224 skipped: No CSR +... +Erase=^?, kill=^U, intr=^C +# ls -al +total 546 +drwxr-xr-x11 root daemon 512 Mar 7 09:00 . +drwxr-xr-x11 root daemon 512 Mar 7 09:00 .. +-rw-rw-r-- 1 root daemon 164 Sep 29 09:20 .cshrc +-rw-rw-r-- 1 root daemon 266 Mar 7 08:43 .login +-rw-rw-r-- 1 root superuse 2 Jul 26 16:00 .msgsrc +-rw-rw-r-- 1 root daemon 116 Mar 30 00:59 .profile +-rw-r--r-- 1 root superuse 56 Nov 20 16:03 2.9stamp +-rw-rw-r-- 1 root superuse 450 Mar 30 00:50 READ_ME +drwxrwxr-x 2 bin bin 1632 Nov 20 16:04 bin +-rwxrwxr-x 1 root superuse 23572 Mar 7 09:05 boot +... +# cat /etc/passwd +root::0:2:The Man:/:/bin/csh +toor::0:2:The Man:/: +daemon:***:1:1:The devil himself:/: +sys:***:2:1::/: +bin:***:3:1::/: +uucp::4:1:UNIX-to-UNIX Copy:/usr/spool/uucppublic:/usr/lib/uucp/uucico +notes:***:5:1:Notesfiles maintainer:/usr/spool/notes: +anon:***:6:1:Notesfiles anonymous user:/usr/spool/notes: +news:***:7:1:News maintainer:/usr/spool/news: +wnj:ZDjXDBwXle2gc:8:2:Bill Joy,457E,7780:/a/guest/wnj:/bin/csh +dmr:AiInt5qKdjmHs:9:2:Dennis Ritchie:/a/guest/dmr: +ken:sq5UDrPlKj1nA:10:2:& Thompson:/a/guest/ken: +mike:KnKNwMkyCt8ZI:11:2:mike karels:/a/guest/mike:/bin/csh +carl:S2KiTfS3pH3kg:12:2:& Smith,508-21E,6258:/a/guest/carl:/bin/csh +joshua::999:2:&:/usr/games:/usr/games/wargames +# CTRL/D +Wed Dec 31 16:04:16 PST 1969 +/etc/fstab: No such file or directory +/usr/sys: No such file or directory +init: /dev/tty07: cannot open +... + +Berkeley Unix 2.9BSD + +:login: root + +Welcome to the 2.9BSD (Berkeley) UNIX system. + +tty: Command not found. +# ls -al /bin +total 1182 +-rwxrwxr-x 1 bin bin 8692 Dec 31 16:59 # +drwxrwxr-x 2 bin bin 1632 Nov 20 16:04 . +drwxr-xr-x11 root daemon 512 Mar 7 09:00 .. +-rwxrwxr-x 2 bin bin 2917 Dec 31 16:59 [ +-rwxrwxr-x 1 bin bin 30340 Mar 24 08:27 adb +-rwxrwxr-x 1 bin bin 9844 Dec 31 16:58 ar +-rwxrwxr-t 1 bin bin 5626 Sep 30 17:39 as +-rwxrwxr-x 1 bin bin 4508 Jan 18 08:22 cat +-rwxrwxr-t 1 bin bin 7314 Oct 9 04:04 cc +-rwxrwxr-x 1 bin bin 5096 Dec 31 16:59 chgrp +-rwxrwxr-x 1 bin bin 3364 Dec 31 16:59 chmod +... ++ + +
+BOOT> boot rl1 + + RSX-11M V3.2 BL26 1912K MAPPED +>RED DL1:=SY: +>RED DL1:=LB: +>MOU DL1:RSXM26 +>@DL1:[1,2]STARTUP +>* Enter date and time ( dd-mmm-yy hh:mm ) [S]: 29-JAN-90 12:01 +>TIM 29-JAN-90 12:01 +>INS $PIP +>INS $EDT +>BYE +> +HAVE A GOOD AFTERNOON +29-JAN-90 12:01 TT0: LOGGED OFF +>@ <EOF> +>HELLO 1,2 +PASSWORD: + + RSX-11M BL26 MULTI-USER SYSTEM + +GOOD AFTERNOON +29-JAN-90 12:01 LOGGED ON TERMINAL TT0: + +Welcome to RSX-11M V3.2 timesharing + +>PIP/LI + +DIRECTORY DL1:[1,2] +29-JAN-90 12:01 + +HELLO.MAC;1 1. 30-OCT-76 12:02 +SYE.HLP;1 8. 26-MAY-79 13:52 +EDTCOM.MSG;1 16. 26-MAY-79 13:52 +QIOSYM.MSG;1 29. 26-MAY-79 13:52 +LOGIN.TXT;1 1. 31-OCT-81 12:11 +HELP.HLP;1 1. 31-OCT-81 12:11 +STARTUP.CMD;1 1. 31-OCT-81 12:04 +FORTH.MAC;1 149. 30-OCT-76 12:02 + +TOTAL OF 206./223. BLOCKS IN 8. FILES + +>INS $MAC +>MAC HELLO,HELLO=HELLO +>INS $TKB +>TKB HELLO=HELLO +>INS HELLO +>RUN HELLO +> + HELLO WORLD! + +>UNS HELLO +>BYE +> +HAVE A GOOD AFTERNOON +29-JAN-90 12:02 TT0: LOGGED OFF +> ++ + +
+BOOT> boot rl2 + +RSTS V7.0-07 Vixen (DL2) + +Option: + +You currently have: JOB MAX = 63, SWAP MAX = 31K. + +Default memory allocation table specifies some existing memory +as being nonexistent. + +Table will be reset by RSTS. + + Memory allocation table: + + 0K: 00000000 - 00207777 ( 34K) : EXEC + 34K: 00210000 - 00307777 ( 16K) : RTS (BASIC) + 50K: 00310000 - 16737777 (1862K) : USER + 1912K: 16740000 - End : NXM + + + Table suboption? + +You currently have crash dump disabled. + +DD-MMM-YY? 31-OCT-81 +12:00 PM? +INIT V7.0-07 RSTS V7.0-07 Vixen + +Command File Name? +DETACHING... + +I11/70 +Password: PDP + +Ready + +DIR + Name .Ext Size Prot Date SY:[11,70] +ACEY .BAS 5 < 60> 31-Oct-76 +TREK .BAS 16 < 60> 31-Oct-76 +TREK .DOC 9 < 60> 31-Oct-76 +ANIMAL.BAS 5 < 60> 31-Oct-76 +STRTRK.BAS 27 < 60> 31-Mar-81 +STRTR1.BAS 9 < 60> 31-Mar-81 +ADVENT.DOC 4 < 60> 20-Jul-85 +ADVENT.SAV 93 <124> 20-Jul-85 +ADVENT.VAR 22 < 60> 20-Jul-85 +ADVTXT.TXT 125 < 60> 20-Jul-85 +SYSMAC.SML 42 < 60> 13-Mar-77 +HELLO .MAC 1 < 60> 13-Mar-77 +BOOT .MAC 24 < 60> 13-Mar-77 + +Total of 382 blocks in 13 files in SY:[11,70] + +Ready + +SYSTAT + +RSTS V7.0-07 Vixen status at 31-Oct-81, 12:00 PM Up: 31 + +Job Who Where What Size State Run-Time RTS + 1 [OPR] Det ERRCPY 5K SR 4.8 BASIC + 2 [SELF] KB0 SYSTAT 11K RN Lck 0.9 BASIC + +Busy Devices: None + +Disk Structure: +Disk Open Free Cluster Errors Name Comments +DL2 3 2502 1 0 VIXEN Pub, DLW + +Small Large Jobs Hung TTY's Errors +488 1 2/2 0 0 + +Run-Time Systems: + Name Ext Size Users Comments +BASIC BAC 16(16)K 2 Perm, Addr:34, KBM, CSZ +RT11 SAV 4(28)K 0 Non-Res, KBM, CSZ, EMT:255 +RSX TSK 3(28)K 0 Non-Res, KBM +TECO TEC 8(24)K 0 Non-Res + +Resident Libraries: None + +Message Receivers: + Name Job Msgs Max Senders +ERRLOG 1 0 40 Priv + +Ready + +EDT TEST.BAS +*I +10 PRINT "HELP" +20 END +^Z +*EXIT +2 LINES OUTPUT + +Ready + +OLD TEST + +Ready + +RUN +TEST 12:01 AM 31-Oct-81 +HELP + +Ready + +BYE +Confirm: Y +Saved all disk files; 385 blocks in use, 115 free +Job 2 User 11,70 logged off KB0 at 31-Oct-81 12:01 AM +System RSTS V7.0-07 Vixen +Run time was 1.7 seconds +Elapsed time was 1 minute +Good morning + + ++ +
+BOOT> boot rl3 + +CHMDLD0 XXDP+ DL MONITOR +BOOTED VIA UNIT 3 +28K UNIBUS SYSTEM + +ENTER DATE (DD-MMM-YY): <CR> + +RESTART ADDR: 152010 +THIS IS XXDP+. TYPE "H" OR "H/L" FOR HELP. + +.D + +ENTRY# FILNAM.EXT DATE LENGTH START + + 1 MMDP .SAV 3-MAR-83C 17 000310 + 2 MTDP .SAV 3-MAR-83C 17 000331 + 3 HSAAD0.SYS 3-MAR-83 24 000352 + 4 HSABC0.SYS 3-MAR-83 28 000402 + 5 HSACC0.SYS 3-MAR-83 27 000436 + 6 HSADB0.SYS 3-MAR-83 25 000471 + 7 HUDIB0.SYS 3-MAR-83 5 000522 + 8 HELP .TXT 3-MAR-83 14 000527 +... +.R EKBA?? +EKBAD0.BIC +AA +CEKBAD0 11/70 CPU #1 + +END PASS +END PASS +END PASS +restart through switches + +.R EKBE?? +EKBEE1.BIC + +CEKBEE0 11/70 MEM MGMT + +CPU UNDER TEST FOUND TO BE A KB11-CM + +END PASS # 1 TOTAL ERRORS SINCE LAST REPORT 0 +END PASS # 2 TOTAL ERRORS SINCE LAST REPORT 0 +END PASS # 3 TOTAL ERRORS SINCE LAST REPORT 0 +END PASS # 4 TOTAL ERRORS SINCE LAST REPORT 0 +END PASS # 5 TOTAL ERRORS SINCE LAST REPORT 0 +.... +restart through switches + +.R EQKC?? +EQKCE1.BIC + +CEQKC-E...PDP 11/70 CPU EXERCISER + +CPU UNDER TEST FOUND TO BE A 11/74 (KB11CM) + +PROCESSOR ID REGISTER =000001 (OCTAL) 1 (DECIMAL) +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 ALLOW RELOCATION VIA I/O DEVICE (NOTE CHANGE) + 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 + +**NOTE** SWITCH REG BIT 8 HAS BEEN REVERSED IN REV D +NOTE THAT SWR BIT 8 SET NOW ALLOWS I/O RELOCATION + +THIS PROGRAM SUPPORTS I/O RELOCATION ONLY WITH THE FOLLOWING DEVICES: +RP03,RK05,RP04/5/6,RS03/4 +THE FOLLOWING DEVICES AND DRIVES WILL BE USED FOR RELOCATION IF BIT 8 SET: +DEVICE DRIVES +RK05 0, 1, 2, 3, 4, 5, 6, 7, +RP04 0, 1, 2, 3, +TYPE A CHARACTER TO CONTINUE + +1THE QUICK BROWN FOX JUMPED OVER THE LAZY DOGS BACK 0123456789 +2THE QUICK BROWN FOX JUMPED OVER THE LAZY DOGS BACK 0123456789 +3THE QUICK BROWN FOX JUMPED OVER THE LAZY DOGS BACK 0123456789 +4THE QUICK BROWN FOX JUMPED OVER THE LAZY DOGS BACK 0123456789 +5THE QUICK BROWN FOX JUMPED OVER THE LAZY DOGS BACK 0123456789 +000:01:33 + +END PASS # 1 TOTAL ERRORS SINCE LAST REPORT 0 +1THE QUICK BROWN FOX JUMPED OVER THE LAZY DOGS BACK 0123456789 +2THE QUICK BROWN FOX JUMPED OVER THE LAZY DOGS BACK 0123456789 +... + ++ + +
+BOOT> boot rp0 + +Sizing Memory... + +Boot: hp(0,0)unix (CTRL/C will abort auto-boot) + +Load device (? for help, <RETURN> if none) < ht tm ts tk rx rl rc > ? <CR> + +hp(0,0)unix: 14784+17024+8192+8000+8064+8000+8064+8128+8000+7808+7936+7936+7680+7360+1344 + +ULTRIX-11 Kernel V3.1 + +realmem = 3915776 +buffers = 25600 +clists = 1600 +usermem = 3756608 +maxumem = 212992 +erase = delete, kill = ^U, intr = ^C +# CTRL/D + +Restricted rights: + + Use, duplication, or disclosure is subject + to restrictions stated in your contract with + Digital Equipment Corporation. + +*UNIX is a trademark of AT&T Bell Laboratories. + +Mounted /dev/hp01 on /usr +Mounted /dev/hp04 on /user1 + +Sat Oct 31 12:06:33 GMT-0:00 1981 + +ERROR LOG has - 1 of 200 blocks used + +ULTRIX-11 System V3.1 (vixen) + +login: root + +Welcome to the ULTRIX-11 System + +erase = delete, kill = ^U, intr = ^C +vixen# uname -a +ULTRIX-11 vixen 3 0 PDP-11/70 +vixen# ps -xl + + F S UID PID PPID CPU PRI NICE ADDR SZ WCHAN TTY TIME CMD + 3 S 0 0 0 205 0 20 3756 4 73326 ? 0:10 swapper + 1 S 0 1 0 0 30 20 4770 13 114226 ? 0:00 /etc/init +1101 S 0 2 1 0 5 0 6162 22 112272 ? 0:00 /etc/elc + 1 S 0 41 1 0 30 20 6433 16 114352 co 0:00 -sh + 1 R 0 49 41 6 50 20 10370 28 co 0:00 ps -xl + 201 S 0 33 1 0 40 20 5251 7 140000 ? 0:00 /etc/update + 201 S 0 37 1 0 40 20 7017 13 140000 ? 0:00 /etc/cron +vixen# w + 12:06pm up 1 user, load average: 0.00, 0.00, 0.00 +User tty login@ idle JCPU PCPU what +root console 12:06pm 1 w +vixen# mount +hp01 on /usr +hp04 on /user1 +vixen# df +Filesystem total kbytes kbytes percent + node kbytes used free used Mounted on +/dev/hp00 4606 3077 1529 67% / +/dev/hp01 9629 3594 6035 37% /usr +/dev/hp04 148244 2 148242 0% /user1 +vixen# set +HOME=/ +IFS= + +PATH=:/usr/ucb:/bin:/usr/bin:/etc +PS1=vixen# +PS2=> +SHELL=/bin/sh +TERM=dw3 +TZ=GMT0 +USER=root +vixen# cat /.profile +PS1=`hostname`'# ' +echo "erase = delete, kill = ^U, intr = ^C" +if test `tty` = /dev/console +then + stty dec prterase +else + stty crt tabs +fi +PATH=:/usr/ucb:/bin:/usr/bin:/etc +export PATH +vixen# ls /etc +accton fpsim init msf protocols termcap +arp fsdb ipatch mtab rawfs tss +catman fstab labelit networks rc ttys +cron getty log newfs rdate ttytype +cshprofile gettytab lpdrestart nu remote tzname +dcopy group lpset nu.cf route umount +ddate hosts mkfs nulib rx2fmt update +dmesg hosts.equiv mknod passwd services utmp +elc ifconfig motd printcap syslog.conf vipw +eli inetd.conf mount profile syslog.pid volcopy +vixen# ++ +
+BOOT> boot rp1 + +70Boot from xp(0,1,0) at 0176700 +Press <CR> to boot, or any other key to abort: 0 +: xp(0,1,0)unix +Boot: bootdev=05010 bootcsr=0176700 + +2.11 BSD UNIX #2: Oct 31 04:05:24 PST 1981 + root@Sat:/usr/src/sys/VIXEN + +phys mem = 3915776 +avail mem = 3684480 +user mem = 307200 + +hk ? csr 177440 vector 210 skipped: No CSR. +ht ? csr 172440 vector 224 skipped: No CSR. +ra ? csr 172150 vector 154 skipped: No CSR. +rl 0 csr 174400 vector 160 attached +tm ? csr 172520 vector 224 skipped: No CSR. +tms ? csr 174500 vector 260 skipped: No CSR. +ts ? csr 172520 vector 224 skipped: No CSR. +xp 0 csr 176700 vector 254 attached +Automatic reboot in progress... +Sat Oct 31 04:28:59 PST 1981 +Sat Oct 31 04:28:59 PST 1981 +checking quotas: done. +Assuming non-networking system ... +checking for core dump... +preserving editor files +clearing /tmp +standard daemons: update cron accounting. +starting lpd +starting local daemons: sendmail. +Sat Oct 31 04:29:02 PST 1981 + +2.11 BSD UNIX (vixen.2bsd.com) (console) + +login: root +erase, kill ^U, intr ^C +# uname +2.11BSD +# ps -al + F S UID PID PPID CPU PRI NICE ADDR SZ WCHAN TTY TIME COMMAND + 1 R 0 80 75 3 50 0 21600 59 co 0:00 ps -al +# cat /etc/passwd +root:*:0:1:The Man:/:/bin/sh +daemon:*:1:1:The devil himself:/:/bin/sh +sys:*:4:2:Operating System:/tmp:nologin +operator:*:2:5:System &:/usr/guest/operator:/bin/csh +bin:*:3:20:Binaries Commands and Source:/:/bin/csh +nobody:*:32767:31:Nobody:/nonexistent:/bin/sh +uucp:*:66:1:UNIX-to-UNIX Copy:/usr/spool/uucppublic:/usr/sbin/uucico +ingres:*:39:74:INGRES Owner:/usr/ingres:/bin/csh +# ls -al /sys/conf +total 147 +drwxr-xr-x 5 root 512 Mar 31 13:55 . +drwxr-xr-x 23 root 512 Mar 31 15:45 .. +-r--r--r-- 1 root 238 Dec 27 1986 :comm-to-bss +... +# cat > hello.c +#include <stdio.h> +main() +{ + printf("Hello world\n"); +} +CTRL/D +# cc hello.c +# ls -al hello* a.out +-rwxr-x--x 1 root 5335 Mar 31 15:52 a.out +-rw-r----- 1 root 59 Mar 31 15:52 hello.c +# ./a.out +Hello world +# cd /sys/VIXEN +# make +make -f Make.sys I=/usr/include H=../h M=../machine AS="/bin/as -V" ..... +cc -O -DKERNEL -DVIXEN -DFPSIM -DSOFUB_MAP -I. -I../h -S ../sys/init_main.c +/bin/sed -f SPLFIX init_main.s | /bin/as -V -u -o init_main.o +rm -f init_main.s +cc -O -DKERNEL -DVIXEN -DFPSIM -DSOFUB_MAP -I. -I../h -S ../sys/init_sysent.c +/bin/sed -f SPLFIX init_sysent.s | /bin/as -V -u -o init_sysent.o +rm -f init_sysent.s +cc -O -DKERNEL -DVIXEN -DFPSIM -DSOFUB_MAP -I. -I../h -S ../sys/kern_acct.c +/bin/sed -f SPLFIX kern_acct.s | /bin/as -V -u -o kern_acct.o +rm -f kern_acct.s +cc -O -DKERNEL -DVIXEN -DFPSIM -DSOFUB_MAP -I. -I../h -S ../sys/kern_clock.c +/bin/sed -f SPLFIX kern_clock.s | /bin/as -V -u -o kern_clock.o +rm -f kern_clock.s +... +size unix +text data bss dec hex +50624 7792 23708 82124 140cc total text: 118272 + overlays: 7680,7360,7680,7488,7488,7744,5632,6144,7680,2752 +Compacting symbol table +symcompact unix +symcompact: 209 symbols removed +Compacting strings table +strcompact unix +rearranging symbols +symorder ../pdp/symbols.sort unix +./checksys unix +System will occupy 210528 bytes of memory (including buffers and clists). + + end {0075414} nbuf {0017116} buf {0044360} + nproc {0017104} proc {0061314} ntext {0017106} + text {0074354} nfile {0017112} file {0071720} + ninode {0017110} inode {0017200} ncallout {0017114} + callout {0035764} ucb_clist {0017122} nclist {0017120} + ram_size {0000000} xitdesc {0017176} quotdesc {0000000} + namecache {0036504} _iosize {0010004} nlog {0016156} +# make install +install -c -o root -g kmem -m 744 unix /unix +# ps -aux +USER PID NICE SZ TTY TIME COMMAND +root 0 0 8 ? 0:00 swapper +root 1 0 29 ? 0:00 (init) +root 42 0 11 ? 0:00 update +root 45 0 51 ? 0:00 cron +root 49 -1 26 ? 0:00 acctd +root 55 0 47 ? 0:00 /usr/sbin/lpd +root 75 0 19 co 0:00 -sh +root 884 0 59 co 0:00 ps -aux +# ++ + + +
+BOOT> boot rp2 + +RSTS V9.6-11 RSTS (DB2) INIT V9.6-11 + +Today's date? 31-OCT-86 + +Current time? 11:12 + +Start timesharing? <Yes> + +Default memory allocation table shows LESS +memory than INIT detects on this machine. + +Adjusting memory table. + + Memory allocation table: + + 0K: 00000000 - 00433777 ( 71K) : EXEC + 71K: 00434000 - 15163777 (1622K) : USER + 1693K: 15164000 - 16737777 ( 219K) : XBUF + +Memory available to RSTS/E is 1912K words. + +86.10.31 11:12 + +1 device disabled + +Proceed with system startup? <YES> + + Beginning RSTS/E system startup... +86.10.31 11:12 Installing monitor overlays +86.10.31 11:12 Mounting disks +86.10.31 11:12 Assigning logical names +86.10.31 11:12 Starting error logging +86.10.31 11:12 Setting system characteristics +31-Oct-86 11:12 AM Installing run-time systems and libraries +31-Oct-86 11:12 AM Setting terminal characteristics +31-Oct-86 11:12 AM Defining system commands +31-Oct-86 11:12 AM Setting printer characteristics +31-Oct-86 11:12 AM Starting spoolers + +*** From [1,2] on KB0: at 11:12 AM 31-Oct-86 +** RSTS/E is on the air... + + + +I11/70 + +$ systat + +RSTS V9.6-11 RSTS/E V9.6 status at 31-Oct-86, 11:14 AM Up: 2:45 + +Job Who Where What Size State Run-Time RTS + 1 1,2 Det ERRCPY 5K SR 0.8 ...RSX + 2 11,70 KB0 SYSTAT 16K RN Lck 0.4 ...RSX + 3 1,2 Det PBS... 19K SL 0.0 ...RSX + +Busy Devices: None + +Disk Structure: +Dsk Open Size Free Clu Err Name Level Comments +DB2 18 171796 119452 69% 4 0 VIXEN 1.2 Pub, DLW + +General FIP Hung +Buffers Buffers Jobs/Jobmax TTY's Errors + 741 461 3/63 0 0 + +Run-Time Systems: + Name Typ Dev Size Users Comments +...RSX TSK 0(32)K 3 Monitor, KBM +DCL COM DB2: 24(8)K 0 Temp, Addr:71, DF KBM +RT11 SAV DB2: 4(28)K 0 Temp, Addr:108, KBM, CSZ, EMT:255 +BASIC BAC DB2: 16(16)K 0 Temp, Addr:166, KBM, CSZ +TECO TEC DB2: 10(20)K 0 Non-Res, KBM + +Resident Libraries: + Name Prot Acct Size Users Comments +CSPLIB < 42> DB2:[ 0,1 ] 8K 2 Temp, Addr:100 +EDT < 42> DB2:[ 0,11 ] 39K 0 Non-Res +RMSRES < 42> DB2:[ 0,10 ] 4K 1 Temp, Addr:1689 +RMSLBA < 42> DB2:[ 0,10 ] 4K 1 Temp, Addr:139 +RMSLBB < 42> DB2:[ 0,10 ] 3K 1 Temp, Addr:132 +RMSLBC < 42> DB2:[ 0,10 ] 3K 1 Non-Res +RMSLBD < 42> DB2:[ 0,10 ] 2K 1 Temp, Addr:143 +RMSLBE < 42> DB2:[ 0,10 ] 3K 1 Temp, Addr:125 +RMSLBF < 42> DB2:[ 0,10 ] 4K 1 Temp, Addr:128 +DAPRES < 42> DB2:[ 0,10 ] 10K 0 Non-Res, Addr:1679 + +Message Receivers: +Rcvrid Job Rib Obj Msgs/Max Links/InMax/OutMax Access +ERRLOG 1 0 1 0/40 0/0/0 Prv +QM$CMD 3 1 3 0/20 0/0/255 Prv +QM$SRV 3 2 4 0/30 0/0/255 Prv +QM$URP 3 3 5 0/10 0/0/255 Lcl +PR$03A 3 17 65 0/5 0/0/255 Prv +PR$03B 3 25 65 0/5 0/0/255 Prv +BA$03A 3 41 66 0/5 0/0/255 Prv +BA$03B 3 49 66 0/5 0/0/255 Prv +BA$03C 3 57 66 0/5 0/0/255 Prv +$ help + +You can obtain on-line information about any DCL command or qualifier, as +well as many other general topics. For more complete details about a +topic, refer to the appropriate RSTS manual or guide. + +The RSTS/E System User's Guide contains descriptions of the DCL commands +and qualifiers that you use in file, system, and programming operations. + +The RSTS/E System Manager's Guide contains descriptions of the DCL +commands and qualifiers used in system management operations. + +See the RSTS/E Quick Reference Guide for the syntax of all DCL commands +and qualifiers on RSTS/E. + +For instructions on how to use this HELP facility, type HELP HELP from +DCL, or type HELP in response to the HELP Topic? prompt. + + +Additional help is available on: + +@ Accounts Advanced ALLOCATE APPEND +ASSIGN ATTACH BACKUP BASIC BROADCAST +BYE CCL CLOSE COBOL COPY +CREATE Dates DCL DEALLOCATE DEASSIGN +DEFINE DELETE DETACH DIBOL DIFFERENCES +DIRECTORY DISMOUNT DUMP EDIT Entries +EOD EXIT Expressions Files FORCE +Forms FORTRAN Functions GOSUB GOTO +HANGUP HELP IF INITIALIZE INQUIRE +INSTALL Keys Labels LINK LOAD +LOGIN LOGOUT MACRO MAIL MERGE +MOUNT ON OPEN Operators Passwords +PRINT Privileges Programs Queues Quotas +READ REMOVE RENAME REQUEST RESTORE +RETURN RT11 RSX RUN Runtime Systems +Servers SET SHOW SORT START +STOP SUBMIT Symbols Times TYPE +UNLOAD WRITE + +Topic? ^Z + +$ set term/width:80 +$ dir + + Name .Typ Size Prot Name .Typ Size Prot SY:[11,70] +ACEY .BAS 5 < 60> TREK .BAS 16 < 60> +TREK .DOC 9 < 60> ANIMAL.BAS 5 < 60> +STRTRK.BAS 27 < 60> STRTR1.BAS 9 < 60> +ADVENT.DOC 4 < 60> ADVENT.SAV 93 <124> +ADVENT.VAR 22 < 60> ADVTXT.TXT 125 < 60> +SYSMAC.SML 42 < 60> HELLO .MAC 1 < 60> +BOOT .MAC 24 < 60> + +Total of 382 blocks in 13 files in SY:[11,70] + +$ run advent + +WELCOME TO ADVENTURE!! WOULD YOU LIKE INSTRUCTIONS? + +yes +SOMEWHERE NEARBY IS COLOSSAL CAVE, WHERE OTHERS HAVE FOUND FORTUNES IN +TREASURE AND GOLD, THOUGH IT IS RUMORED THAT SOME WHO ENTER ARE NEVER +SEEN AGAIN. MAGIC IS SAID TO WORK IN THE CAVE. I WILL BE YOUR EYES +AND HANDS. DIRECT ME WITH COMMANDS OF 1 OR 2 WORDS. I SHOULD WARN +YOU THAT I LOOK AT ONLY THE FIRST FOUR LETTERS OF EACH WORD, SO YOU'LL +HAVE TO ENTER "NORTHEAST" AS "NE" TO DISTINGUISH IT FROM "NORTH". +(SHOULD YOU GET STUCK, TYPE "HELP" FOR SOME GENERAL HINTS. FOR INFOR- +MATION ON HOW TO END YOUR ADVENTURE, ETC., TYPE "INFO".) + - - - +THIS PROGRAM WAS ORIGINALLY DEVELOPED BY WILLIE CROWTHER. MOST OF THE +FEATURES OF THE CURRENT PROGRAM WERE ADDED BY DON WOODS (DON @ SU-AI). +THE CURRENT VERSION WAS DONE BY MIKE WESTON. + +YOU ARE STANDING AT THE END OF A ROAD BEFORE A SMALL BRICK BUILDING. +AROUND YOU IS A FOREST. A SMALL STREAM FLOWS OUT OF THE BUILDING AND +DOWN A GULLY. + +quit +DO YOU REALLY WANT TO QUIT NOW? + +yes +OK + +YOU SCORED 27 OUT OF A POSSIBLE 350, USING 8 TURNS. + +YOU ARE OBVIOUSLY A RANK AMATEUR. BETTER LUCK NEXT TIME. + +TO ACHIEVE THE NEXT HIGHER RATING, YOU NEED 9 MORE POINTS. +$ run $switch +Keyboard Monitor to switch to? basic + +Ready + +run acey + ACEY DUCEY CARD GAME + CREATIVE COMPUTING MORRISTOWN, NEW JERSEY + + +ACEY-DUCEY IS PLAYED IN THE FOLLOWING MANNER +THE DEALER (COMPUTER) DEALS TWO CARDS FACE UP +YOU HAVE AN OPTION TO BET OR NOT BET DEPENDING +ON WHETHER OR NOT YOU FEEL THE CARD WILL HAVE +A VALUE BETWEEN THE FIRST TWO. +IF YOU DO NOT WANT TO BET, INPUT A 0 +YOU NOW HAVE 100 DOLLARS. + +HERE ARE YOUR NEXT TWO CARDS: + 4 + 5 + +WHAT IS YOUR BET? 1 + 9 +SORRY, YOU LOSE +YOU NOW HAVE 99 DOLLARS. + +HERE ARE YOUR NEXT TWO CARDS: + 3 +QUEEN + +WHAT IS YOUR BET? 1 +QUEEN +SORRY, YOU LOSE +YOU NOW HAVE 98 DOLLARS. + +HERE ARE YOUR NEXT TWO CARDS: + 7 +JACK + +WHAT IS YOUR BET? ^C + +Ready + +bye +Saved all disk files on SY: 416 blocks in use +Job 2 User 11,70 logged off KB0: at 31-Oct-86 11:14 AM +System RSTS V9.6-11 RSTS/E V9.6 +Run time was 3.4 seconds +Elapsed time was 2 minutes +Good morning + ++ +
+PAUL NANKERVIS - PAULNANK@HOTMAIL.COM + +BOOT> boot rp3 + + + RSX-11M V4.6 BL56 1912.K MAPPED +>RED DB3:=SY: +>RED DB3:=LB: +>MOU DB3:RSXM56 +>@DB3:[1,2]STARTUP +>* PLEASE ENTER TIME AND DATE (HR:MN DD-MMM-YY) [S]: 11:12 31-OCT-76 +>TIM 11:12 31-OCT-76 +>* ENTER LINE WIDTH OF THIS TERMINAL [D D:132.]: 80 +>SET /BUF=TI:80. +>ACS SY:/BLKS=1024. +>; +>; This system startup command file (LB:[1,2]STARTUP.CMD) contains a +>; template of commands to initialize the queue print spooler and queue +>; LP0:, initialize the error logger, initialize the DCL CLI, and install +>; the RMS Library and Utilities. As is these commands are commented out +>; and are not executed. To include these commands as part of the +>; startup procedure, edit the file to remove the period and semi-colon +>; (.;) comment delimiter from the beginning of each line. These +>; commands may be useful for initializing the various facilities for +>; your installation or else they may provide a model with which to +>; tailor initialization commands for your particular installation. +>; +>ELI /LOG +11:12:04 ERRLOG -- Error Logging initialized +>CLI /INIT=DCL/TASK=...DCL +>INS LB:[1,54]PIP.TSK +>INS LB:[1,54]EDT.TSK +>INS LB:[1,54]TKB.TSK +>INS LB:[1,54]MAC.TSK +>INS LB:[1,54]BRU.TSK +>@ <EOF> +>PIP [200,1]/LI + + +Directory DB3:[200,1] +31-OCT-76 11:12 + +GSA.MAC;1 19. 03-JAN-90 17:07 +SEARCH.MAC;1 10. 03-JAN-90 17:07 +RENAME.MAC;1 12. 03-JAN-90 17:07 +ERASE.MAC;1 10. 03-JAN-90 17:07 +PARSE.MAC;1 11. 03-JAN-90 17:07 +SEARCH.TSK;1 26. C 03-JAN-90 17:07 +RENAME.TSK;1 26. C 03-JAN-90 17:07 +ERASE.TSK;1 25. C 03-JAN-90 17:07 +PARSE.TSK;1 22. C 03-JAN-90 17:07 +INTRO.ULB;1 199. 31-OCT-76 06:50 +INTROFIL.CMD;1 2. 31-OCT-76 06:50 +CLEAN.CMD;1 1. 31-OCT-76 06:50 +CLKGEN.CMD;1 8. 31-OCT-76 06:50 +DELETE.CMD;1 1. 31-OCT-76 06:50 +LOGIN.CMD;1 1. 31-OCT-76 06:50 +MYDISK.CMD;1 4. 31-OCT-76 06:50 +SHAVE.CMD;1 1. 31-OCT-76 06:50 +SHOW.CMD;1 1. 31-OCT-76 06:50 +CLOCK.MAC;1 47. 31-OCT-76 06:50 +HIYA.MAC;1 8. 31-OCT-76 06:50 +STARS.MAC;1 2. 31-OCT-76 06:50 +TMCLI.MAC;1 22. 31-OCT-76 06:50 +TMCLI.FTN;1 22. 31-OCT-76 06:50 +ERROR.TSK;1 4. C 31-OCT-76 06:50 +SEVERE.TSK;1 4. C 31-OCT-76 06:50 +SUCCESS.TSK;1 4. C 31-OCT-76 06:50 +WARNING.TSK;1 4. C 31-OCT-76 06:50 +FLU.TXT;1 1. 31-OCT-76 06:50 +FLY.TXT;1 1. 31-OCT-76 06:50 +FLY.TXT;2 1. 31-OCT-76 06:50 +FLY.TXT;3 1. 31-OCT-76 06:50 +HELLO.TXT;1 2. 31-OCT-76 06:50 +LONG.TXT;1 25. 31-OCT-76 06:50 +WHATSHERE.TXT;1 6. 31-OCT-76 06:50 + +Total of 533./533. blocks in 34. files + +>HELP + + Help is available for many RSX-11M commands and utilities. + + For help in logging into the system, type HELP HELLO or HELP + LOGIN. You'll need a user-ID and password to log in. Ask your + system manager. + + RSX-11M systems have two major command languages or CLIs: MCR + and DCL. Once you log in, your terminal is set to either + MCR or DCL. (All terminals are set to MCR prior to logging in.) + + The general forms of the HELP command are: + + >HELP[/cli] topic [subtopic[s]] + + >HELP commandname [switch] + + Once you are logged in, you need not include the name of the CLI + to which your terminal is set. + + For information on what further help is available, type + HELP[/MCR] LIST (brackets indicate an optional command line + entry) or HELP/DCL. For a listing of help available on other + topics, type HELP[/MCR] MORE or HELP/DCL MORE. You need not + log in to get help. + +>TAS +LDR... 13.02 LDRPAR 248. 00002600 LB0:-00104402 FIXED +TKTN 05.00 SYSPAR 248. 00011700 LB0:-00110145 +...RMD 03.00 GEN 225. 00027200 LB0:-00112034 +F11MSG 13.00 GEN 200. 00005700 LB0:-00110164 +MTAACP 15.01 GEN 200. 00014700 LB0:-00111651 +...DMO 04.00 GEN 160. 00014600 LB0:-00107227 +MCR... 07.00 SYSPAR 160. 00011700 LB0:-00110050 +...DCL 5.04 GEN 160. 00051500 LB0:-00110525 +...MOU 27.01 GEN 160. 00037700 LB0:-00110402 +...MCR 07.00 GEN 160. 00020000 LB0:-00110211 +F11ACP 06.01 FCPPAR 149. 00024200 LB0:-00107323 +ERRLOG 2.00 GEN 148. 00040000 LB0:-00112507 +PMT... 2.00 GEN 148. 00006300 LB0:-00107503 +COT... 2.0 GEN 145. 00013600 LB0:-00107246 +PMD... 08.01 GEN 140. 00016200 LB0:-00111623 +SHF... 6.00 SYSPAR 105. 00011700 LB0:-00112174 +...INS 9.01 GEN 100. 00034600 LB0:-00107777 +...SAV 05.00 GEN 100. 00033300 LB0:-00111541 +...UFD 05.00 GEN 100. 00005700 LB0:-00110176 +QMG... 03.04 GEN 75. 00031700 LB0:-00110460 +PRT... 2.0 GEN 70. 00001100 LB0:-00110160 +LP0 06.00 GEN 70. 00014500 LB0:-00111437 +...ACS 3.00 GEN 70. 00005000 LB0:-00112500 +...BRU 11.03 GEN 70. 00173500 LB0:-00113217 +...EDT V03.17 GEN 65. 00145600 LB0:-00114674 +...AT. 9.0 GEN 64. 00060000 LB0:-00107575 +...QUE 05.01 GEN 50. 00020100 LB0:-00111244 +...PRI 05.01 GEN 50. 00020100 LB0:-00111244 +...BOO 06.02 GEN 50. 00022000 LB0:-00107166 +...ELI 1.00 GEN 50. 00017300 LB0:-00112553 +...MAG 03.00 GEN 50. 00031500 LB0:-00110077 +...LOA 04.02 GEN 50. 00032600 LB0:-00111777 +...HEL 04.00 GEN 50. 00024100 LB0:-00112354 +...BYE 07.00 GEN 50. 00012700 LB0:-00112312 +...BRO 07.00 GEN 50. 00030400 LB0:-00112421 +...UNL 4.02 GEN 50. 00024500 LB0:-00111406 +...PIP 18.03 GEN 50. 00040000 LB0:-00116314 +...TKB X43.00 GEN 50. 00070000 LB0:-00117002 +...MAC V05.05 GEN 50. 00070000 LB0:-00116130 +>BYE +Have a Good Morning +31-OCT-76 11:12 TT0: logged off VIXEN +>HELLO +Account or name: 11,70 +Password: PDP + +RSX-11M BL56 [1,54] System VIXEN +31-OCT-76 11:12 Logged on Terminal TT0: + +Good Morning + +>@LOGIN.CMD + + +Welcome to RSX-11M V4.6 time sharing + +This is a minimal system with a user account of 200,1 (no password) and 11,70 (p +assword of PDP) + +Hopefully it demonstrates how things were in the good old days + +Paul Nankervis +paulnank@hotmail.com +http://skn.noip.me/pdp11/pdp11.html + +>@ <EOF> +>DIR + + +Directory DB3:[11,70] +31-OCT-76 11:12 + +HELLO.MAC;1 1. 31-OCT-76 06:55 +LOGIN.CMD;1 1. 31-OCT-76 06:48 +LOGIN.CMD;2 1. 31-OCT-76 06:48 + +Total of 3./3. blocks in 3. files + +>EDIT HELLO.MAC + 1 ; HELLO WORLD IN ASSEMBLER FOR THE DEC PDP-11 WITH THE +*type whole + 1 ; HELLO WORLD IN ASSEMBLER FOR THE DEC PDP-11 WITH THE + 2 ; RSX-11M-PLUS OPERATING SYSTEM + 3 ; + 4 .TITLE HELLO + 5 .IDENT /V0001A/ + 6 .MCALL QIOW$S, EXIT$S + 7 .PSECT $CODE,RO,I + 8 START: QIOW$S #IO.WVB,#5,#2,,,,<#STR,#LEN,#40> + 9 EXIT$S + 10 .PSECT $DATA,RO,D + 11 STR: .ASCII / HELLO WORLD!/ + 12 LEN=.-STR + 13 .END START + 14 +[EOB] +*exit +DB3:[11,70]HELLO.MAC;2 14 lines +>MACRO HELLO +>LINK HELLO +>RUN HELLO + HELLO WORLD! +>LOGOUT +Have a Good Morning +31-OCT-76 11:13 TT0: logged off VIXEN +> ++ +
+Plenty! Especially in the places where I haven't managed to figure out what a real PDP 11/70 should do. :-( Core PDP 11/70 +stuff is well documented but some lesser used system functions require reverse engineering to understand. +
+If you have something you want me to look at let me know and I'll prioritise. However always happy to accept fixes! + +
home + + + + + + + + + + + diff --git a/pdp11.js b/pdp11.js new file mode 100755 index 0000000..41f0707 --- /dev/null +++ b/pdp11.js @@ -0,0 +1,2176 @@ +// Javascript PDP 11/70 Emulator v1.8 +// written by Paul Nankervis +// Please send suggestions, fixes and feedback to paulnank@hotmail.com +// I'm particularly interested in hearing from anyone with real experience on a PDP 11/70 front panel +// +// This code may be used freely provided the original author name is acknowledged in any modified source code +// +// http://skn.noip.me/pdp11/pdp11.html +// +// +// +// + + +const IOBASE_VIRT = 0160000, + IOBASE_18BIT = 0760000, + IOBASE_UNIBUS = 017000000, + IOBASE_22BIT = 017760000, + MAX_MEMORY = IOBASE_UNIBUS - 16384, // Maximum memory address (need less memory for BSD 2.9 boot) + MAX_ADDRESS = 020000000, // Special register addresses are above 22 bit addressing + BYTE_MODE = 1, // accessMode length of 1 and flag for byte addressing + READ_MODE = 16, + WRITE_MODE = 32, + READ_WORD = READ_MODE | 2, + READ_BYTE = READ_MODE | BYTE_MODE, + WRITE_WORD = WRITE_MODE | 2, + WRITE_BYTE = WRITE_MODE | BYTE_MODE, + MODIFY_WORD = READ_MODE | WRITE_MODE | 2, + MODIFY_BYTE = READ_MODE | WRITE_MODE | BYTE_MODE; + + +var STATE = { + RUN: 0, + RESET: 1, + WAIT: 2, + HALT: 3 +}; // CPU.runState is either STATE.RUN, STATE.WAIT or STATE.HALT + +var CPU = { + controlReg: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], // various control registers we don't really care about + CPU_Error: 0, + cpuType: 70, + displayAddress: 0, // Address display for console operations + displayPhysical: 0, // Physical address display for console operations + displayRegister: 0, // Console display lights register + flagC: 0x10000, // PSW C bit + flagN: 0x8000, // PSW N bit + flagV: 0x8000, // PSW V bit + flagZ: 0xffff, // ~ PSW Z bit + memory: [], // Main memory (in words) + MMR0: 0, // MMU control registers + MMR1: 0, + MMR2: 0, + MMR3: 0, + MMR3Mask: [7, 7, 7, 7], // I&D page mask by mode from KSU bits in MMR3 + mmuEnable: 0, // MMU enable mask for READ_MODE or WRITE_MODE + mmuLastPage: 0, // last used MMU page for MMR0 - 2 bits of mode and 4 bits of I/D page - used as an index into PAR/PDR + mmuMode: 0, // current memory management mode (0=kernel,1=super,2=undefined,3=user) + mmuPAR: [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //0 kernel + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //1 super + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //2 illegal + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 //3 user + ], // memory management PAR registers by mode + mmuPDR: [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //0 kernel + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //1 super + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, // 2 illegal + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 //3 user + ], // memory management PDR registers by mode + PIR: 0, // Programmable interrupt register + priorityReview: 1, // flag to mark if we need to check priority change + PSW: 0xf, // PSW less flags C, N, V & Z + registerAlt: [0, 0, 0, 0, 0, 0], // Alternate registers R0 - R5 + registerVal: [0, 0, 0, 0, 0, 0, 0, 0], // Current registers R0 - R7 + stackLimit: 0xff, // stack overflow limit + stackPointer: [0, 0, 0, 0], // Alternate R6 (kernel, super, illegal, user) + switchRegister: 0, // console switch register + trapMask: 0, // Mask of traps to be taken at the end of the current instruction + trapPSW: -1, // PSW when first trap invoked - for tacking double traps + unibusMap: [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + ], // 32 unibus map registers + runState: -1, // current machine state defined in STATE + interruptQueue: [], // List of interrupts pending +}; + + +var log = { + depth: 64, // 256, + extras: 0, + history: [] +}; + + +function LOG_INSTRUCTION(instruction, format, name) { + if (log.depth > 0) { + if (log.history.length > log.depth + 4) { + ////LOG_PRINT(); + //log.history = []; + log.history.splice(0, 5); + } + log.history.push([readPSW(), CPU.registerVal[7], instruction, format, name, -1, -1]); + } +} + + +function LOG_SOURCE(source) { + if (log.depth > 0) { + if (log.history.length > 0) { + if (log.history[log.history.length - 1][5] < 0) { + log.history[log.history.length - 1][5] = source; + } else { + log.history[log.history.length - 1][6] = source; + } + } + } +} + + +function LOG_ADDRESS(address) { + if (log.depth > 0) { + if (log.history.length > 0) { + log.history[log.history.length - 1].push(address); + } + } +} + + +function LOG_OPERAND(operand, history, index) { + var result = "R" + (operand & 7).toString(); + switch ((operand >> 3) & 7) { + case 1: + result = "(" + result + ")"; + break; + case 2: + if ((operand & 7) == 7 && history[5 + index] >= 0) { + result = "#" + history[5 + index].toString(8); + } else { + result = "(" + result + ")+"; + } + break; + case 3: + if ((operand & 7) == 7) { + result = "@#" + history[log.extras++].toString(8); + } else { + result = "@" + result; + } + break; + case 4: + result = "-(" + result + ")"; + break; + case 5: + result = "@-(" + result + ")"; + break; + case 6: + if ((operand & 7) == 7) { + result = history[log.extras++].toString(8); + } else { + result = history[log.extras++].toString(8) + "(" + result + ")"; + } + break; + case 7: + if ((operand & 7) == 7) { + result = "@" + history[log.extras++].toString(8); + } else { + result = "@" + history[log.extras++].toString(8) + "(" + result + ")"; + } + break; + } + return result; +} + + +function LOG_OCTAL(n) { + n = n.toString(8); + return "000000".substr(n.length) + n; +} + + +function LOG_PRINT() { + var i, result, psw, pc, instruction, name, historyRec; + for (i = 0; i < log.history.length; i++) { + log.extras = 7; + historyRec = log.history[i]; + psw = historyRec[0]; + pc = (historyRec[1] - 2) & 0xffff; + instruction = historyRec[2]; + name = historyRec[4]; + result = LOG_OCTAL(psw) + " " + LOG_OCTAL(pc) + " " + name; + switch (historyRec[3]) { + case 0: // No operands - just print name + break; + case 1: // One operand instructions + result += " " + LOG_OPERAND(instruction, historyRec, 0); + break; + case 2: // Two operand instructions + result += " " + LOG_OPERAND(instruction >> 6, historyRec, 0) + "," + LOG_OPERAND(instruction, historyRec, 1); + break; + case 3: // Instruction with a register and a normal operand + result += " " + LOG_OPERAND((instruction >> 6) & 7, [], 0) + "," + LOG_OPERAND(instruction, historyRec, 0); + break; + case 4: // Branch instructions + result += " " + branch(historyRec[1], instruction).toString(8); + break; + case 5: // SOB instruction + result += " " + LOG_OPERAND((instruction >> 6) & 7, [], 0) + "," + (historyRec[1] - (((instruction & 0x3f) << 1)) & 0xffff).toString(8); + break; + case 6: // Single register instruction (RTS) + result += " " + LOG_OPERAND(instruction & 7, [], 0); + break; + case 7: // Eight bit number instruction (EMT & TRAP) + result += " " + (instruction & 0xff).toString(8); + break; + case 8: // Six bit number instruction (MARK) + result += " " + (instruction & 0x3f).toString(8); + break; + case 9: // Three bit number instruction (SPL) + result += " " + (instruction & 0x7).toString(8); + break; + case 10: // Set and clear CC Instructions + break; + case 11: // Specials - not actually instructions + result += " " + instruction.toString(8); + break; + default: + result = "bugger"; + break; + } + if (historyRec[5] >= 0) { + result += " ; " + historyRec[5].toString(8); + if (historyRec[6] >= 0) { + result += " " + historyRec[6].toString(8); + } + } + console.log(result); + } + log.history = []; +} + + +// Interrupts are stored in a queue in delay order with the delay expressed as +// a difference. For example if the delays were 0, 1, 0 then the first entry +// is active and both the second and third are waiting for one more instruction +// cycle to become active. +// If the current runState is WAIT then skip any delay and go to RUN. + +function interrupt(cleanFlag, delay, priority, vector, callback, callarg) { + "use strict"; + var i = CPU.interruptQueue.length; + if (typeof callback == "undefined") { + callback = null; + } + if (cleanFlag) { + while (i-- > 0) { + if (CPU.interruptQueue[i].vector == vector) { + if (i > 0) { + CPU.interruptQueue[i - 1].delay += CPU.interruptQueue[i].delay; + } + CPU.interruptQueue.splice(i, 1); + break; + } + } + } + if (delay >= 0) { // delay below 0 is just used to remove vector from queue + if (CPU.runState == STATE.WAIT) { // if currently in wait then resume + delay = 0; + CPU.runState = STATE.RUN; + if (!panel.halt) { + setTimeout(emulate, 0); + } + } + i = CPU.interruptQueue.length; // queue in delay 'difference' order + while (i-- > 0) { + if (CPU.interruptQueue[i].delay > delay) { + CPU.interruptQueue[i].delay -= delay; + break; + } + delay -= CPU.interruptQueue[i].delay; + } + CPU.interruptQueue.splice(i + 1, 0, { + "delay": delay, + "priority": priority & 0xe0, + "vector": vector, + "callback": callback, + "callarg": callarg + }); + if (delay > 0 || (priority & 0xe0) > (CPU.PSW & 0xe0)) { + CPU.priorityReview = 1; // Schedule an interrupt priority review if required + } + } +} + + +// When a wait instruction is executed do a search through the interrupt list +// to see if we can run something (anything!) which has been delayed. If there is +// something then we don't actually need to enter WAIT state. + +function interruptWaitRelease() { + "use strict"; + var savePSW, i; + savePSW = CPU.PSW & 0xe0; + i = CPU.interruptQueue.length; + while (i-- > 0) { + CPU.interruptQueue[i].delay = 0; + if (CPU.interruptQueue[i].priority > (CPU.PSW & 0xe0)) { + CPU.priorityReview = 1; + return 1; // Found something that can run + } + } + return 0; // No candidates found for WAIT release +} + + +// When the PSW, PIR or interrupt queue state have changed then it is time to review +// the list of pending interrupts to see if we can invoke one. If nothing changes +// then more reviews are not required until something does change. +// Review controlled by the flag CPU.priorityReview + +function interruptReview() { + "use strict"; + var highPriority, high, i; + CPU.priorityReview = 0; + high = -1; + highPriority = CPU.PIR & 0xe0; + if ((i = CPU.interruptQueue.length) > 0) { + while (i-- > 0) { + if (CPU.interruptQueue[i].delay > 0) { + CPU.interruptQueue[i].delay--; + CPU.priorityReview = 1; + break; // Decrement only one delay 'difference' per cycle + } + if (CPU.interruptQueue[i].callback) { + if (!CPU.interruptQueue[i].callback(CPU.interruptQueue[i].callarg)) { + CPU.interruptQueue.splice(i, 1); + high--; + continue; + } + CPU.interruptQueue[i].callback = null; + } + if (CPU.interruptQueue[i].priority > highPriority) { + highPriority = CPU.interruptQueue[i].priority & 0xe0; + high = i; + } + } + } + if (highPriority > (CPU.PSW & 0xe0)) { // check if we found an interrupt + if (high < 0) { + trap(0xa0, 42); // PIR trap 240 + } else { + trap(CPU.interruptQueue[high].vector, 44); // BR trap + CPU.interruptQueue.splice(high, 1); + } + } +} + + +// writePSW() is used to update the CPU Processor Status Word. The PSW should generally +// be written through this routine so that changes can be tracked properly, for example +// the correct register set, the current memory management mode, etc. An exception is +// SPL which writes the priority directly. Note that that N, Z, V, and C flags are +// actually stored separately to the PSW (CPU.PSW) for performance reasons. +// +// CPU.PSW 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 +// CM | PM |RS| |PRIORITY| T| N| Z| V| C +// mode 0 kernel 1 super 2 illegal 3 user + +function writePSW(newPSW) { + "use strict"; + var i, temp; + CPU.flagN = newPSW << 12; + CPU.flagZ = (~newPSW) & 4; + CPU.flagV = newPSW << 14; + CPU.flagC = newPSW << 16; + if ((newPSW ^ CPU.PSW) & 0x800) { + for (i = 0; i < 6; i++) { + temp = CPU.registerVal[i]; + CPU.registerVal[i] = CPU.registerAlt[i]; + CPU.registerAlt[i] = temp; // swap the active register sets + } + } + CPU.mmuMode = (newPSW >> 14) & 3; + temp = (CPU.PSW >> 14) & 3; + if (CPU.mmuMode != temp) { + CPU.stackPointer[temp] = CPU.registerVal[6]; + CPU.registerVal[6] = CPU.stackPointer[CPU.mmuMode]; // swap to new mode SP + } + if ((newPSW & 0xe0) < (CPU.PSW & 0xe0)) { + CPU.priorityReview = 1; // trigger check of priority levels + } + CPU.PSW = newPSW; +} + + +// readPSW() reassembles the N, Z, V, and C flags back into the PSW (CPU.PSW) + +function readPSW() { + "use strict"; + CPU.PSW = (CPU.PSW & 0xf8f0) | ((CPU.flagN >> 12) & 8) | ((CPU.flagV >> 14) & 2) | ((CPU.flagC >> 16) & 1); + if (!(CPU.flagZ & 0xffff)) { + CPU.PSW |= 4; + } + return CPU.PSW; +} + + +// trap() handles all the trap/abort functions. It reads the trap vector from kernel +// D space, changes mode to reflect the new PSW and PC, and then pushes the old PSW and +// PC onto the new mode stack. trap() returns a -1 which is passed up through function +// calls to indicate that a trap/abort has occurred (suspend instruction processing) +// CPU.trapPSW records the first PSW for double trap handling. The special value of -2 +// allows console mode to propagate an abort without trapping to the new vector. + +function trap(vector, reason) { + "use strict"; + var newPC, newPSW, doubleTrap = 0; + if (CPU.trapPSW > -2) { + if (CPU.trapPSW < 0) { + CPU.trapMask = 0; // No other traps unless we cause one here + CPU.trapPSW = readPSW(); // Remember original PSW + } else { + if (!CPU.mmuMode) { + vector = 4; + doubleTrap = 1; + } + } + //LOG_INSTRUCTION(vector, 11, "-trap-"); + if (!(CPU.MMR0 & 0xe000)) { + CPU.MMR1 = 0xf6f6; + CPU.MMR2 = vector; + } + CPU.mmuMode = 0; // read from kernel D space + if ((newPC = readWordByVirtual(vector | 0x10000)) >= 0) { + if ((newPSW = readWordByVirtual(((vector + 2) & 0xffff) | 0x10000)) >= 0) { + writePSW((newPSW & 0xcfff) | ((CPU.trapPSW >> 2) & 0x3000)); // set new CPU.PSW with previous mode + if (doubleTrap) { + CPU.CPU_Error |= 4; + CPU.registerVal[6] = 4; + } + if (pushWord(CPU.trapPSW, doubleTrap) >= 0 && pushWord(CPU.registerVal[7], doubleTrap) >= 0) { + CPU.registerVal[7] = newPC; + } + } + } + CPU.trapPSW = -1; // reset flag that we have a trap within a trap + } + return -1; // signal that a trap has occurred +} + + +// mapVirtualToPhysical() does memory management. It converts a 17 bit I/D +// virtual address to a 22 bit physical address (Note: the eight pseudo addresses +// for handling registers are NOT known at this level - those exist only for +// higher level functions). 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 by having +// an enable mask (CPU.mmuEnable) which is tested against the operation access mask +// (accessMask). 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. +// Access bit mask values are READ_MODE and WRITE_MODE with the lower 4 bits giving +// the operand length; for autoincrement calculation and to indicate byte mode access. +// +// As an aside it turns out that it is the memory management unit that does odd address +// and non-existant memory trapping: who knew? :-) I thought these would have been +// handled at access time. +// +// When doing mapping CPU.mmuMode is used to decide what address space is to be +// used. 0 = kernel, 1 = supervisor, 2 = illegal, 3 = user. Normally CPU.mmuMode is +// set by the writePSW() function but there are execptions for instructions which +// move data between address spaces (MFPD, MFPI, MTPD, and MTPI) and trap(). These will +// modify CPU.mmuMode outside of writePSW() and then restore it again if all worked. If +// however something happens to cause a trap then no restore is done as writePSW() +// will have been invoked as part of the trap, which will resynchronise CPU.mmuMode +// +// A PDP 11/70 is different to other PDP 11's in that the highest 18 bit space (017000000 +// & above) map directly to unibus space - including low memory. This doesn't appear to +// be particularly useful as it restricts maximum system memory - although it does +// allow software testing of the unibus map. This feature also appears to confuse some +// OSes which test consequetive memory locations to find maximum memory - and on a full +// memory system find themselves accessing low memory again at high addresses. +// +// 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 MMR0 +//nonr leng read trap unus unus ena mnt cmp -mode- i/d --page-- enable +// +// Map a 17 bit I/D virtual address to a 22 bit physical address + +function mapVirtualToPhysical(virtualAddress, accessMask) { + "use strict"; + var page, pdr, pdrUpdate, physicalAddress, errorMask; + //var CPU = window.CPU; + //if (virtualAddress & ~0x1ffff) panic(89); // check VA range + //if (!accessMask) panic(93); // Must have READ_MODE or WRITE_MODE + CPU.displayAddress = virtualAddress; // Remember the virtual address + if (!(accessMask & CPU.mmuEnable)) { // This access does not require the MMU + physicalAddress = virtualAddress & 0xffff; // virtual address without MMU is 16 bit (no I&D) + if (physicalAddress >= IOBASE_VIRT) { + physicalAddress |= IOBASE_22BIT; + } else { // no max_memory check in 16 bit mode + if ((physicalAddress & 1) && !(accessMask & BYTE_MODE)) { + CPU.CPU_Error |= 0x40; + return trap(4, 22); + } + } + return physicalAddress; + } else { // This access is mapped by the MMU + page = ((virtualAddress >> 13) & CPU.MMR3Mask[CPU.mmuMode]) | (CPU.mmuMode << 4); // Determine PDR/PAR page index using mode and I&D + physicalAddress = ((CPU.mmuPAR[page] << 6) + (virtualAddress & 0x1fff)) & 0x3fffff; + if (!(CPU.MMR3 & 0x10)) { // 18 bit mapping needs extra trimming + physicalAddress &= 0x3ffff; + if (physicalAddress >= IOBASE_18BIT) { + physicalAddress |= IOBASE_22BIT; + } + } + if (physicalAddress < MAX_MEMORY) { // Ordinary memory space only needs an odd address check + if ((physicalAddress & 1) && !(accessMask & BYTE_MODE)) { + CPU.CPU_Error |= 0x40; + return trap(4, 26); + } + CPU.mmuLastPage = page; + } else { // Higher addresses may require unibus mapping and a check if non-existant + if (physicalAddress < IOBASE_22BIT) { + if (physicalAddress >= IOBASE_UNIBUS) { + physicalAddress = mapUnibus(physicalAddress & 0x3ffff); // 18bit unibus space + if (physicalAddress >= MAX_MEMORY && physicalAddress < IOBASE_22BIT) { + CPU.CPU_Error |= 0x10; // Unibus timeout + return trap(4, 24); // KB11-EM does this after ABORT handling - KB11-CM before + } + } else { + CPU.CPU_Error |= 0x20; // Non-existant main memory + return trap(4, 24); // + } + } + if ((physicalAddress != 0x3fff7a) || CPU.mmuMode) { // MMR0 is 017777572 and doesn't affect MMR0 bits + CPU.mmuLastPage = page; + } + } + errorMask = 0; + pdr = CPU.mmuPDR[page]; + switch (pdr & 0x7) { // Check the Access Control Field (ACF) - really a page type + case 1: // read-only with trap + errorMask = 0x1000; // MMU trap - then fall thru + case 2: // read-only + pdrUpdate = pdr | 0x80; // Set A bit + if (accessMask & WRITE_MODE) { + errorMask = 0x2000; // read-only abort + } + break; + case 4: // read-write with read-write trap + errorMask = 0x1000; // MMU trap - then fall thru + case 5: // read-write with write trap + if (accessMask & WRITE_MODE) { + errorMask = 0x1000; // MMU trap - then fall thru + } + case 6: // read-write + pdrUpdate = pdr | ((accessMask & WRITE_MODE) ? 0xc0 : 0x80); // Set A & W bits + break; + default: + pdrUpdate = pdr; + errorMask = 0x8000; // non-resident abort + break; + } + if (pdrUpdate != pdr) { + CPU.mmuPDR[page] = pdrUpdate; + } + if ((pdr & 0x7f08) != 0x7f00) { // Skip page length check for most common case (hopefully) + if (pdr & 0x8) { // Page expands downwards + if (pdr & 0x7f00) { + if ((virtualAddress & 0x1fc0) < ((pdr >> 2) & 0x1fc0)) { + errorMask |= 0x4000; // page length error abort + } + } + } else { // Page expand upwards + if ((virtualAddress & 0x1fc0) > ((pdr >> 2) & 0x1fc0)) { + errorMask |= 0x4000; // page length error abort + } + } + } + // aborts and traps: log FIRST trap and MOST RECENT abort + if (errorMask) { + if (errorMask & 0xe000) { + if (CPU.trapPSW >= 0) { + errorMask |= 0x80; // Instruction complete + } + if (!(CPU.MMR0 & 0xe000)) { + CPU.MMR0 |= errorMask | (CPU.mmuLastPage << 1); + } + return trap(0xa8, 28); // 0250 + } + if (!(CPU.MMR0 & 0xf000)) { + if (physicalAddress < 0x3ff480 || physicalAddress > 0x3fffbf) { // 017772200 - 017777677 + CPU.MMR0 |= 0x1000; // MMU trap flag + if (CPU.MMR0 & 0x0200) { + CPU.trapMask |= 2; // MMU trap + } + } + } + } + return (CPU.displayPhysical = physicalAddress); + } +} + + +function readWordByAddr(physicalAddress) { + "use strict"; + if (physicalAddress >= MAX_ADDRESS) { + return CPU.registerVal[physicalAddress - MAX_ADDRESS]; + } else { + if (physicalAddress >= IOBASE_UNIBUS) { + return access_iopage(physicalAddress, -1, 0); + } else { + if (physicalAddress >= 0) { + return CPU.memory[physicalAddress >> 1]; + } + } + } + return physicalAddress; +} + + +function writeWordByAddr(physicalAddress, data) { + "use strict"; + data &= 0xffff; + if (physicalAddress >= MAX_ADDRESS) { + return (CPU.registerVal[physicalAddress - MAX_ADDRESS] = data); + } else { + if (physicalAddress >= IOBASE_UNIBUS) { + return access_iopage(physicalAddress, data, 0); + } else { + if (physicalAddress >= 0) { + return (CPU.memory[physicalAddress >> 1] = data); + } + } + } + return physicalAddress; +} + + +function readByteByAddr(physicalAddress) { + "use strict"; + var result; + if (physicalAddress >= MAX_ADDRESS) { + return (CPU.registerVal[physicalAddress - MAX_ADDRESS] & 0xff); + } else { + if (physicalAddress >= IOBASE_UNIBUS) { + return access_iopage(physicalAddress, -1, 1); + } else { + if (physicalAddress >= 0) { + result = CPU.memory[physicalAddress >> 1]; + if (physicalAddress & 1) { + result = result >> 8; + } + return (result & 0xff); + } + } + } + return physicalAddress; +} + + +function writeByteByAddr(physicalAddress, data) { + "use strict"; + data &= 0xff; + if (physicalAddress >= MAX_ADDRESS) { + return (CPU.registerVal[physicalAddress - MAX_ADDRESS] = (CPU.registerVal[physicalAddress - MAX_ADDRESS] & 0xff00) | data); + } else { + if (physicalAddress >= IOBASE_UNIBUS) { + return access_iopage(physicalAddress, data, 1); + } else { + if (physicalAddress >= 0) { + if (physicalAddress & 1) { + return (CPU.memory[physicalAddress >> 1] = (data << 8) | (CPU.memory[physicalAddress >> 1] & 0xff)); + } else { + return (CPU.memory[physicalAddress >> 1] = (CPU.memory[physicalAddress >> 1] & 0xff00) | data); + } + } + } + } + return physicalAddress; +} + + +function readWordByVirtual(virtualAddress) { // input address is 17 bit (I&D) + "use strict"; + return readWordByAddr(mapVirtualToPhysical(virtualAddress, READ_MODE)); +} + + +function popWord() { + "use strict"; + var result; + if ((result = readWordByVirtual(CPU.registerVal[6] | 0x10000)) >= 0) { + CPU.registerVal[6] = (CPU.registerVal[6] + 2) & 0xffff; + } + return result; +} + +// Stack limit checks only occur for Kernel mode and are either a yellow warning trap +// after instruction completion, or a red abort which stops the current instruction. + +function stackCheck(virtualAddress) { + "use strict"; + if (!CPU.mmuMode) { // Kernel mode 0 checking only + if (virtualAddress <= CPU.stackLimit || virtualAddress >= 0xfffe) { + if (virtualAddress + 32 <= CPU.stackLimit || virtualAddress >= 0xfffe) { + CPU.CPU_Error |= 4; // Red stack + CPU.registerVal[6] = 4; + return trap(4, 38); + } + CPU.CPU_Error |= 8; // Yellow + CPU.trapMask |= 4; + } + } + return virtualAddress; +} + + +function pushWord(data, skipLimitCheck) { + "use strict"; + var physicalAddress, virtualAddress; + CPU.registerVal[6] = virtualAddress = (CPU.registerVal[6] - 2) & 0xffff; // BSD meeds SP updated before any fault :-( + if (!(CPU.MMR0 & 0xe000)) { + CPU.MMR1 = (CPU.MMR1 << 8) | 0xf6; + } + if (!skipLimitCheck) { + if ((virtualAddress = stackCheck(virtualAddress)) < 0) { + return virtualAddress; + } + } + if ((physicalAddress = mapVirtualToPhysical(virtualAddress | 0x10000, WRITE_MODE)) >= 0) { + return writeWordByAddr(physicalAddress, data); + } + return physicalAddress; +} + + +// getVirtualByMode() maps a six bit operand to a 17 bit I/D virtual address space. +// Instruction operands are six bits in length - three bits for the mode and three +// for the register. The 17th I/D bit in the resulting virtual address represents +// whether the reference is to Instruction space or Data space - which depends on +// combination of the mode and whether the register is the Program Counter (register 7). +// +// The eight addressing modes are:- +// 0 R no valid virtual address +// 1 (R) operand from I/D depending if R = 7 +// 2 (R)+ operand from I/D depending if R = 7 +// 3 @(R)+ address from I/D depending if R = 7 and operand from D space +// 4 -(R) operand from I/D depending if R = 7 +// 5 @-(R) address from I/D depending if R = 7 and operand from D space +// 6 x(R) x from I space but operand from D space +// 7 @x(R) x from I space but address and operand from D space +// +// Kernel mode stack limit checks are implemented for addressing modes 1, 2, 4 & 6 (!) +// +// The accessMode field specifies two bit flags for read or write, or both for a modify. +// Mask values for these are constants READ_MODE and WRITE_MODE +// In addition the lower four bits specify the operand length. This is 1 for a byte +// or 2 for a word - however the FPP processor may also use 4 or 8. Thus if autoincrement +// is used on a double FPP word the register will autoincrement by 8. This should always +// be set to get the autoincrement correct. +// +// Just to keep us on our toes the mode (PC)+ (immediate mode, octal 27) ALWAYS increments +// by 2 no matter what type of operand is used. +// +// Also need to keep CPU.MMR1 updated as this stores which registers have been +// incremented and decremented so that the OS can reset and restart an instruction +// if a page fault occurs. +// +// Convert a six bit instruction operand to a 17 bit I/D virtual address + +function getVirtualByMode(addressMode, accessMode) { + "use strict"; + var virtualAddress, autoIncrement, reg = addressMode & 7; + if (!(accessMode & 0xf)) panic(75); + switch ((addressMode >> 3) & 7) { + case 0: // Mode 0: Registers don't have a virtual address so trap! + return trap(4, 34); // trap for invalid virtual address + case 1: // Mode 1: (R) + virtualAddress = CPU.registerVal[reg]; + if (reg < 6) { + virtualAddress |= 0x10000; + } else { + if (reg == 6) { + if (accessMode & WRITE_MODE) { + if ((virtualAddress = stackCheck(virtualAddress)) < 0) { + return virtualAddress; + } + } + virtualAddress |= 0x10000; + } + } + return virtualAddress; + case 2: // Mode 2: (R)+ + virtualAddress = CPU.registerVal[reg]; + if (reg == 7) { + autoIncrement = 2; // immediate mode (PC)+ always autoIncrements by 2 + } else { + autoIncrement = accessMode & 0xf; + if (reg == 6) { + if (accessMode & BYTE_MODE) { + autoIncrement = 2; // R6 doesn't autoIncrement by 1 + } + if (accessMode & WRITE_MODE) { + if ((virtualAddress = stackCheck(virtualAddress)) < 0) { + return virtualAddress; + } + } + } + virtualAddress |= 0x10000; + } + break; + case 3: // Mode 3: @(R)+ + autoIncrement = 2; + virtualAddress = CPU.registerVal[reg]; + if ((virtualAddress = readWordByVirtual(reg == 7 ? virtualAddress : virtualAddress | 0x10000)) < 0) { + return virtualAddress; + } + if (reg == 7) { + //LOG_ADDRESS(virtualAddress); // @#n not operational + } + virtualAddress |= 0x10000; + break; + case 4: // Mode 4: -(R) + autoIncrement = -(accessMode & 0xf); + if (reg < 6) { + virtualAddress = ((CPU.registerVal[reg] + autoIncrement) & 0xffff) | 0x10000; + } else { + if (accessMode & BYTE_MODE) { + autoIncrement = -2; // R6 & R7 don't decrement by 1 + } + virtualAddress = (CPU.registerVal[reg] + autoIncrement) & 0xffff; + if (reg == 6) { + if (accessMode & WRITE_MODE) { + if ((virtualAddress = stackCheck(virtualAddress)) < 0) { + return virtualAddress; + } + } + virtualAddress |= 0x10000; + } + } + break; + case 5: // Mode 5: @-(R) + autoIncrement = -2; + virtualAddress = (CPU.registerVal[reg] - 2) & 0xffff; + if ((virtualAddress = readWordByVirtual(reg == 7 ? virtualAddress : virtualAddress | 0x10000)) < 0) { + return virtualAddress; + } + virtualAddress |= 0x10000; + break; + case 6: // Mode 6: d(R) + if ((virtualAddress = readWordByVirtual(CPU.registerVal[7])) < 0) { + return virtualAddress; + } + CPU.registerVal[7] = (CPU.registerVal[7] + 2) & 0xffff; + if (reg < 7) { + //LOG_ADDRESS(virtualAddress); + virtualAddress = (virtualAddress + CPU.registerVal[reg]) & 0xffff; + } else { + virtualAddress = (virtualAddress + CPU.registerVal[reg]) & 0xffff; + //LOG_ADDRESS(virtualAddress); + } + if (reg == 6 && (accessMode & WRITE_MODE)) { + if ((virtualAddress = stackCheck(virtualAddress)) < 0) { + return virtualAddress; + } + } + return virtualAddress | 0x10000; + case 7: // Mode 7: @d(R) + if ((virtualAddress = readWordByVirtual(CPU.registerVal[7])) < 0) { + return virtualAddress; + } + CPU.registerVal[7] = (CPU.registerVal[7] + 2) & 0xffff; + if (reg < 7) { + //LOG_ADDRESS(virtualAddress); + virtualAddress = (virtualAddress + CPU.registerVal[reg]) & 0xffff; + } else { + virtualAddress = (virtualAddress + CPU.registerVal[reg]) & 0xffff; + //LOG_ADDRESS(virtualAddress); + } + if ((virtualAddress = readWordByVirtual(virtualAddress | 0x10000)) < 0) { + return virtualAddress; + } + return virtualAddress | 0x10000; // @x + } + CPU.registerVal[reg] = (CPU.registerVal[reg] + autoIncrement) & 0xffff; + if (!(CPU.MMR0 & 0xe000)) { + CPU.MMR1 = (CPU.MMR1 << 8) | ((autoIncrement << 3) & 0xf8) | reg; + } + return virtualAddress; +} + + +function getAddrByMode(addressMode, accessMode) { + "use strict"; + var result; + if (!(addressMode & 0x38)) { + return MAX_ADDRESS + (addressMode & 7); // Registers have special addresses above maximum address + } else { + if ((result = getVirtualByMode(addressMode, accessMode)) >= 0) { + result = mapVirtualToPhysical(result, accessMode); + } + return result; + } +} + + +function readWordByMode(addressMode) { + "use strict"; + var result; + if (!(addressMode & 0x38)) { + result = CPU.registerVal[addressMode & 7]; + //LOG_SOURCE(result); + } else { + if ((result = getAddrByMode(addressMode, READ_WORD)) >= 0) { + if ((result = readWordByAddr(result)) >= 0) { + //LOG_SOURCE(result); + } + } + } + return result; +} + + +function readByteByMode(addressMode) { + "use strict"; + var result; + if (!(addressMode & 0x38)) { + result = CPU.registerVal[addressMode & 7] & 0xff; + //LOG_SOURCE(result); + } else { + if ((result = getAddrByMode(addressMode, READ_BYTE)) >= 0) { + if ((result = readByteByAddr(result)) >= 0) { + //LOG_SOURCE(result); + } + } + } + return result; +} + + +// branch() calculates the branch to PC from a branch instruction offset + +function branch(PC, instruction) { + "use strict"; + return (PC + ((instruction & 0x80 ? instruction | 0xff00 : instruction & 0xff) << 1)) & 0xffff; +} + + +// Most instruction read operations use a 6 bit instruction operand via +// readWordByMode(instruction operand). If the result is negative then +// something failed and generally a trap or abort has already been invoked. +// The code for this would generally look like: +// if ((src = readWordByMode(instruction >> 6)) >= 0) { +// success - do operation with src +// +// For each Word function there are usually corresponding Byte functions, eg readByteByMode() +// +// Write only operations are just a special case of read/write and there are +// no special functions to support them, mainly because they would only be used by MOV +// and CLR instructions. Maybe they should have been added for consistency? +// +// Read/Write operations are harder than read because we want to do memory mapping +// only once. So the strategy is to get a physical address, use it to read +// the operand, and if nothing went wrong use it to do a write. +// The instruction code for this would generally look like: +// if ((dst = readWordByAddr(dstAddr = getAddrByMode(instruction, MODIFY_WORD))) >= 0) { +// result = something and dst +// if (writeWordByAddr(dstAddr, result) >= 0) { +// finish instruction, set CC codes etc +// +// Note that the getAddrByMode() function requires that we specify a mode bit mask +// to tell it if we are doing a read or write (or modify for both), and the length +// of the operand for autoincrement and to determine if addressing is byte mode. +// This information is also passed to memory management for it's use. +// +// For performance reasons many instructions have a special case optimization for register +// read/write (operand mode 0). Although the code would work without this (because +// getAddrByMode() will return a special pseudo physical address for registers), +// it is much quicker to bypass address generation and directly access the register. +// The code for this would generally look like: +// if (!(operand & 0x38)) { +// CPU.registerVal[operand & 7] = something +// } else { +// normal non-register code +// +// Some instructions (eg JMP, JSR, MTPx..) require the address of an operand. +// The code for this would generally look like: +// if ((virtualAddress = getVirtualByMode(instruction, 2)) >= 0) { +// +// Note the access mode does not need to specify READ_MODE or WRITE_MODE however +// the operand length must still be specified for correct address autoincrement. +// +// Most of these functions work in layers. For example: +// readWordByMode() fetches an operand after converting an instruction operand to +// a physical address, which is generated using getAddrByMode() +// The physical addresses is then accessed using readWordByAddr() +// getAddrByMode() first converts an instruction operand to a 17 bit I/D virtual address +// using getVirtualByMode() - which it then converts to a physical +// address using mapVirtualToPhysical() to represent a standard 22 bit PDP 11/70 memory +// and unibus address, or to one of eight special pseudo addresses for registers. +// readWordByAddr() / writeWordByAddr() either access registers directly, memory as +// CPU.memory[], or access the unibus I/O space through the function access_iopage() +// +// access_iopage() does a unibus read if it is passed -1 as data, or a write otherwise. +// +// CPU flags are stored outside of the PSW for performance reasons. A call to +// readPSW() will assemble them back into the PSW. Writes to the PSW should generally +// be through writePSW() as it needs to track which register set is in use, the memory +// management mode, whether priority has changed etc. +// Individual flags are CPU.flagC, CPU.flagN, CPU.flagV, and CPU.flagZ. These hold +// the value of the last result affecting them. So for example bit 16 of CPU.flagC +// is the only useful bit it contains. Likewise bit 15 of CPU.flagN and CPU.flagV is +// the only bit they use. For CPU.flagZ the lower 16 bits must be tested to see if +// the result was zero or not. +// +// All traps and aborts go through the trap() function. It returns a -1 value which +// is then passed up through other function layers and interpreted as an indicator +// that something has gone wrong, and that no futher processing is to be done for the +// current instruction. +// +// Instruction execution is performed by the step() function which processes a batch of +// instructions. The current strategy is to execute 5000 instructions repeating until +// 20 milliseconds (50 hz) have passed. +// Batching instructions in this way is required in Javascript as it is necessary +// to relinquish control periodically to let timer and I/O functions execute, and to +// update the console lights. Of course if JavaScript had a method of testing whether +// an event was pending then we could structure things differently... + + +function step(loopCount) { + var instruction, + src, + dst, + dstAddr, + result, + virtualAddress, savePSW, reg; + var loopTime = Date.now() + 20; // execute for 20 ms (50 Hz); + var CPU = window.CPU; + do { + // If something has changed review priority - with one instruction delay after SPL (!) + if (CPU.priorityReview) { + if (!(--CPU.priorityReview)) { + interruptReview(); + } + } + if (CPU.trapMask) { // check for any post instruction traps pending + if (CPU.trapMask & 2) { + trap(0250, 52); // MMU trap 250 has priority + } else { + if (CPU.trapMask & 4) { + trap(4, 54); // then stack warning trap + } else { + if (CPU.trapMask & 8) { + trap(0224, 55); // then floating point exception + } else { + if (CPU.trapMask & 0x10) { // same bit as T bit trap in PSW + trap(014, 56); // and finally a T-bit trap + } + } + } + } + } + if (!(CPU.MMR0 & 0xe000)) { + CPU.MMR1 = 0; + CPU.MMR2 = CPU.registerVal[7]; + } + // Remember if T-bit trap required at the end of this instruction + CPU.trapMask = CPU.PSW & 0x10; + if ((instruction = readWordByVirtual(CPU.registerVal[7])) >= 0) { + //if (CPU.registerVal[7] >= 0157220) { // DDEEBBUUGG + // console.log("PC " + CPU.registerVal[7].toString(8) + " instruction: " + instruction.toString(8) + " R0: " + CPU.registerVal[0].toString(8) + " R4: " + CPU.registerVal[4].toString(8)); + //} + CPU.registerVal[7] = (CPU.registerVal[7] + 2) & 0xffff; + switch (instruction & 0170000) { // Double operand instructions xxSSDD + case 0010000: // MOV 01SSDD + //LOG_INSTRUCTION(instruction, 2, "MOV"); + if ((src = readWordByMode(instruction >> 6)) >= 0) { + if (!(instruction & 0x38)) { + CPU.registerVal[instruction & 7] = src; + CPU.flagN = CPU.flagZ = src; + CPU.flagV = 0; + } else { + if ((dstAddr = getAddrByMode(instruction, WRITE_WORD)) >= 0) { + if (writeWordByAddr(dstAddr, src) >= 0) { + CPU.flagN = CPU.flagZ = src; + CPU.flagV = 0; + } + } + } + } + break; + case 0020000: // CMP 02SSDD + //LOG_INSTRUCTION(instruction, 2, "CMP"); + if ((src = readWordByMode(instruction >> 6)) >= 0) { + if ((dst = readWordByMode(instruction)) >= 0) { + result = src - dst; + CPU.flagN = CPU.flagZ = CPU.flagC = result; + CPU.flagV = (src ^ dst) & (src ^ result); + } + } + break; + case 0030000: // BIT 03SSDD + //LOG_INSTRUCTION(instruction, 2, "BIT"); + if ((src = readWordByMode(instruction >> 6)) >= 0) { + if ((dst = readWordByMode(instruction)) >= 0) { + CPU.flagN = CPU.flagZ = src & dst; + CPU.flagV = 0; + } + } + break; + case 0040000: // BIC 04SSDD + //LOG_INSTRUCTION(instruction, 2, "BIC"); + if ((src = readWordByMode(instruction >> 6)) >= 0) { + if (!(instruction & 0x38)) { + result = CPU.registerVal[instruction & 7] &= ~src; + CPU.flagN = CPU.flagZ = result; + CPU.flagV = 0; + } else { + if ((dst = readWordByAddr(dstAddr = getAddrByMode(instruction, MODIFY_WORD))) >= 0) { + result = dst & ~src; + if (writeWordByAddr(dstAddr, result) >= 0) { + CPU.flagN = CPU.flagZ = result; + CPU.flagV = 0; + } + } + } + } + break; + case 0050000: // BIS 05SSDD + //LOG_INSTRUCTION(instruction, 2, "BIS"); + if ((src = readWordByMode(instruction >> 6)) >= 0) { + if (!(instruction & 0x38)) { + result = CPU.registerVal[instruction & 7] |= src; + CPU.flagN = CPU.flagZ = result; + CPU.flagV = 0; + } else { + if ((dst = readWordByAddr(dstAddr = getAddrByMode(instruction, MODIFY_WORD))) >= 0) { + result = dst | src; + if (writeWordByAddr(dstAddr, result) >= 0) { + CPU.flagN = CPU.flagZ = result; + CPU.flagV = 0; + } + } + } + } + break; + case 0060000: // ADD 06SSDD + //LOG_INSTRUCTION(instruction, 2, "ADD"); + if ((src = readWordByMode(instruction >> 6)) >= 0) { + if (!(instruction & 0x38)) { + reg = instruction & 7; + dst = CPU.registerVal[reg]; + CPU.registerVal[reg] = (result = src + dst) & 0xffff; + CPU.flagN = CPU.flagZ = CPU.flagC = result; + CPU.flagV = (src ^ result) & (dst ^ result); + } else { + if ((dst = readWordByAddr(dstAddr = getAddrByMode(instruction, MODIFY_WORD))) >= 0) { + result = src + dst; + if (writeWordByAddr(dstAddr, result) >= 0) { + CPU.flagN = CPU.flagZ = CPU.flagC = result; + CPU.flagV = (src ^ result) & (dst ^ result); + } + } + } + } + break; + case 0110000: // MOVB 11SSDD + //LOG_INSTRUCTION(instruction, 2, "MOVB"); + if ((src = readByteByMode(instruction >> 6)) >= 0) { + if (!(instruction & 0x38)) { + if (src & 0200) { + src |= 0xff00; // movb sign extends register to word size + } + CPU.registerVal[instruction & 7] = src; + CPU.flagN = CPU.flagZ = src; + CPU.flagV = 0; + } else { + if ((dstAddr = getAddrByMode(instruction, WRITE_BYTE)) >= 0) { // write byte + if (writeByteByAddr(dstAddr, src) >= 0) { + CPU.flagN = CPU.flagZ = src << 8; + CPU.flagV = 0; + } + } + } + } + break; + case 0120000: // CMPB 12SSDD + //LOG_INSTRUCTION(instruction, 2, "CMPB"); + if ((src = readByteByMode(instruction >> 6)) >= 0) { + if ((dst = readByteByMode(instruction)) >= 0) { + result = src - dst; + CPU.flagN = CPU.flagZ = CPU.flagC = result << 8; + CPU.flagV = ((src ^ dst) & (src ^ result)) << 8; + } + } + break; + case 0130000: // BITB 13SSDD + //LOG_INSTRUCTION(instruction, 2, "BITB"); + if ((src = readByteByMode(instruction >> 6)) >= 0) { + if ((dst = readByteByMode(instruction)) >= 0) { + CPU.flagN = CPU.flagZ = (src & dst) << 8; + CPU.flagV = 0; + } + } + break; + case 0140000: // BICB 14SSDD + //LOG_INSTRUCTION(instruction, 2, "BICB"); + if ((src = readByteByMode(instruction >> 6)) >= 0) { + if ((dst = readByteByAddr(dstAddr = getAddrByMode(instruction, MODIFY_BYTE))) >= 0) { + result = dst & ~src; + if (writeByteByAddr(dstAddr, result) >= 0) { + CPU.flagN = CPU.flagZ = result << 8; + CPU.flagV = 0; + } + } + } + break; + case 0150000: // BISB 15SSDD + //LOG_INSTRUCTION(instruction, 2, "BISB"); + if ((src = readByteByMode(instruction >> 6)) >= 0) { + if ((dst = readByteByAddr(dstAddr = getAddrByMode(instruction, MODIFY_BYTE))) >= 0) { + result = dst | src; + if (writeByteByAddr(dstAddr, result) >= 0) { + CPU.flagN = CPU.flagZ = result << 8; + CPU.flagV = 0; + } + } + } + break; + case 0160000: // SUB 16SSDD + //LOG_INSTRUCTION(instruction, 2, "SUB"); + if ((src = readWordByMode(instruction >> 6)) >= 0) { + if (!(instruction & 0x38)) { + reg = instruction & 7; + dst = CPU.registerVal[reg]; + CPU.registerVal[reg] = (result = dst - src) & 0xffff; + CPU.flagN = CPU.flagZ = CPU.flagC = result; + CPU.flagV = (src ^ dst) & (dst ^ result); + } else { + if ((dst = readWordByAddr(dstAddr = getAddrByMode(instruction, MODIFY_WORD))) >= 0) { + result = dst - src; + if (writeWordByAddr(dstAddr, result) >= 0) { + CPU.flagN = CPU.flagZ = CPU.flagC = result; + CPU.flagV = (src ^ dst) & (dst ^ result); + } + } + } + } + break; + case 0170000: // FPP instructions + if (typeof executeFPP !== 'undefined') { + executeFPP(instruction); + } else { // Say we don't know this instruction + //LOG_INSTRUCTION(instruction, 11, "-unknown-"); + trap(010, 48); // Trap 10 - Illegal instruction + } + break; + default: + switch (instruction & 0177000) { // Misc instructions xxRDD + case 04000: // JSR 004RDD + //LOG_INSTRUCTION(instruction, 3, "JSR"); + if ((virtualAddress = getVirtualByMode(instruction, 2)) >= 0) { + reg = (instruction >> 6) & 7; + if (pushWord(CPU.registerVal[reg], 0) >= 0) { + CPU.registerVal[reg] = CPU.registerVal[7]; + CPU.registerVal[7] = virtualAddress & 0xffff; + } + } + break; + case 0070000: // MUL 070RSS + //LOG_INSTRUCTION(instruction, 3, "MUL"); + if ((src = readWordByMode(instruction)) >= 0) { + reg = (instruction >> 6) & 7; + if (src & 0x8000) { + src |= ~0xffff; + } + dst = CPU.registerVal[reg]; + if (dst & 0x8000) { + dst |= ~0xffff; + } + result = ~~(src * dst); + CPU.registerVal[reg] = (result >> 16) & 0xffff; + CPU.registerVal[reg | 1] = result & 0xffff; + CPU.flagN = result >> 16; + CPU.flagZ = CPU.flagN | result; + CPU.flagC = CPU.flagV = 0; + if (result < -32768 || result > 32767) { + CPU.flagC = 0x10000; + } + } + break; + case 0071000: // DIV 071RSS + //LOG_INSTRUCTION(instruction, 3, "DIV"); + if ((src = readWordByMode(instruction)) >= 0) { + if (!src) { + CPU.flagN = 0; // NZVC + CPU.flagZ = 0; + CPU.flagV = 0x8000; + CPU.flagC = 0x10000; // divide by zero + } else { + reg = (instruction >> 6) & 7; + dst = (CPU.registerVal[reg] << 16) | CPU.registerVal[reg | 1]; + CPU.flagC = CPU.flagV = 0; + if (src & 0x8000) { + src |= ~0xffff; + } + result = ~~(dst / src); + if (result >= -32768 && result <= 32767) { + CPU.registerVal[reg] = result & 0xffff; + CPU.registerVal[reg | 1] = (dst - (result * src)) & 0xffff; + CPU.flagZ = (result >> 16) | result; + CPU.flagN = result >> 16; + } else { + CPU.flagV = 0x8000; // overflow - following are indeterminate + CPU.flagZ = (result >> 15) | result; // dodgy + CPU.flagN = dst >> 16; // just as dodgy + if (src == -1 && CPU.registerVal[reg] == 0xfffe) { + CPU.registerVal[reg] = CPU.registerVal[reg | 1] = 1; // etc + } + } + } + } + break; + case 072000: // ASH 072RSS + //LOG_INSTRUCTION(instruction, 3, "ASH"); + if ((src = readWordByMode(instruction)) >= 0) { + reg = (instruction >> 6) & 7; + result = CPU.registerVal[reg]; + if (result & 0x8000) { + result |= 0xffff0000; + } + CPU.flagC = CPU.flagV = 0; + src &= 077; + if (src & 040) { // shift right + src = 64 - src; + if (src > 16) { + src = 16; + } + CPU.flagC = result << (17 - src); + result = result >> src; + } else { + if (src) { + if (src > 16) { + CPU.flagV = result; + result = 0; + } else { + result = result << src; + CPU.flagC = result; + dst = (result >> 15) & 0xffff; // check successive sign bits + if (dst && dst != 0xffff) { + CPU.flagV = 0x8000; + } + } + } + } + CPU.registerVal[reg] = result & 0xffff; + CPU.flagN = CPU.flagZ = result; + } + break; + case 073000: // ASHC 073RSS + //LOG_INSTRUCTION(instruction, 3, "ASHC"); + if ((src = readWordByMode(instruction)) >= 0) { + reg = (instruction >> 6) & 7; + dst = (CPU.registerVal[reg] << 16) | CPU.registerVal[reg | 1]; + CPU.flagC = CPU.flagV = 0; + src &= 077; + if (src & 040) { + src = 64 - src; + if (src > 32) { + src = 32; + } + result = dst >> (src - 1); + CPU.flagC = result << 16; + result >>= 1; + if (dst & 0x80000000) { + result |= 0xffffffff << (32 - src); + } + } else { + if (src) { // shift left + result = dst << (src - 1); + CPU.flagC = result >> 15; + result <<= 1; + if (src > 32) { + src = 32; + } + dst = dst >> (32 - src); + if (dst) { + dst |= (0xffffffff << src) & 0xffffffff; + if (dst != 0xffffffff) { + CPU.flagV = 0x8000; + } + } + } else { + result = dst; + } + } + CPU.registerVal[reg] = (result >> 16) & 0xffff; + CPU.registerVal[reg | 1] = result & 0xffff; + CPU.flagN = result >> 16; + CPU.flagZ = result >> 16 | result; + } + break; + case 0074000: // XOR 074RSS + //LOG_INSTRUCTION(instruction, 3, "XOR"); + src = CPU.registerVal[(instruction >> 6) & 7]; + if (!(instruction & 0x38)) { + result = CPU.registerVal[instruction & 7] ^= src; + CPU.flagN = CPU.flagZ = result; + CPU.flagV = 0; + } else { + if ((dst = readWordByAddr(dstAddr = getAddrByMode(instruction, MODIFY_WORD))) >= 0) { + result = dst ^ src; + if (writeWordByAddr(dstAddr, result) >= 0) { + CPU.flagN = CPU.flagZ = result; + CPU.flagV = 0; + } + } + } + break; + case 0077000: // SOB 077Rnn + //LOG_INSTRUCTION(instruction, 5, "SOB"); + reg = (instruction >> 6) & 7; + if ((CPU.registerVal[reg] = ((CPU.registerVal[reg] - 1) & 0xffff))) { + CPU.registerVal[7] = (CPU.registerVal[7] - ((instruction & 077) << 1)) & 0xffff; + } + break; + default: + switch (instruction & 0177400) { // Program control instructions & traps + case 0000400: // BR + //LOG_INSTRUCTION(instruction, 4, "BR"); + CPU.registerVal[7] = branch(CPU.registerVal[7], instruction); + break; + case 0001000: // BNE + //LOG_INSTRUCTION(instruction, 4, "BNE"); + if (CPU.flagZ & 0xffff) { + CPU.registerVal[7] = branch(CPU.registerVal[7], instruction); + } + break; + case 0001400: // BEQ + //LOG_INSTRUCTION(instruction, 4, "BEQ"); + if (!(CPU.flagZ & 0xffff)) { + CPU.registerVal[7] = branch(CPU.registerVal[7], instruction); + } + break; + case 0002000: // BGE + //LOG_INSTRUCTION(instruction, 4, "BGE"); + if ((CPU.flagN & 0x8000) == (CPU.flagV & 0x8000)) { + CPU.registerVal[7] = branch(CPU.registerVal[7], instruction); + } + break; + case 0002400: // BLT + //LOG_INSTRUCTION(instruction, 4, "BLT"); + if ((CPU.flagN & 0x8000) != (CPU.flagV & 0x8000)) { + CPU.registerVal[7] = branch(CPU.registerVal[7], instruction); + } + break; + case 0003000: // BGT + //LOG_INSTRUCTION(instruction, 4, "BGT"); + if ((CPU.flagZ & 0xffff) && ((CPU.flagN & 0x8000) == (CPU.flagV & 0x8000))) { + CPU.registerVal[7] = branch(CPU.registerVal[7], instruction); + } + break; + case 0003400: // BLE + //LOG_INSTRUCTION(instruction, 4, "BLE"); + if (!(CPU.flagZ & 0xffff) || ((CPU.flagN & 0x8000) != (CPU.flagV & 0x8000))) { + CPU.registerVal[7] = branch(CPU.registerVal[7], instruction); + } + break; + case 0100000: // BPL + //LOG_INSTRUCTION(instruction, 4, "BPL"); + if (!(CPU.flagN & 0x8000)) { + CPU.registerVal[7] = branch(CPU.registerVal[7], instruction); + } + break; + case 0101000: // BHI + //LOG_INSTRUCTION(instruction, 4, "BHI"); + if (!(CPU.flagC & 0x10000) && (CPU.flagZ & 0xffff)) { + CPU.registerVal[7] = branch(CPU.registerVal[7], instruction); + } + break; + case 0100400: // BMI + //LOG_INSTRUCTION(instruction, 4, "BMI"); + if ((CPU.flagN & 0x8000)) { + CPU.registerVal[7] = branch(CPU.registerVal[7], instruction); + } + break; + case 0101400: // BLOS + //LOG_INSTRUCTION(instruction, 4, "BLOS"); + if ((CPU.flagC & 0x10000) || !(CPU.flagZ & 0xffff)) { + CPU.registerVal[7] = branch(CPU.registerVal[7], instruction); + } + break; + case 0102000: // BVC + //LOG_INSTRUCTION(instruction, 4, "BVC"); + if (!(CPU.flagV & 0x8000)) { + CPU.registerVal[7] = branch(CPU.registerVal[7], instruction); + } + break; + case 0102400: // BVS + //LOG_INSTRUCTION(instruction, 4, "BVS"); + if ((CPU.flagV & 0x8000)) { + CPU.registerVal[7] = branch(CPU.registerVal[7], instruction); + } + break; + case 0103000: // BCC + //LOG_INSTRUCTION(instruction, 4, "BCC"); + if (!(CPU.flagC & 0x10000)) { + CPU.registerVal[7] = branch(CPU.registerVal[7], instruction); + } + break; + case 0103400: // BCS + //LOG_INSTRUCTION(instruction, 4, "BCS"); + if (CPU.flagC & 0x10000) { + CPU.registerVal[7] = branch(CPU.registerVal[7], instruction); + } + break; + case 0104000: // EMT 104000 -> 104377 + //LOG_INSTRUCTION(instruction, 7, "EMT"); + trap(030, 2); // Trap 30 - EMT instruction + break; + case 0104400: // TRAP 104400 -> 104777 + //LOG_INSTRUCTION(instruction, 7, "TRAP"); + trap(034, 4); // Trap 34 - TRAP instruction + break; + default: + switch (instruction & 0177700) { // Single operand instructions xxxxDD + case 0000100: // JMP 0001DD + //LOG_INSTRUCTION(instruction, 1, "JMP"); + if ((virtualAddress = getVirtualByMode(instruction, 2)) >= 0) { + CPU.registerVal[7] = virtualAddress & 0xffff; + } + break; + case 0000300: // SWAB 0003DD + //LOG_INSTRUCTION(instruction, 1, "SWAB"); + if ((dst = readWordByAddr(dstAddr = getAddrByMode(instruction, MODIFY_WORD))) >= 0) { + result = (dst << 8) | (dst >> 8); + if (writeWordByAddr(dstAddr, result) >= 0) { + CPU.flagN = CPU.flagZ = dst & 0xff00; + CPU.flagV = CPU.flagC = 0; + } + } + break; + case 0005000: // CLR 0050DD + //LOG_INSTRUCTION(instruction, 1, "CLR"); + if (!(instruction & 0x38)) { + CPU.registerVal[instruction & 7] = 0; + CPU.flagN = CPU.flagC = CPU.flagV = CPU.flagZ = 0; + } else { + if ((dstAddr = getAddrByMode(instruction, WRITE_WORD)) >= 0) { // write word + if (writeWordByAddr(dstAddr, 0) >= 0) { + CPU.flagN = CPU.flagC = CPU.flagV = CPU.flagZ = 0; + } + } + } + break; + case 0005100: // COM 0051DD + //LOG_INSTRUCTION(instruction, 1, "COM"); + if ((dst = readWordByAddr(dstAddr = getAddrByMode(instruction, MODIFY_WORD))) >= 0) { + result = ~dst; + if (writeWordByAddr(dstAddr, result) >= 0) { + CPU.flagN = CPU.flagZ = result; + CPU.flagC = 0x10000; + CPU.flagV = 0; + } + } + break; + case 0005200: // INC 0052DD + //LOG_INSTRUCTION(instruction, 1, "INC"); + if (!(instruction & 0x38)) { + reg = instruction & 7; + dst = CPU.registerVal[reg]; + result = dst + 1; + CPU.registerVal[reg] = result & 0xffff; + CPU.flagN = CPU.flagZ = result; + CPU.flagV = result & (result ^ dst); + } else { + if ((dst = readWordByAddr(dstAddr = getAddrByMode(instruction, MODIFY_WORD))) >= 0) { + result = dst + 1; + if (writeWordByAddr(dstAddr, result) >= 0) { + CPU.flagN = CPU.flagZ = result; + CPU.flagV = result & (result ^ dst); + } + } + } + break; + case 0005300: // DEC 0053DD + //LOG_INSTRUCTION(instruction, 1, "DEC"); + if (!(instruction & 0x38)) { + reg = instruction & 7; + dst = CPU.registerVal[reg]; + result = dst - 1; + CPU.registerVal[reg] = result & 0xffff; + CPU.flagN = CPU.flagZ = result; + CPU.flagV = (result ^ dst) & dst; + } else { + if ((dst = readWordByAddr(dstAddr = getAddrByMode(instruction, MODIFY_WORD))) >= 0) { + result = dst - 1; + if (writeWordByAddr(dstAddr, result) >= 0) { + CPU.flagN = CPU.flagZ = result; + CPU.flagV = (result ^ dst) & dst; + } + } + } + break; + case 0005400: // NEG 0054DD + //LOG_INSTRUCTION(instruction, 1, "NEG"); + if ((dst = readWordByAddr(dstAddr = getAddrByMode(instruction, MODIFY_WORD))) >= 0) { + result = -dst; + if (writeWordByAddr(dstAddr, result) >= 0) { + CPU.flagC = CPU.flagN = CPU.flagZ = result; + CPU.flagV = result & dst; + } + } + break; + case 0005500: // ADC 0055DD + //LOG_INSTRUCTION(instruction, 1, "ADC"); + if ((dst = readWordByAddr(dstAddr = getAddrByMode(instruction, MODIFY_WORD))) >= 0) { + result = dst + ((CPU.flagC >> 16) & 1); + if (writeWordByAddr(dstAddr, result) >= 0) { + CPU.flagC = CPU.flagN = CPU.flagZ = result; + CPU.flagV = result & (result ^ dst); + } + } + break; + case 0005600: // SBC 0056DD + //LOG_INSTRUCTION(instruction, 1, "SBC"); + if ((dst = readWordByAddr(dstAddr = getAddrByMode(instruction, MODIFY_WORD))) >= 0) { + result = dst - ((CPU.flagC >> 16) & 1); + if (writeWordByAddr(dstAddr, result) >= 0) { + CPU.flagC = CPU.flagN = CPU.flagZ = result; + CPU.flagV = (result ^ dst) & dst; + } + } + break; + case 0005700: // TST 0057DD + //LOG_INSTRUCTION(instruction, 1, "TST"); + if ((dst = readWordByMode(instruction)) >= 0) { + CPU.flagN = CPU.flagZ = dst; + CPU.flagC = CPU.flagV = 0; + } + break; + case 0006000: // ROR 0060DD + //LOG_INSTRUCTION(instruction, 1, "ROR"); + if ((dst = readWordByAddr(dstAddr = getAddrByMode(instruction, MODIFY_WORD))) >= 0) { + result = ((CPU.flagC & 0x10000) | dst) >> 1; + if (writeWordByAddr(dstAddr, result) >= 0) { + CPU.flagC = (dst << 16); + CPU.flagN = CPU.flagZ = result; + CPU.flagV = result ^ (CPU.flagC >> 1); + } + } + break; + case 0006100: // ROL 0061DD + //LOG_INSTRUCTION(instruction, 1, "ROL"); + if ((dst = readWordByAddr(dstAddr = getAddrByMode(instruction, MODIFY_WORD))) >= 0) { + result = (dst << 1) | ((CPU.flagC >> 16) & 1); + if (writeWordByAddr(dstAddr, result) >= 0) { + CPU.flagC = CPU.flagN = CPU.flagZ = result; + CPU.flagV = result ^ dst; + } + } + break; + case 0006200: // ASR 0062DD + //LOG_INSTRUCTION(instruction, 1, "ASR"); + if ((dst = readWordByAddr(dstAddr = getAddrByMode(instruction, MODIFY_WORD))) >= 0) { + result = (dst & 0x8000) | (dst >> 1); + if (writeWordByAddr(dstAddr, result) >= 0) { + CPU.flagC = dst << 16; + CPU.flagN = CPU.flagZ = result; + CPU.flagV = CPU.flagN ^ (CPU.flagC >> 1); + } + } + break; + case 0006300: // ASL 0063DD + //LOG_INSTRUCTION(instruction, 1, "ASL"); + if ((dst = readWordByAddr(dstAddr = getAddrByMode(instruction, MODIFY_WORD))) >= 0) { + result = dst << 1; + if (writeWordByAddr(dstAddr, result) >= 0) { + CPU.flagC = CPU.flagN = CPU.flagZ = result; + CPU.flagV = result ^ dst; + } + } + break; + case 0006400: // MARK 0064nn + //LOG_INSTRUCTION(instruction, 8, "MARK"); + virtualAddress = (CPU.registerVal[7] + ((instruction & 077) << 1)) & 0xffff; + if ((src = readWordByVirtual(virtualAddress | 0x10000)) >= 0) { + CPU.registerVal[7] = CPU.registerVal[5]; + CPU.registerVal[5] = src; + CPU.registerVal[6] = (virtualAddress + 2) & 0xffff; + } + break; + case 0006500: // MFPI 0065SS + //LOG_INSTRUCTION(instruction, 1, "MFPI"); + if (!(instruction & 0x38)) { + reg = instruction & 7; + if (6 != reg || ((CPU.PSW >> 2) & 0x3000) == (CPU.PSW & 0x3000)) { + src = CPU.registerVal[reg]; + } else { + src = CPU.stackPointer[(CPU.PSW >> 12) & 3]; + } + if (pushWord(src, 0) >= 0) { + CPU.flagN = CPU.flagZ = src; + CPU.flagV = 0; + } + } else { + if ((virtualAddress = getVirtualByMode(instruction, 2)) >= 0) { + if ((CPU.PSW & 0xf000) != 0xf000) { + virtualAddress &= 0xffff; + } + CPU.mmuMode = (CPU.PSW >> 12) & 3; + if ((src = readWordByVirtual(virtualAddress)) >= 0) { + CPU.mmuMode = (CPU.PSW >> 14) & 3; + if (pushWord(src, 0) >= 0) { + CPU.flagN = CPU.flagZ = src; + CPU.flagV = 0; + } + } + } + } + break; + case 0006600: // MTPI 0066DD + //LOG_INSTRUCTION(instruction, 1, "MTPI"); + if ((dst = popWord()) >= 0) { + if (!(CPU.MMR0 & 0xe000)) { + CPU.MMR1 = 026; + } + if (!(instruction & 0x38)) { + reg = instruction & 7; + if (6 != reg || ((CPU.PSW >> 2) & 0x3000) == (CPU.PSW & 0x3000)) { + CPU.registerVal[reg] = dst; + } else { + CPU.stackPointer[(CPU.PSW >> 12) & 3] = dst; + } + CPU.flagN = CPU.flagZ = dst; + CPU.flagV = 0; + } else { + if ((virtualAddress = getVirtualByMode(instruction, 2)) >= 0) { + virtualAddress &= 0xffff; + CPU.mmuMode = (CPU.PSW >> 12) & 3; + if ((dstAddr = mapVirtualToPhysical(virtualAddress, WRITE_MODE)) >= 0) { + CPU.mmuMode = (CPU.PSW >> 14) & 3; + if (writeWordByAddr(dstAddr, dst) >= 0) { + CPU.flagN = CPU.flagZ = dst; + CPU.flagV = 0; + } + } + } + } + } + break; + case 0006700: // SXT 0067DD + //LOG_INSTRUCTION(instruction, 1, "SXT"); + if ((dstAddr = getAddrByMode(instruction, WRITE_WORD)) >= 0) { // write word + result = -((CPU.flagN >> 15) & 1); + if (writeWordByAddr(dstAddr, result) >= 0) { + CPU.flagZ = result; + CPU.flagV = 0; + } + } + break; + case 0105000: // CLRB 1050DD + //LOG_INSTRUCTION(instruction, 1, "CLRB"); + if (!(instruction & 0x38)) { + CPU.registerVal[instruction & 7] &= 0xff00; + CPU.flagN = CPU.flagC = CPU.flagV = CPU.flagZ = 0; + } else { + if ((dstAddr = getAddrByMode(instruction, WRITE_BYTE)) >= 0) { // write byte + if (writeByteByAddr(dstAddr, 0) >= 0) { + CPU.flagN = CPU.flagC = CPU.flagV = CPU.flagZ = 0; + } + } + } + break; + case 0105100: // COMB 1051DD + //LOG_INSTRUCTION(instruction, 1, "COMB"); + if ((dst = readByteByAddr(dstAddr = getAddrByMode(instruction, MODIFY_BYTE))) >= 0) { + result = ~dst; + if (writeByteByAddr(dstAddr, result) >= 0) { + CPU.flagN = CPU.flagZ = result << 8; + CPU.flagC = 0x10000; + CPU.flagV = 0; + } + } + break; + case 0105200: // INCB 1052DD + //LOG_INSTRUCTION(instruction, 1, "INCB"); + if ((dst = readByteByAddr(dstAddr = getAddrByMode(instruction, MODIFY_BYTE))) >= 0) { + result = dst + 1; + if (writeByteByAddr(dstAddr, result) >= 0) { + CPU.flagN = CPU.flagZ = result << 8; + CPU.flagV = (result & (result ^ dst)) << 8; + } + } + break; + case 0105300: // DECB 1053DD + //LOG_INSTRUCTION(instruction, 1, "DECB"); + if ((dst = readByteByAddr(dstAddr = getAddrByMode(instruction, MODIFY_BYTE))) >= 0) { + result = dst - 1; + if (writeByteByAddr(dstAddr, result) >= 0) { + CPU.flagN = CPU.flagZ = result << 8; + CPU.flagV = ((result ^ dst) & dst) << 8; + } + } + break; + case 0105400: // NEGB 1054DD + //LOG_INSTRUCTION(instruction, 1, "NEGB"); + if ((dst = readByteByAddr(dstAddr = getAddrByMode(instruction, MODIFY_BYTE))) >= 0) { + result = -dst; + if (writeByteByAddr(dstAddr, result) >= 0) { + CPU.flagC = CPU.flagN = CPU.flagZ = result << 8; + CPU.flagV = (result & dst) << 8; + } + } + break; + case 0105500: // ADCB 01055DD + //LOG_INSTRUCTION(instruction, 1, "ADCB"); + if ((dst = readByteByAddr(dstAddr = getAddrByMode(instruction, MODIFY_BYTE))) >= 0) { + result = dst + ((CPU.flagC >> 16) & 1); + if (writeByteByAddr(dstAddr, result) >= 0) { + CPU.flagN = CPU.flagZ = CPU.flagC = result << 8; + CPU.flagV = (result & (result ^ dst)) << 8; + } + } + break; + case 0105600: // SBCB 01056DD + //LOG_INSTRUCTION(instruction, 1, "SBCB"); + if ((dst = readByteByAddr(dstAddr = getAddrByMode(instruction, MODIFY_BYTE))) >= 0) { + result = dst - ((CPU.flagC >> 16) & 1); + if (writeByteByAddr(dstAddr, result) >= 0) { + CPU.flagN = CPU.flagZ = CPU.flagC = result << 8; + CPU.flagV = ((result ^ dst) & dst) << 8; + } + } + break; + case 0105700: // TSTB 1057DD + //LOG_INSTRUCTION(instruction, 1, "TSTB"); + if ((dst = readByteByMode(instruction)) >= 0) { + CPU.flagN = CPU.flagZ = dst << 8; + CPU.flagC = CPU.flagV = 0; + } + break; + case 0106000: // RORB 1060DD + //LOG_INSTRUCTION(instruction, 1, "RORB"); + if ((dst = readByteByAddr(dstAddr = getAddrByMode(instruction, MODIFY_BYTE))) >= 0) { + result = (((CPU.flagC & 0x10000) >> 8) | dst) >> 1; + if (writeByteByAddr(dstAddr, result) >= 0) { + CPU.flagC = (dst << 16); + CPU.flagN = CPU.flagZ = (result << 8); + CPU.flagV = CPU.flagN ^ (CPU.flagC >> 1); + } + } + break; + case 0106100: // ROLB 1061DD + //LOG_INSTRUCTION(instruction, 1, "ROLB"); + if ((dst = readByteByAddr(dstAddr = getAddrByMode(instruction, MODIFY_BYTE))) >= 0) { + result = (dst << 1) | ((CPU.flagC >> 16) & 1); + if (writeByteByAddr(dstAddr, result) >= 0) { + CPU.flagC = CPU.flagN = CPU.flagZ = result << 8; + CPU.flagV = (result ^ dst) << 8; + } + } + break; + case 0106200: // ASRB 1062DD + //LOG_INSTRUCTION(instruction, 1, "ASRB"); + if ((dst = readByteByAddr(dstAddr = getAddrByMode(instruction, MODIFY_BYTE))) >= 0) { + result = (dst & 0x80) | (dst >> 1); + if (writeByteByAddr(dstAddr, result) >= 0) { + CPU.flagC = dst << 16; + CPU.flagN = CPU.flagZ = result << 8; + CPU.flagV = CPU.flagN ^ (CPU.flagC >> 1); + } + } + break; + case 0106300: // ASLB 1063DD + //LOG_INSTRUCTION(instruction, 1, "ASLB"); + if ((dst = readByteByAddr(dstAddr = getAddrByMode(instruction, MODIFY_BYTE))) >= 0) { + result = dst << 1; + if (writeByteByAddr(dstAddr, result) >= 0) { + CPU.flagC = CPU.flagN = CPU.flagZ = result << 8; + CPU.flagV = (result ^ dst) << 8; + } + } + break; + //case 0106400: // MTPS 1064SS + // //LOG_INSTRUCTION(instruction, 1, "MTPS"); + // if ((src = readByteByMode(instruction)) >= 0) { + // writePSW((CPU.PSW & 0xff00) | (src & 0xef)); + // } // Temporary PDP 11/34A + // break; + case 0106500: // MFPD 1065DD + //LOG_INSTRUCTION(instruction, 1, "MFPD"); + if (!(instruction & 0x38)) { + reg = instruction & 7; + if (6 != reg || ((CPU.PSW >> 2) & 0x3000) == (CPU.PSW & 0x3000)) { + src = CPU.registerVal[reg]; + } else { + src = CPU.stackPointer[(CPU.PSW >> 12) & 3]; + } + if (pushWord(src, 0) >= 0) { + CPU.flagN = CPU.flagZ = src; + CPU.flagV = 0; + } + } else { + if ((virtualAddress = getVirtualByMode(instruction, 2)) >= 0) { + CPU.mmuMode = (CPU.PSW >> 12) & 3; + if ((src = readWordByVirtual(virtualAddress | 0x10000)) >= 0) { + CPU.mmuMode = (CPU.PSW >> 14) & 3; + if (pushWord(src, 0) >= 0) { + CPU.flagN = CPU.flagZ = src; + CPU.flagV = 0; + } + } + } + } + break; + case 0106600: // MTPD 1066DD + //LOG_INSTRUCTION(instruction, 1, "MTPD"); + if ((dst = popWord()) >= 0) { + if (!(CPU.MMR0 & 0xe000)) { + CPU.MMR1 = 026; + } + if (!(instruction & 0x38)) { + reg = instruction & 7; + if (6 != reg || ((CPU.PSW >> 2) & 0x3000) == (CPU.PSW & 0x3000)) { + CPU.registerVal[reg] = dst; + } else { + CPU.stackPointer[(CPU.PSW >> 12) & 3] = dst; + } + CPU.flagN = CPU.flagZ = dst; + CPU.flagV = 0; + } else { + if ((virtualAddress = getVirtualByMode(instruction, 2)) >= 0) { + CPU.mmuMode = (CPU.PSW >> 12) & 3; + if ((dstAddr = mapVirtualToPhysical(virtualAddress | 0x10000, WRITE_MODE)) >= 0) { + CPU.mmuMode = (CPU.PSW >> 14) & 3; + if (writeWordByAddr(dstAddr, dst) >= 0) { + CPU.flagN = CPU.flagZ = dst; + CPU.flagV = 0; + } + } + } + } + } + break; + //case 0106700: // MTFS 1064SS + // //LOG_INSTRUCTION(instruction, 1, "MFPS"); + // src = readPSW() & 0xff; + // if (instruction & 0x38) { + // if ((dstAddr = getAddrByMode(instruction, WRITE_BYTE)) >= 0) { // write byte + // if (writeByteByAddr(dstAddr, src) >= 0) { + // CPU.flagN = CPU.flagZ = src << 8; + // CPU.flagV = 0; + // } + // } + // } else { + // if (src & 0200) src |= 0xff00; + // CPU.registerVal[instruction & 7] = src; + // CPU.flagN = CPU.flagZ = src << 8; + // CPU.flagV = 0; + // } // Temporary PDP 11/34A + // break; + default: + switch (instruction & 0177770) { // Single register instructions xxxxxR (and CC) + case 0000200: // RTS 00020R + //LOG_INSTRUCTION(instruction, 6, "RTS"); + if ((src = popWord()) >= 0) { + reg = instruction & 7; + CPU.registerVal[7] = CPU.registerVal[reg]; + CPU.registerVal[reg] = src; + } + break; + case 0000230: // SPL 00023N + //LOG_INSTRUCTION(instruction, 9, "SPL"); + if (!(CPU.PSW & 0xc000)) { + CPU.PSW = (CPU.PSW & 0xf81f) | ((instruction & 7) << 5); + CPU.priorityReview = 2; + } + break; + case 0000240: // CLR CC 00024M Part 1 without N + case 0000250: // CLR CC 00025M Part 2 with N + //LOG_INSTRUCTION(instruction, 10, "CLR CC"); + if (instruction & 1) { + CPU.flagC = 0; // CLC + } + if (instruction & 2) { + CPU.flagV = 0; // CLV + } + if (instruction & 4) { + CPU.flagZ = 1; // CLZ + } + if (instruction & 8) { + CPU.flagN = 0; // CLN + } + break; + case 0000260: // SET CC 00026M Part 1 without N + case 0000270: // SET CC 00026M Part 2 with N + //LOG_INSTRUCTION(instruction, 10, "SET CC"); + if (instruction & 1) { + CPU.flagC = 0x10000; // SEC + } + if (instruction & 2) { + CPU.flagV = 0x8000; // SEV + } + if (instruction & 4) { + CPU.flagZ = 0; // SEZ + } + if (instruction & 8) { + CPU.flagN = 0x8000; // SEN + } + break; + default: // Misc instructions (decode ALL remaining bits) xxxxxx + switch (instruction) { + case 0000000: // HALT 000000 + //LOG_INSTRUCTION(instruction, 0, "HALT"); + if (0xc000 & CPU.PSW) { + CPU.CPU_Error |= 0200; + trap(4, 46); + } else { + CPU.runState = STATE.HALT; // halt + loopCount = 0; // go update the lights + //LOG_PRINT(); + console.log("HALT at " + CPU.registerVal[7].toString(8)); + } + break; + case 0000001: // WAIT 000001 + //LOG_INSTRUCTION(instruction, 0, "WAIT"); + if (!interruptWaitRelease()) { + CPU.runState = STATE.WAIT; // WAIT; // Go to wait state and exit loop + loopCount = 0; // go update the lights + } + break; + case 0000003: // BPT 000003 + //LOG_INSTRUCTION(instruction, 0, "BPT"); + trap(014, 6); // Trap 14 - BPT + break; + case 0000004: // IOT 000004 + //LOG_INSTRUCTION(instruction, 0, "IOT"); + trap(020, 8); // Trap 20 - IOT + break; + case 0000005: // RESET 000005 + //LOG_INSTRUCTION(instruction, 0, "RESET"); + if (!(CPU.PSW & 0xc000)) { + reset_iopage();; + CPU.runState = STATE.RESET; // reset state for special pause + loopCount = 0; // go update the lights + } + break; + case 0000002: // RTI 000002 + case 0000006: // RTT 000006 + //LOG_INSTRUCTION(instruction, 0, "RTT"); + dstAddr = CPU.registerVal[6]; + if ((virtualAddress = readWordByVirtual(dstAddr | 0x10000)) >= 0) { + dstAddr = (dstAddr + 2) & 0xffff; + if ((savePSW = readWordByVirtual(dstAddr | 0x10000)) >= 0) { + CPU.registerVal[6] = (dstAddr + 2) & 0xffff; + savePSW &= 0xf8ff; + if (CPU.PSW & 0xc000) { // user / super restrictions + // keep SPL and allow lower only for modes and register set + savePSW = (savePSW & 0xf81f) | (CPU.PSW & 0xf8e0); + } + CPU.registerVal[7] = virtualAddress; + writePSW(savePSW); + CPU.trapMask &= ~0x10; // turn off Trace trap + if (instruction == 2) { + CPU.trapMask |= CPU.PSW & 0x10; // RTI enables immediate trace + } + } + } + break; + //case 0000007: // MFPT 000007 + // //LOG_INSTRUCTION(instruction, 0, "MFPT"); + // CPU.registerVal[0] = 1; + // break; // Exists on pdp 11/44 & KB11-EM + default: // We don't know this instruction + //LOG_INSTRUCTION(instruction, 11, "-unknown-"); + trap(010, 48); // Trap 10 - Illegal instruction + break; + } + } + } + } + } + } + } + } while (--loopCount > 0 || (loopCount == 0 && (loopCount = ((loopTime >= Date.now()) ? 4000 : 0)))); // check clock every 4000 iterations + + if (CPU.runState != STATE.RUN) { + CPU.displayAddress = CPU.registerVal[7]; // In other than RUN state display current PC + } + updateDisplayLights(result); // update address and data displays (result represents 'random' data from 'logic') + updateStatusLights(0); // update status lights (run, mode, memory mode, etc...) +} + + +function emulate() { + "use strict"; + if (CPU.runState == STATE.RUN) { + step(1); // wish there was a way to test for a pending javascript events :-( + } + if (CPU.runState == STATE.RUN) { + setTimeout(emulate, 0); // immediately schedule another batch of instructions + } else { + if (CPU.runState == STATE.RESET) { + CPU.runState = STATE.RUN; + setTimeout(emulate, 60); // schedule instructions after a reset pause + } + } +} + + +function updateDisplayLights(result) { // Update address and data display lights + "use strict"; + if (panel.rotary2 != 1) { + if (panel.address != (CPU.displayAddress & 0xffff)) { + panel.address = updatePanel("a", panel.address, (CPU.displayAddress & 0xffff)); + } + } else { + if (panel.address != CPU.displayPhysical) { + panel.address = updatePanel("a", panel.address, CPU.displayPhysical); + } + } + switch (panel.rotary1) { + case 0: + case 1: + if (CPU.runState == STATE.RUN) { // Update the data lights + panel.display = updatePanel("d", panel.display, result & 0xffff); // Random data path + } else { + if (panel.display != CPU.registerVal[0]) { + panel.display = updatePanel("d", panel.display, CPU.registerVal[0]); + } + } + break; + case 2: + if (panel.display != CPU.switchRegister) { + panel.display = updatePanel("d", panel.display, CPU.switchRegister); + } + break; + case 3: + if (panel.display != CPU.displayRegister) { + panel.display = updatePanel("d", panel.display, CPU.displayRegister); + } + break; + } +} + + +function updateStatusLights(result) { // Recalculate status panel - Addr Err may be set + "use strict"; + switch (CPU.runState) { + case STATE.RUN: + result |= 0x280; // RUN & MASTER + break; + case STATE.WAIT: + result |= 0x300; // RUN & PAUSE + break; + case STATE.RESET: + result |= 0x100; // PAUSE + break; + default: + result |= 0x80; // MASTER + break; + } + result |= (((CPU.PSW >> 10) & 0x70) + 0x10) | ((CPU.displayAddress >> 13) & 8); // Calculate mode (K, S or U) & data for status lights + if (CPU.MMR0 & 0x101) { // Set addressing mode lights (16, 18 or 22 bit) + if (CPU.MMR3 & 0x10) { + result |= 1; // 22 bit + } else { + result |= 2; // 18 bit + } + } else { + result |= 4; // 16 bit + } + if (panel.status != result) { + panel.status = updatePanel("m", panel.status, result); + } +} \ No newline at end of file diff --git a/vt11.js b/vt11.js new file mode 100755 index 0000000..d50a01a --- /dev/null +++ b/vt11.js @@ -0,0 +1,376 @@ +// 017772000 - 017772006 VT11 control registers +// Stop interrupt 320 +// Light pen interrupt 324 +// Time out/Shift out interrupt 330 +// All interrupts at BR4 +// 1024 x 768 (0,0) at bottom left +// screen content fades in approx 30ms :-( +// light pen detects a hit within a region :-( +// +// still need line types, blinking & intensity +// need to track if refreshing/active better +// +var vt11 = { + stopInterrupt: 0, // enable interrupt on stop flag + penInterrupt: 0, // enable interrupt on pen flag + refreshOn: 0, // DP Refresh or line refresh + DPC: 0, // Display Program Counter + DSR: 0x8000, // Display Status Register Stop 15, Mode 14:11 Intensity 10:8 Pen 7 Shift 6, Edge 5 Italics 4 Blink 3 Spare 2 Line 0:1 + XRegister: 0, // X Status Register + YRegister: 0, // Y Status Register + graphIncrement: 0, // Graphplot increment + canvasFG: null, + canvasBG: null, + ctxFG: null, + ctxBG: null, + refreshDPC: 0, + refreshTime: 0, + blinkFlag: 0, + penX: 0, + penY: 0, + mouseX: 0, + mouseY: 0, + DEBUG: 0 // debug mode +}; + +function vt11Initialize() { + vt11.canvasFG = document.createElement('canvas'); + vt11.canvasFG.width = 1024; + vt11.canvasFG.height = 768; + vt11.canvasFG.style.border = "1px solid"; + vt11.canvasFG.style.cursor = "none"; + document.getElementById('vt11').appendChild(vt11.canvasFG); + vt11.ctxFG = vt11.canvasFG.getContext("2d"); + vt11.canvasBG = document.createElement('canvas'); + vt11.canvasBG.width = 1024; + vt11.canvasBG.height = 768; + vt11.ctxBG = vt11.canvasBG.getContext('2d'); + vt11.canvasFG.addEventListener('mousemove', vt11TrackMouse, false); + setInterval(vt11BlinkCycle, 500); +} + +function vt11TrackMouse(evt) { + var rect = vt11.canvasFG.getBoundingClientRect(); + vt11.mouseX = evt.clientX - rect.left, + vt11.mouseY = evt.clientY - rect.top +} + +function vt11BlinkCycle() { + vt11.blinkFlag = 1 - vt11.blinkFlag; +} + +function vt11Read(physicalAddress) { + if (physicalAddress & 1) { + CPU.CPU_Error |= 0x40; + return trap(4, 26); + } else { + return readWordByAddr(physicalAddress); + } +} + +function vt11PaintChar(ctx, code) { + if (code >= 32 && code <= 127) { + ctx.fillText(String.fromCharCode(code), vt11.XRegister, 767 - vt11.YRegister); + vt11.XRegister += 8; + } else { + switch (code) { + case 015: + vt11.XRegister = 0; + break; + case 012: + vt11.YRegister -= 10; + if (vt11.YRegister < 0) vt11.YRegister = 0; + break; + } + } +} + +function vt11Refresh() { // all drawing happens in background canvas which is periodically moved to the foreground + if (vt11.refreshOn) { + vt11.ctxFG.clearRect(0, 0, 1024, 768); + vt11.ctxFG.beginPath(); + vt11.ctxFG.drawImage(vt11.canvasBG, 0, 0); + if (!(vt11.DSR & 0x80)) { + vt11.ctxFG.fillRect(vt11.mouseX, vt11.mouseY, 3, 2); // mark light pen position + } + vt11.ctxBG.clearRect(0, 0, 1024, 768); + vt11.ctxBG.beginPath(); + } + vt11.refreshDPC = vt11.DPC; // refresh done when we see the same DPC location + vt11.refreshTime = Date.now() + 800; // or a timeout +} + + +function vt11DistanceCheck(x1, y1, x2, y2, p, q) { + var dx, dy; + // check if near (x1,y1) + var dx = x1 - p; + var dy = y1 - q; + if (Math.sqrt(dx * dx + dy * dy) < 8) { + vt11.penX = x1; + vt11.penY = y1; + return true; + } + // if other point is the same we have finished + if (x1 == x2 && y1 == y2) { + return false; + } + // check if near (x2,y2) + var dx = x2 - p; + var dy = y2 - q; + if (Math.sqrt(dx * dx + dy * dy) < 8) { + vt11.penX = x2; + vt11.penY = y2; + return true; + } + // check if point outside x range + if ((p < x1 && p < x2) || (p > x1 && p > x2)) { + return false; + } + // check if point outside y range + if ((q < y1 && q < y2) || (q > y1 && q > y2)) { + return false; + } + // check if x is the same (vertical line) + if (x1 == x2) { + vt11.penX = x1; + vt11.penY = q; + return (Math.abs(p - x1) < 8); + } + // check if y is the same (horizontal line) + if (y1 == y2) { + vt11.penX = p; + vt11.penY = y1; + return (Math.abs(q - y1) < 8); + } + // calculate distance from line + dx = x2 - x1; + dy = y2 - y1; + if ((Math.abs(dy * p - dx * q + x2 * y1 - y2 * x1) / Math.sqrt(dy * dy + dx * dx)) < 8) { + vt11.penX = p; // fake it for now + vt11.penY = q; // fake it for now + return true; + } + return false; +} + + +function vt11Execute() { + //"use strict"; + var count, instruction, mode, intensify, style, penHit, XValue, YValue; + if (vt11.ctxBG === null) { + vt11Initialize(); + } + if (vt11.DPC == vt11.refreshDPC || vt11.refreshTime < Date.now()) { + vt11Refresh(); + } + count = 0; + vt11.refreshOn = 1; + vt11.DSR &= ~0x8000; // clear done + do { + if ((instruction = vt11Read(vt11.DPC)) >= 0) { + vt11.DPC = (vt11.DPC + 2) & 0xffff; + if (!(instruction & 0x8000)) { // data vs control + mode = (vt11.DSR >> 11) & 0xf; // data values depend on stored mode + intensify = instruction & 0x4000; + if (vt11.DEBUG) console.log((vt11.DPC - 2).toString(8) + " " + instruction.toString(8) + " Data mode " + mode.toString(2) + " I " + (intensify >> 14) + " X " + vt11.XRegister + " Y " + vt11.YRegister); + switch (mode) { + case 0: // character mode + break; + case 1: // short vector mode + case 6: // relative point mode + XValue = ((instruction >> 7) & 0x3f); + if (instruction & 0x2000) { + XValue = vt11.XRegister - XValue; + } else { + XValue = vt11.XRegister + XValue; + } + YValue = (instruction & 0x3f); + if (instruction & 0x40) { + YValue = vt11.YRegister - YValue; + } else { + YValue = vt11.YRegister + YValue; + } + break; + case 2: // long vector mode + case 3: // point mode + XValue = (instruction & 0x3ff); + if (mode == 2) { + if (instruction & 0x2000) { + XValue = vt11.XRegister - XValue; + } else { + XValue = vt11.XRegister + XValue; + } + } + if ((instruction = vt11Read(vt11.DPC)) >= 0) { + vt11.DPC = (vt11.DPC + 2) & 0xffff; + YValue = (instruction & 0x3ff); + if (mode == 2) { + if (instruction & 0x2000) { + YValue = vt11.YRegister - YValue; + } else { + YValue = vt11.YRegister + YValue; + } + } + if (vt11.DEBUG) console.log((vt11.DPC - 2).toString(8) + " " + instruction.toString(8) + " Extend " + " Xvalue " + XValue + " Yvalue " + YValue); + } + break; + case 4: // graph x mode + YValue = YRegister + vt11.graphIncrement; + XValue = instruction & 0x3ff; + break; + case 5: // graph y mode + XValue = XRegister + vt11.graphIncrement; + YValue = instruction & 0x3ff; + break; + } + if (instruction >= 0) { + penHit = 0; + if (!(vt11.DSR & 0x8) || vt11.blinkFlag) { // if not blinking or in blink cycle paint this... + if (mode == 0) { // character mode + vt11PaintChar(vt11.ctxBG, instruction & 0x7f); + vt11PaintChar(vt11.ctxBG, (instruction >> 8) & 0x7f); + if (vt11.DEBUG) console.log((vt11.DPC - 2).toString(8) + " write text " + String.fromCharCode(instruction & 0x7f, (instruction >> 8) & 0x7f)); + } else { + if (intensify) { + style = ((vt11.DSR >> 7) & 0xe).toString(16) + "0"; // style based on intensity + vt11.ctxBG.beginPath(); + vt11.ctxBG.strokeStyle = "#" + style + style + style; + if (mode == 3 || mode == 6) { + vt11.ctxFG.fillRect(XValue, 767 - YValue, 1, 1); + } else { + switch (vt11.DSR & 0x3) { + case 0: // solid + vt11.ctxBG.setLineDash([]); + break; + case 1: // long dash + vt11.ctxBG.setLineDash([8, 8]); + break; + case 2: // short dash + vt11.ctxBG.setLineDash([4, 4]); + break; + case 3: // dot dash + vt11.ctxBG.setLineDash([2, 2]); + break; + } + vt11.ctxBG.moveTo(vt11.XRegister, 767 - vt11.YRegister); + vt11.ctxBG.lineTo(XValue, 767 - YValue); + vt11.ctxBG.stroke(); + } + + if (vt11.penInterrupt) { + if (vt11DistanceCheck(vt11.XRegister, vt11.YRegister, XValue, YValue, vt11.mouseX, 767 - vt11.mouseY)) { + penHit = 1; + } + } + } + } + } + if (mode != 0) { + vt11.XRegister = XValue; + vt11.YRegister = YValue; + } + if (penHit) { + interrupt(1, 0, 4 << 5, 0324); + vt11.DSR |= 0x8000; // set done + return; + } + } + } else { + mode = (instruction >> 11) & 0xf; // control words contain a mode + if (vt11.DEBUG) console.log((vt11.DPC - 2).toString(8) + " " + instruction.toString(8) + " Control mode " + mode.toString(2)); + if (mode <= 7) { // set graphics mode + if (mode != 7) { // mode + vt11.DSR = (vt11.DSR & 0x87ff) | (instruction & 0x7800); + } + if (instruction & 0x400) { // intensity + vt11.DSR = (vt11.DSR & 0xf8ff) | ((instruction << 1) & 0x700); + } + if (instruction & 0x40) { // Light pen interrupt + vt11.penInterrupt = (instruction & 0x20); + } + if (instruction & 0x10) { // blink + vt11.DSR = (vt11.DSR & 0xfff7) | (instruction & 0x8); + } + if (instruction & 4) { // line type + vt11.DSR = (vt11.DSR & 0xfffc) | (instruction & 0x3); + } + } else { + switch (mode) { + case 0xc: // jump + if ((instruction = vt11Read(vt11.DPC)) >= 0) { + if (vt11.DEBUG) console.log((vt11.DPC).toString(8) + " " + instruction.toString(8) + " Jump to " + instruction.toString(8)); + vt11.DPC = instruction; + } + break; + case 0xe: // status register a + if (instruction & 0x200) { // set stop interrupt enable + vt11.stopInterrupt = instruction & 0x100; + } + if (instruction & 0x80) { // set LP intensity hit register? + vt11.DSR = (vt11.DSR & 0xff7f) | ((instruction << 1) & 0x80); // set LP flag (0=on, 1=off!!!) + } + if (instruction & 0x20) { // set italics + instruction & 0x10; + } + if (instruction & 0x404) { + if (instruction & 0x4) { + vt11.refreshOn = 0; + } + instruction = -1; // halt on either DPU stop or line frequency + } + break; + case 0xf: // status register b + if (instruction & 0x40) { // set graphplot increment register + vt11.grphIncrement = instruction & 0x3f; + } + break; + } + } + } + } + if (instruction >= 0 && ++count > 2000) { + setTimeout(vt11Execute, 0); // do a batch of instruction but if too many schedule more for later + return; + } + } + while (instruction >= 0); + if (vt11.DEBUG) console.log((vt11.DPC).toString(8) + " **end** X " + vt11.XRegister + " Y " + vt11.YRegister); + if (vt11.stopInterrupt) { + interrupt(1, 20, 4 << 5, 0320); + } + vt11.DSR |= 0x8000; // set done +} + +function accessVT11(physicalAddress, data, byteFlag) { + var result; + switch (physicalAddress & ~1) { + case 017772000: // vt11.DPC + result = insertData(vt11.DPC, physicalAddress, data, byteFlag); + if (result >= 0) { + if (data >= 0 && result != 0) { + if (vt11.DEBUG) console.log((vt11.DPC).toString(8) + " **write to DPC** " + result.toString(8) + " X " + vt11.XRegister + " Y " + vt11.YRegister); + if (!(result & 1)) { + vt11.DPC = result; + } + if ((vt11.DSR & 0x8000)) { // need better track of whether active + vt11Execute(); + } + } + } + break; + case 017772002: // vt11.DSR + result = vt11.DSR; + break; + case 017772004: // light pen Y ?? fix to add top bits + result = (vt11.penX & 0x3ff) | ((vt11.graphIncrement & 0x2f) << 10); // return light pen X location + break; + case 017772006: // light pen X ?? fix to add top bits + result = vt11.penY & 0x3ff; // return light pen Y location + break; + default: + CPU.CPU_Error |= 0x10; + return trap(4, 134); + } + return result; +} \ No newline at end of file