From 3cd55da7dd95e0d995352707abb2dee34c66adb7 Mon Sep 17 00:00:00 2001 From: James Hagerman Date: Sun, 25 Feb 2018 20:10:00 -0800 Subject: [PATCH] Adding all original code by Paul Nankervis. Disk images will not be able to live in GitHub repo itself, but will be available as a Release download. --- .gitignore | 1 + RSTSv06c.html | 1970 ++++++++++++++++++++++++++++++ RSTSv06c.jpg | Bin 0 -> 47021 bytes bootcode.js | 143 +++ iopage.js | 1489 +++++++++++++++++++++++ macro-asm/boot.mac | 747 ++++++++++++ macro-asm/chaser.mac | 31 + macro-asm/forth.mac | 2716 ++++++++++++++++++++++++++++++++++++++++++ macro-asm/odt11.mac | 1078 +++++++++++++++++ pdp11-45.html | 1502 +++++++++++++++++++++++ pdp11-45.svg | 82 ++ pdp11-70.svg | 23 + pdp11.html | 2350 ++++++++++++++++++++++++++++++++++++ pdp11.js | 2176 +++++++++++++++++++++++++++++++++ vt11.js | 376 ++++++ 15 files changed, 14684 insertions(+) create mode 100755 .gitignore create mode 100755 RSTSv06c.html create mode 100755 RSTSv06c.jpg create mode 100755 bootcode.js create mode 100755 iopage.js create mode 100755 macro-asm/boot.mac create mode 100755 macro-asm/chaser.mac create mode 100755 macro-asm/forth.mac create mode 100755 macro-asm/odt11.mac create mode 100755 pdp11-45.html create mode 100755 pdp11-45.svg create mode 100755 pdp11-70.svg create mode 100755 pdp11.html create mode 100755 pdp11.js create mode 100755 vt11.js 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 @@ + + + + + Installing RSTS V6 (V06C) from tape + + + +

Installing RSTS V6

+

+ +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/
+

+

Example of a system build with the modified tape

+

+

+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 0000000000000000000000000000000000000000..815a08774140dea755e77199e4a1613437116e48 GIT binary patch literal 47021 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 XGzcA>#hT$DTAsj-$Gfc%zX$#o;v|Y` literal 0 HcmV?d00001 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$ ; + CMPB R0,#10 + BNE 4$ ; BACK SPACE +2$: TST LENGTH ; DATA IN BUFFER? + BEQ 3$ + DEC LENGTH ; LENGTH + LDA #DEL + JSR PC,PRINT +3$: BR 9$ +4$: CMPB R0,#40 + BLT 9$ ; BELOW SPACE + CMPB R0,#177 + BGE 9$ ; SKIP STUFF TOO BIG + JSR PC,ONECHR ; ECHO CHAR + CMPB R0,#172 + BGT 5$; ABOVE Z + CMPB R0,#141 + BLT 5$ ; BELOW A + BICB #40,R0 ; CONVERT TO UPPER CASE +5$: CMP LENGTH,#254 + BGE 9$ + MOVB R0,-(SP) + MOV BUFFER,R0 + ADD LENGTH,R0 + MOVB (SP)+,(R0) + INC LENGTH + BR 9$ + +7$: MOV BUFFER,R0 + ADD LENGTH,R0 + CLRB (R0) + LDA #CMD + MOV R0,@#240 ; CALL CMD AT PRIORITY 2 + MOV #100,@#242 + MOV #2000,@#177772 +9$: MOV (SP)+,R0 + RTT + +CMD: + CLR @#177772 ; NO MORE PIR CALLS + MOV R0,-(SP) + MOV R1,-(SP) + MOV R2,-(SP) + MOV R3,-(SP) + MOV R4,-(SP) + LDA #EOL + JSR PC,PRINT +1$: BITB #100,@#177564 + BNE 1$ + CLR R4 + MOV #CMDLST,R3 ; CMD LIST +3$: MOVB (R3)+,R0 + BEQ 13$ + MOV BUFFER,R2 ; USER CMD +4$: MOVB (R2)+,R1 + BEQ 15$ ; NULL COMMAND? + CMPB R1,#40 + BEQ 4$ ; SKIP SPACE +5$: CMPB R0,R1 + BNE 7$ + MOVB (R3)+,R0 + BEQ 9$ + MOVB (R2)+,R1 + BEQ 9$ + CMPB R1,#40 + BNE 5$ + BR 9$ +7$: MOVB (R3)+,R0 + BNE 7$ + INC R4 + BR 3$ +9$: ASL R4 + LDA #CMDTBL + ADD R0,R4 + ADD (R4),R4 + JSR PC,(R4) ;EXECUTE + BR 15$ + +13$: LDA #UNKMSG + JSR PC,PRINT + +15$: CLR LENGTH + LDA #PROMPT + JSR PC,PRINT + MOV (SP)+,R4 + MOV (SP)+,R3 + MOV (SP)+,R2 + MOV (SP)+,R1 + MOV (SP)+,R0 + RTT + +;ODT = 170000 + +CMDTBL: .WORD BOOT-.,HELP-.,LIGHTS-.,HALT-.,TEST-.,DIAG-.,CHASER-. + .WORD RELOC-. ;,ODT-. +HLPMSG: .ASCII 'Commands are Boot, Halt, Test, Diag, Lights, and Help' + .ASCIZ <15><12>'Boot devices are RK RL RP or TM'<15><12> +EOL: .BYTE 15,12,0 +DEL: .BYTE 10,40,10,0; +CMDLST: .ASCIZ 'BOOT' + .ASCIZ 'HELP' + .ASCIZ 'LIGHTS' + .ASCIZ 'HALT' + .ASCIZ 'TEST' + .ASCIZ 'DIAG' + .ASCIZ 'CHASER' + .ASCIZ 'RELOCATE' + .ASCIZ 'ODT' + .BYTE 0 +UNKMSG: .ASCIZ 'Unknown command'<12><15> +BANNER: .ASCIZ 'Paul Nankervis - paulnank@hotmail.com'<12><15><12><15> +PROMPT: .ASCIZ 'BOOT> ' +BADBOO: .ASCIZ 'Unknown boot device'<12><15> +PERMSG: .ASCIZ ' clock ticks'<12><15> +LOOMSG: .ASCIZ ' iterations'<12><15> + + .EVEN + +HELP: + LDA #HLPMSG + JSR PC,PRINT + RTS PC + +HALT: + HALT + LDA #EOL + JSR PC,PRINT + RTS PC + +CHASER: + MOV #1,R0 + ROL R0 + RESET + BR .-4 + +RELOC: + SPL 7 + MOV @#177570,R3 + BIC #1,R3 + CMP R3,#4000 + BLO 10$ + MOV #4000,R3 +10$:MOV R3,R2 + + LDA #START + MOV R0,R1 + LDA #END +20$: MOV (R1)+,(R2)+ + CMP R1,R0 + BLO 20$ + JMP (R3) + +TEST: + CLR CLKTIC + + MOV #15000,R5 +10$: + MOV R5,R4 + ASR R4 +15$:CLR R0 + MOV R5,R1 + DIV R4,R0 + MOV R0,R2 + MUL R4,R2 + ADD R3,R1 + CMP R1,R5 + BEQ 20$ + HALT +20$:SOB R4,15$ + SOB R5,10$ + + LDA #PERMSG+5 + MOV CLKTIC,R3 + +25$:CLR R2 + DIV #10,R2 + ADD #'0,R3 + MOVB R3,-(R0) + MOV R2,R3 + BNE 25$ + + JSR PC,PRINT + RTS PC + +LOOCNT: .WORD 0 + +DIAG: + CLR LOOCNT + CLR CLKTIC +DIAGLP: + CLR R5 + BMI L1 + BVS L1 + BHI L1 + BLT L1 + BLOS L2 +L1: HALT + +L2: DEC R5 + BPL L3 + BEQ L3 + BGE L3 + BLE L4 +L3: HALT + +L4: ROR R5 + BVC L5 + BCC L5 + BNE L6 +L5: HALT + +L6: MOVB #17,@#177776 + BPL L7 + BVC L7 + BLT L7 + BHI L7 + BMI L8 +L7: HALT + +L8: MOV #77777,R3 + ADD #77777,R3 + CMPB @#177776,#5 + BNE L9 + HALT + +L9: MOVB #1700,R4 + BPL L10 + CMP R4,#177700 + BEQ L11 +L10: HALT + +L11: CLRB R4 + CMP R4,#177400 + BEQ L12 + HALT + +L12: MOV #125252,R5 + MOV R5,R0 + MOV R0,R1 + MOV R1,R2 + MOV R2,R3 + MOV R3,R4 + MOV R4,R5 + SUB R5,R1 + BLT NODIAG + BEQ L13 +NODIAG: HALT + +L13: ROL R2 + BCC L14 + BLT L15 +L14: HALT + +L15: ADD R2,R3 + INC R3 + COM R3 + ADD R3,R1 + BCS L16 + BLE L17 +L16: HALT + +L17: ROR R4 + BIS R4,R3 + ADD R5,R3 + INC R3 + BCS L18 + DEC R1 + BLT L19 +L18: HALT + +L19: COM R0 + BLOS L20 + HALT + +L20: BIC R0,R1 + ADD R1,R1 + BGT L21 + BLE L22 +L21: HALT + +L22: SWAB R1 + CMP R1,#052125 + BNE L23 + BIT R4,R5 + BGT L23 + COM R5 + BNE L24 +L23: HALT + +L24: MOVB #177401,R0 + BPL L26 +L25: HALT + +L26: SOB R0,L25 + CLR R1 +L27: INC R1 + SOB R0,L27 + TST R0 + BNE L28 + TST R1 + BEQ L29 +L28: HALT + +L29: LDA #N1 + MOV R0,PC + HALT + +N1: TST (PC)+ + HALT + +N2: LDA #N3 + MOV R0,-(SP) + RTS PC + HALT + +N3: LDA #N4 + CLR -(SP) + MOV R0,-(SP) + RTI + HALT + +N4: JMP N5 + HALT + +N5: MOV #3,R5 + LDA #N6 + CLR @#6 + MOV R0,@#4 + TST -(R5) + HALT +N6: ADD #4,SP + + CLR R0 + MOV #7777,R1 +N7: MOV R0,R2 + ADD #5,R2 + CMP (R0)+,-5(R2) + BEQ N8 + HALT +D1: .WORD 0 + +N8: SOB R1,N7 + + INC LOOCNT + BCC 10$ + HALT +10$: CMP CLKTIC,#2200 + BHI 20$ + JMP DIAGLP +20$: LDA #LOOMSG+5 + MOV LOOCNT,R3 + +25$:CLR R2 + DIV #10,R2 + ADD #'0,R3 + MOVB R3,-(R0) + MOV R2,R3 + BNE 25$ + + JSR PC,PRINT + RTS PC + +MMR0=177572 +MMR1=177574 +MMR2=177576 +MMR3=172516 + +LIGHTS: + TST LGHTON + BEQ 1$ + CLR LGHTON + RTS PC + +1$: SPL 7 + CLR @#MMR0 + MOV #77406,R4 ;NORMAL PDR VALUE + MOV #172300,R3 ;KERNEL MAP + CLR R1 ;BASE I AT ZERO + CLR R2 ;BASE D AT ZERO + JSR PC,20$ + MOV #177600,@#172376 ; KERNEL D HAS I/O SPACE + MOV #177600,R3 ;USER MAP + CLR R1 ;BASE I AT ZERO + MOV #2000,R2 ;BASE D AT 200000 + JSR PC,20$ + MOV #177600,@#177676 ; USER D HAS I/O SPACE + + MOV #16.,R0 + MOV #172200,R3 ;SUPER MAP +5$: MOV R4,(R3)+ ;INITIALIZE SUPER PDRS + SOB R0,5$ + + MOV #141400,R0 ;PHYSICAL ADDRESS SUPER CODE + MOV #67,R1 ;22 BIT with UB MAPPING + BIT #10,@#177570 ;USE 18 BIT IF SWITCH 3 + BEQ 8$ + MOV #6000,R0 ;ALTERNATE ADDRESS OF SUPER CODE + MOV #7,R1 ;18 BIT THEN (NO UB MAP) + +8$: MOV R0,@#172244 ; BASE SUPER I (PAR 2) #40000 + MOV R0,@#172264 ; BASE SUPER D (PAR 2) #40000 + MOV #177600,@#172276 ; SUPER D HAS I/O SPACE + MOV #010000,@#177776 ; SET PM TO SUPER + + MOV R1,@#MMR3 ;SET MAPPING MODE + MOV #1,@#MMR0 ;ENABLE MAP + + MOV #40200,R3 ; SUPER CODE VIRTUAL ADDRESS + LDA #SUPERS ; ADDRESS SUPER CODE + MOV R0,R2 + LDA #SUPERE ; END OF CODE +10$:MOV (R2)+,-(SP) + MTPI (R3)+ + CMP R2,R0 + BLO 10$ + + MOV #40200,LGHTON ;CHANGE IDLE TASK + SPL 0 + RTS PC + +20$:MOV #8.,R0 ;SUBROUTINE TO SET UP MAPPING PDR/PAR +25$:MOV R2,60(R3) ;LOAD D PAR + MOV R1,40(R3) ;LOAD I PAR + MOV R4,20(R3) ;LOAD D PDR + MOV R4,(R3)+ ;LOAD I PDR + ADD #200,R1 + ADD #200,R2 + SOB R0,25$ + RTS PC + + +;TO BE COPIED TO SUPER SPACE #40200 +;#40000 TO #40101 FOR WAIT & JMP INSTRUCTIONS +SUPERS: + MOV #37,R0 ;LOAD PATTERN + MOV #174000,R1 + BIT #1,@#177570 + BEQ 10$ + MOV #7417,R0 + MOV R0,R1 + COM R1 + BIT #2,@#177570 + BEQ 10$ + MOV #36163,R0 + MOV #37000,R1 + +10$: MOV R1,R2 + SUB #2,R2 + BIC #1,R2 ; VIRTUAL ADDRESS FOR WAIT + + MOV R2,R3 + BIC #177701,R3 ;ADDRESS OFFSET + MOV #0000001,40000(R3) ; WRITE WAIT + MOV #0000113,40002(R3) ; WRITE JMP (R3) + + MOV R2,R4 + ASH #-6,R4 + BIC #177600,R4 + MOV @#172244,R3 + SUB R4,R3 ;PAR ADDRESS BASE FOR WAIT + + MOV R2,R4 + ASH #-12.,R4 + BIC #177761,R4 ;PAR SELECT OFFSET + MOV R3,172240(R4) ;SUPER I SPACE + + MOV R1,R5 ;VIRTUAL ADDRESS FOR JMP + ASH #-12.,R5 + BIC #177761,R5 ;PAR SELECT OFFSET + CMP R5,R4 ;SAME PAR ? + BEQ 30$ + MOV R3,172240(R5) ;SUPER I SPACE + +30$: MOV #3,R4 ;REPEAT COUNT + MOV PC,R3 + ADD #45$-.,R3 +40$: JMP (R2) ; JMP TO WAIT +45$: SOB R4,40$ + + BIT #4,@#177570 + BNE 50$ + MOV R0,R2 + ROR R2 ;ROTATE PATTERN + ROL R1 + ROR R0 + BR 10$ +50$:MOV R0,R2 + ROL R2 + ROR R1 + ROL R0 + BR 10$ +SUPERE: + + +BOOT: + CLR R3 ;UNIT +1$: MOVB (R2)+,R1 + BEQ BOOTRK ;DEFAULT DEVICE IS RK0 + CMPB R1,#40 + BEQ 1$ + CMPB R1,#'R + BEQ 5$ + + CMPB R1,#'T + BNE 4$ + MOVB (R2)+,R1 + CMPB R1,#'M + BEQ 7$ + +4$: LDA #BADBOO + JSR PC,PRINT + RTS PC + +5$: MOVB (R2)+,R1 ; HOPEFULLY K, L OR P +7$: MOVB (R2)+,R0 ; DIGIT + BEQ 9$ + CMPB R0,#40 + BEQ 9$ + CMPB R0,#'7 + BGT 11$ + SUB #'0,R0 + BLT 11$ + ASL R3 + ASL R3 + ASL R3 + BIS R0,R3 ;PUT DIGIT INTO UNIT + BR 7$ +9$: LDA #START + MOV R0,@#4 + CLR @#6 + CMPB R1,#'K + BEQ BOOTRK + CMPB R1,#'L + BEQ BOOTRL + CMPB R1,#'P + BEQ BOOTRP + CMPB R1,#'M + BEQ BOOTTM + +11$: LDA #UNKMSG + JSR PC,PRINT + RTS PC + +RLCS=174400 +BOOTRL: + RESET + SWAB R3 ; UNIT NUMBER + MOV #RLCS,R1 ; CSR + MOV #13,4(R1) ; CLR ERR + BIS #4,R3 ; UNIT+GSTAT + MOV R3,(R1) ; ISSUE CMD + TSTB (R1) ; WAIT + BPL .-2 + CLRB R3 + BIS #10,R3 ; UNIT+RDHDR + MOV R3,(R1) ; ISSUE CMD + TSTB (R1) ; WAIT + BPL .-2 + MOV 6(R1),R2 ; GET HDR + BIC #77,R2 ; CLR SECTOR + INC R2 ; MAGIC BIT + MOV R2,4(R1) ; SEEK TO 0 + CLRB R3 + BIS #6,R3 ; UNIT+SEEK + MOV R3,(R1) ; ISSUE CMD + TSTB (R1) ; WAIT + BPL .-2 + CLR 2(R1) ; CLR BA + CLR 4(R1) ; CLR DA + MOV #-512.,6(R1) ; SET WC + CLRB R3 + BIS #14,R3 ; UNIT+READ + MOV R3,(R1) ; ISSUE CMD + TSTB (R1) ; WAIT + BPL .-2 + BIC #377,(R1) + CLR R2 + CLR R3 + CLR R4 + CLR R5 + CLR PC + +RKDA=177412 +READGO=5 +BOOTRK: + RESET + SWAB R3 ; UNIT NUMBER + ASL R3 + ASL R3 + ASL R3 + ASL R3 + ASL R3 + MOV #RKDA,R1 ; CSR + MOV R3,(R1) ; LOAD DA + CLR -(R1) ; CLEAR BA + MOV #-256.*2,-(R1) ; LOAD WC + MOV #READGO,-(R1) ; READ & GO + CLR R2 + CLR R3 + CLR R4 + CLR R5 + TSTB (R1) + BPL .-2 + CLRB (R1) + CLR PC + +RPCSR=0176700 +BOOTRP: + RESET + MOV #RPCSR, R1 + MOV #0000040, 10(R1) ; RESET + MOV R3, 10(R1) ; SET UNIT + MOV #0000021, (R1) ; PACK ACK + MOV #0010000, 32(R1) ; 16B MODE + MOV #-512., 2(R1) ; SET WC + CLR 4(R1) ; CLR BA + CLR 6(R1) ; CLR DA + CLR 34(R1) ; CLR CYL + MOV #0000071, (R1) ; READ + TSTB (R1) ; WAIT + BPL .-2 + CLRB (R1) + MOV R3,R0 + CLR PC + + +MTCMA=0172526 +BOOTTM: + RESET + MOV R3, R0 ; UNIT + MOV #MTCMA, R1 ; MTCMA +99$:CLR (R1) + MOV #-1, -(R1) ; MTBRC + MOV R0,R2 + SWAB R2 + ADD #60011, R2 + MOV R2, -(R1) ; SPACE + GO + TSTB (R1) ; MTC + BPL .-2 + MOV R0,R2 + SWAB R2 + ADD #60003, R2 + MOV R2, (R1) ; READ + GO + TSTB (R1) ; MTC + BPL .-2 + CLR R2 + CLR R3 + MOV #99$, R4 ; POINT PAST ADDRESS MTCNA + CLR R5 + CLR PC + +END: .END START diff --git a/macro-asm/chaser.mac b/macro-asm/chaser.mac new file mode 100755 index 0000000..2bf6e37 --- /dev/null +++ b/macro-asm/chaser.mac @@ -0,0 +1,31 @@ + .title chaser + +; Can be loaded and executed via front panel switches + +; Address Data +; 001000 012700 mov #1,r0 Load 1 into R0 +; 001002 000001 +; 001004 006100 rol r0 Rotate R0 left - right to left pattern +; 001006 000005 reset Initialise bus (70ms) +; 001010 000775 br .-4 Loop back to 'rol r0' + +start: + mov #1,r0 ;Load 1 into R0 + rol r0 ;Rotate R0 left + reset ;Initialise bus (70ms) + br .-4 ;Loop back to 'rol r0' + +; Or alternatively a count pattern: + +; Address Data +; 001000 005000 clr r0 Initialize R0 +; 001002 005200 inc r0 Increment counter +; 001004 000005 reset Initialise bus (70ms) +; 001006 000775 br .-4 Loop back to 'inc r0' + + clr r0 ;Initialize R0 + inc r0 ;Increment counter + reset ;Initialise bus (70ms) + br .-4 ;Loop back to 'rol r0' + + .end start \ No newline at end of file diff --git a/macro-asm/forth.mac b/macro-asm/forth.mac new file mode 100755 index 0000000..c35e772 --- /dev/null +++ b/macro-asm/forth.mac @@ -0,0 +1,2716 @@ +.TITLE F.I.G. +; **************************************************************** +; +; PDP-11 FORTH INTRODUCTION PDP-11 FORTH +; +; **************************************************************** +; +; +; +; PDP-11 FORTH RT-11, RSX-11M, AND STAND-ALONE JANUARY 1980 +; +; +; +; DEVELOPED BY THE +; FORTH INTEREST GROUP / FORTH IMPLEMENTATION TEAM +; P.O. BOX 1105 +; SAN CARLOS, CA. 94070 +; +; +; IMPLEMENTED BY +; JOHN S. JAMES +; P.O. BOX 348 +; BERKELEY, CA. 94701 +; +; +; THIS SYSTEM IS IN THE PUBLIC DOMAIN AND CAN BE USED +; WITHOUT RESTRICTION. PLEASE CREDIT THE FORTH INTEREST +; GROUP IF YOU REPUBLISH SUBSTANTIAL PORTIONS. +; +; +; THE FORTH INTEREST GROUP / FORTH IMPLEMENTATION TEAM +; ALSO HAS DEVELOPED NEARLY IDENTICAL VERSIONS OF THIS +; SYSTEM FOR THE +; 8080 +; 6800 +; 6502 +; 9900 +; PACE +; +; +; FOR MORE INFORMATION, WRITE: +; +; JOHN S. JAMES +; P.O. BOX 348 +; BERKELEY, CA. 94701 +; +; OR +; +; FORTH INTEREST GROUP +; P.O. BOX 1105 +; SAN CARLOS, CA. 94070 +; +; +; 'PDP' AND 'RSX' ARE TRADEMARKS OF DIGITAL EQUIPMENT CORPORATION. +.PAGE +; THIS FORTH SYSTEM HAS +; - FULL LENGTH NAMES +; - EXTENSIVE COMPILE-TIME CHECKS AND ERROR MESSAGES +; - DOUBLE INTEGER I/O +; - A FORTH ASSEMBLER, PERMITTING STRUCTURED, INTERACTIVE +; DEVELOPMENT OF DEVICE HANDLERS, SPEED-CRITICAL +; ROUTINES, AND LINKAGE TO OPERATING SYSTEMS OR TO +; SUBROUTINE PACKAGES WRITTEN IN OTHER LANGUAGES. +; - STRING-HANDLING ROUTINES +; - A STRING-SEARCH EDITOR +; - LINKED VOCABULARIES +; - HOOKS FOR MULTITASKING/MULTIUSER (CURRENTLY SINGLE TASK) +; - AND AS CURRENTLY CONFIGURED IT RUNS IN A 24K BYTE +; TASK IMAGE (THIS INCLUDES BUFFERS, OPERATING-SYSTEM +; AREA, AND ROOM FOR SUBSTANTIAL ADDITIONAL FORTH +; PROGRAMMING) ON ANY PDP-11 OR LSI-11 CPU, WITH OR +; WITHOUT HARDWARE MULTIPLY/DIVIDE. THIS DISKETTE +; WILL BOOT AND RUN STAND-ALONE; ALSO IT CONTAINS A +; SOURCE PROGRAM WHICH CAN BE ASSEMBLED TO RUN +; UNDER RT-11, RSX-11M, OR STAND-ALONE. THIS SYSTEM +; CAN BE MODIFIED TO INTERFACE WITH ANY OTHER OPERATING +; SYSTEM WHICH SUPPORTS READ AND WRITE A CHARACTER, +; DETECT A CHARACTER (OPTIONAL), AND READ AND WRITE +; A DISK BLOCK. +; +; +; IT IS ALIGNED WITH THE 1978 STANDARD OF THE FORTH INTERNATIONAL +; STANDARDS TEAM. +; +; +; +; RECOMMENDED DOCUMENTATION: +; - A FORTH LANGUAGE MANUAL. WE PARTICULARLY RECOMMEND EITHER +; (A) 'USING FORTH', BY FORTH, INC. +; OR +; (B) 'A FORTH PRIMER', BY W. RICHARD STEVENS, KITT +; PEAK NATIONAL OBSERVATORY. +; EITHER IS AVAILABLE THROUGH THE FORTH INTEREST GROUP, +; P.O. BOX 1105, SAN CARLOS, CA. 94070. +; - PDP-11 FORTH USER'S GUIDE, AVAILABLE FROM JOHN S. JAMES, +; ADDRESS ABOVE. +; - FORTH REFERENCE CARD FOR THE FORTH IMPLEMENTATION TEAM +; COMMON MODEL, AVAILABLE FROM FIG. +; - 'FIG-FORTH INSTALLATION MANUAL', ALSO FROM FIG. +; +; +; +; ACKNOWLEDGMENTS: +; THIS FORTH SYSTEM (IN 'FORTH.MAC') IS A GROUP PRODUCT +; OF THE FORTH IMPLEMENTATION TEAM OF THE FORTH INTEREST +; GROUP (P.O. BOX 1105, SAN CARLOS CA. 94070). THE IMPLEMENTER +; IS RESPONSIBLE FOR THIS PDP-11 VERSION OF THE MODEL, AND FOR +; THE SOFTWARE ON SCREENS IN 'FORTH.DAT'. ALTHOUGH THE LATTER +; IS NOT AN OFFICIAL RELEASE OF THE F.I.G., THE CONTRIBUTIONS +; FROM MEMBERS OF THE GROUP ARE TOO NUMEROUS TO CITE +; INDIVIDUALLY. +; IN ADDITION WE APPRECIATE THE PDP-11 CODING +; IMPROVEMENTS SUGGESTED BY STUART R. DOLE, DOLE & FARMER, +; PO BOX 142, PETALUMA, CA. 94952; BY PAUL EDELSTEIN; +; BY RICK STEVENS OF KITT PEAK; AND OTHERS. +.PAGE +; **************************************************************** +; +; BRINGING UP THE SYSTEM +; +; **************************************************************** +; +; +; +; TO RUN STAND-ALONE: +; - BOOT THE DISKETTE LIKE ANY OTHER SYSTEM DISK, FROM DX0. +; FORTH SHOULD COME UP AND TYPE 'FIG FORTH' AND THE VERSION +; NUMBER. TEST AS DESCRIBED FOR RT-11 BELOW. +; - MAKE A COPY OF THE DISK; THIS STAND-ALONE SYSTEM DOES NOT +; PROTECT AGAINST ACCIDENTALLY OVERWRITING THE SYSTEM OR THE +; SOURCE PROGRAMS. TO MAKE AN EXACT COPY OF THE ENTIRE DISK, +; 1. PUT A BLANK DISK INTO THE SECOND DRIVE (DX1). FOR +; SAFETY, SET THE WRITE-PROTECT SWITCH ON THE DRIVE +; WHICH CONTAINS THE ORIGINAL SYSTEM DISK. +; 2. TYPE '38 LOAD', AND CARRIAGE RETURN. THE SYSTEM SHOULD +; RESPOND 'OK'. THEN TYPE 'COPY' AND RETURN. EACH OF +; THE 77 TRACKS WILL BE READ FROM DX0 AND WRITTEN ON DX1. +; - NOTE THE LAYOUT OF THE DISKETTE. IT IS SET UP TO BOOT AND +; RUN STAND-ALONE, BUT IT ALSO CONTAINS AN RT-11 DIRECTORY, +; AND A MACRO-11 SOURCE PROGRAM 'FORTH.MAC' (WHICH PRODUCED +; THIS LISTING). THIS ALLOWS THE SAME DISK TO BE BOOTED +; AND RUN, OR TO PROVIDE SOURCE FOR MODIFICATION AND RE-ASSEMBLY. +; AS PROVIDED, THE FILE 'FORTH.DAT' CONTAINS FORTH SCREENS +; 1-70. YOU CAN USE LOCATIONS BEYOND 70, BUT THESE WILL +; OVERWRITE THE 'FORTH.MAC' SOURCE PROGRAM. STAND-ALONE USERS +; MAY NEVER NEED TO USE THIS SOURCE, AND MAY WANT TO REMOVE IT +; AND USE THE SPACE FOR SOMETHING ELSE. MAKE A COPY FIRST. +; - STAND-ALONE USERS CAN ADD THEIR OWN OPERATIONS AND THEN +; SAVE A BOOTABLE IMAGE OF THE NEW SYSTEM. THE NEW OPERATIONS +; WILL BE AVAILABLE WHEN THE DISK IS BOOTED IN THE FUTURE. +; THE LOADER WHICH IS USED WILL ONLY LOAD IMAGES UP TO 7.9K; +; THIS LEAVES SEVERAL HUNDRED BYTES FOR NEW OPERATIONS, WHICH +; CAN INCLUDE EXTENDING THE SYSTEM BY BRINGING IN SOURCE OR +; OBJECT CODE. TO SAVE THE CURRENT SYSTEM, EXECUTE 'FORTH DEFINITIONS' +; IF NECESSARY TO GET INTO THE FORTH VOCABULARY, THEN 'DECIMAL 34 LOAD'. +; SOME WARNING MESSAGES WILL BE PRINTED (MSG #4); THEY CAN BE +; IGNORED. +; - IF YOU DO WANT TO RE-ASSEMBLE THE SYSTEM FOR STAND-ALONE +; USE (WHICH MOST USERS SHOULD NEVER FIND NECESSARY), +; YOU MUST USE RT-11 TO EDIT AND ASSEMBLE 'FORTH.MAC'. NOTE +; THAT ALTHOUGH THIS LISTING IS ASSEMBLED FOR STAND-ALONE, +; THE SOURCE PROGRAM SUPPLIED IS SET FOR RT-11 ASSEMBLY; +; COMMENT OUT THE 'RT11' DEFINITION, AND REMOVE THE COMMENTING +; ON 'ALONE'. ASSEMBLE, LINK, AND RUN, AND THE SYSTEM SHOULD +; COME UP STAND-ALONE. IMMEDIATELY REMOVE THE RT-11 SYSTEM DISK +; AND PLACE THE FORTH DISK IN DRIVE ZERO. TO REVISE +; THE BOOTABLE IMAGE ON THE FORTH DISK SO THAT YOUR NEW SYSTEM +; BOOTS STAND-ALONE, LIST SCREEN 34 (DECIMAL), AND FOLLOW THE +; INSTRUCTIONS THERE. THE RUN TAKES ABOUT ONE MINUTE. +; - THE BOOTABLE SYSTEM DOES NOT USE HARDWARE MULTIPLY AND DIVIDE. +; IF YOU DON'T HAVE RT-11 TO EDIT AND RECOMPILE WITH 'EIS' +; CONDITIONAL ASSEMBLY, THE MULTIPLY/DIVIDE ROUTINES CAN BE +; PATCHED. IF YOU PATCH FROM THE KEYBOARD MONITOR, THE +; RESTART ADDRESS IS 1000 OCTAL ( COLD START) OR 1004 (WARM +; START). SAVE THE NEW VERSION AS A BOOTABLE SYSTEM, AS +; DESCRIBED ABOVE. +; - THE SKEWED DISK I/O OPERATIONS SKIP TRACK ZERO, FOR COMPATIBILITY +; WITH STANDARD PDP-11 SECTOR SKEWING. THE PHYSICAL READ +; OPERATIONS ('RTS', 'WTS', 'NRTS', 'NWTS') CAN READ ANY SECTOR, +; HOWEVER. +; - ALSO THE SYSTEM AS DISTRIBUTED SKIPS THE FIRST 56 SECTORS +; (7 SCREENS) IN ORDER TO SKIP THE BOOT BLOCK AND AN +; RT-11 DIRECTORY. THIS CAUSES THE SCREEN POSITIONS TO BE THE +; SAME FOR STAND-ALONE AND FOR RT-11 (WHICH ACCESSES THE FILE +; 'FORTH.DAT'). YOU CAN CHANGE THIS BY CHANGING THE VALUE OF +; THE VARIABLES 'S-SKIP' (NUMBER OF SCREENS SKIPPED) AND +; 'S-USE' (NUMBER OF SCREENS USED BEFORE ACCESSING THE +; SECOND DISK). THESE VARIABLES CAN BE CHANGED AT ANY TIME, +; SO DISK SCREENS CAN BE READ INTO BUFFERS AND THEN FLUSHED +; TO DIFFERENT LOCATIONS ON THE DISK. +; - ADVANCED USERS MAY NOTE THAT THIS SYSTEM IS DESIGNED TO +; ALLOW THE MEMORY LAYOUT - NUMBER AND LOCATION OF DISK +; BUFFERS, LOCATION OF THE STACK, ETC. - TO BE CHANGED +; DYNAMICALLY, WITHOUT REASSEMBLY. +; +; +; TO BRING UP THIS SYSTEM UNDER RT-11: +; - BE SURE THAT RT-11 IS SELECTED BELOW. THE LINES DEFINING +; 'RSX11M' AND 'ALONE' SHOULD BE COMMENTED OUT; 'RT11' SHOULD +; NOT BE. NOTE THAT THIS DISK IS DISTRIBUTED READY FOR RT-11 +; ASSEMBLY (EVEN THOUGH THIS LISTING IS FOR STAND-ALONE). +; - IF YOU HAVE HARDWARE MULTIPLY/DIVIDE, ALSO REMOVE THE +; SEMICOLON FROM THE LINE DEFINING 'EIS'. +; - IF YOU ARE USING AN OLDER VERSION OF RT-11 (VERSION 2), +; YOU MAY NEED TO USE THE MACROS '..V2..' AND '.REGDEF'. +; - ASSEMBLE, LINK, AND RUN. THE SYSTEM SHOULD COME UP AND +; TYPE 'FIG-FORTH' AND THE VERSION NUMBER. +; - TEST THAT IT IS UP BY TRYING SOME ARITHMETIC OR DEFINITIONS, E.G. +; 88 88 * . (NOTE THAT THE '.' MEANS PRINT) +; : SQUARE DUP * ; +; 25 SQUARE . +; OR TYPE 'VLIST' FOR A LIST OF ALL THE FORTH OPERATIONS IN THE +; DICTIONARY. +; - THE DISK SHOULD WORK IF THE DISKETTE IS IN DRIVE 'DK'. +; MAKE SURE THAT 'DK' IS ASSIGNED TO WHATEVER PHYSICAL +; DRIVE YOU ARE USING - OR CHANGE LINE 'RTFILE:' IN +; 'FORTH.MAC'. TEST THE DISK BY TYPING +; 1 LIST +; WHICH SHOULD LIST THE SCREEN WHICH LOADS THE EDITOR, +; ASSEMBLER, AND STRING ROUTINES. +; - IN CASE YOU NEED TO GET A LISTING FROM THE ASSEMBLY OF +; 'FORTH.MAC' (NOT USUALLY NECESSARY), AND YOUR SYSTEM HAS +; ONLY DISKETTES (NO LARGER DISKS), THE 'ALLOCATE' OPTION +; IS NECESSARY BECAUSE OF THE SIZE OF THE '.LST' FILE +; (AROUND 230 BLOCKS). FIRST COPY 'FORTH.MAC' ONTO A +; SEPARATE DISKETTE BY ITSELF. THEN EXECUTE +; .MACRO /LIST:FORTH.LST /ALLOCATE:300. /NOOBJECT +; AND REPLY 'FORTH.MAC' WHEN ASKED FOR 'FILES?'. +; +; +; +; TO BRING UP THE SYSTEM UNDER RSX-11M: +; - THE DISKETTE PROVIDED IS IN RT-11 FILE FORMAT. THE TWO FILES +; MUST BE COPIED OFF THE DISKETTE INTO AN RSX DIRECTORY. THE +; 'FORTH.DAT' FILE MUST BE COPIED IN IMAGE MODE. ANY RSX +; DIRECTORY MAY BE USED. ASSUMING THE DISKETTE IS IN DRIVE 0, +; USE THE RSX COMMANDS: +; >FLX =DX:FORTH.MAC/RT +; >FLX =DX:FORTH.DAT/RT/IM +; INCIDENTALLY, 'FORTH.DAT' IS THE SYSTEM'S 'VIRTUAL MEMORY' +; FILE, USED FOR DISK I/O. THE REST OF THE SYSTEM (THIS +; PROGRAM ALONE) CAN RUN INDEPENDENTLY, EVEN IF 'FORTH.DAT' +; IS NOT AVAILABLE. +; - EDIT 'FORTH.MAC' TO SELECT RSX ASSEMBLY. CHANGE THE SEMICOLON +; TO COMMENT OUT 'RT11' NOT 'RSX11'. LET 'EIS' BE DEFINED IF +; YOU HAVE HARDWARE MULTIPLY/DIVIDE. +; - ASSEMBLE, TASK BUILD, AND RUN. TEST AS WITH RT11 ABOVE. +; - THE DISK I/O SHOULD WORK IF 'FORTH.DAT' IS IN THE DEFAULT +; DEVICE AND DIRECTORY. TEST AS ABOVE. +; +; +; +; THE SYSTEM AS SUPPLIED RESERVES 8000. BYTES FOR YOUR FORTH +; PROGRAMMING AND STACK. THIS IS ENOUGH FOR SUBSTANTIAL PROJECTS. +; (NOTE THAT THE EDITOR, ASSEMBLER, AND STRING PACKAGE, IF LOADED, +; USE MORE THAN 5K OF THIS.) TO CHANGE THIS MEMORY SIZE, CHANGE +; THE '8000.' WHICH IS IN THE LINES FOLLOWING THE LABEL 'DP:', +; NEAR THE END OF THIS PROGRAM. INCIDENTALLY, VERY FEW JOBS +; (E.G. RECURSION) WILL EVER USE MORE THAN 100 WORDS OF THIS SPACE +; FOR THE STACK; THE REST OF THE SPACE IS AVAILABLE FOR A STRING +; STACK (IF USED) OR FOR YOUR PROGRAMS - AND FORTH OBJECT CODE IS +; CONSIDERABLY MORE COMPACT THAN ASSEMBLY. +; +; +; +; THE FORTH VIRTUAL FILE 'FORTH.DAT' IS USED FOR STORING SOURCE +; PROGRAMS (OR DATA). THIS FILE HAS 70 1-K SCREENS (1-70), +; I.E. 140 PDP-11 DISK BLOCKS. SCREENS 4 AND 5 ARE USED BY THE +; SYSTEM FOR STORING ERROR AND WARNING MESSAGES. SCREENS 6-30 +; CONTAIN A TEXT EDITOR, ASSEMBLER, STRING PACKAGE, AND MISCELLANEOUS +; EXAMPLES. SCREENS 40 THROUGH 47 CONTAIN A BINARY STAND-ALONE +; SYSTEM (NOT USED UNDER RT-11 OR RSX-11M). USERS MAY WANT +; TO SAVE THEIR SOURCE PROGRAMS AND DATA IN THE BLANK SCREENS. +; THE SIZE OF THIS FORTH SCREENS FILE ('FORTH.DAT') CAN BE INCREASED +; IF NEEDED. IF THE SYSTEM IS TO BE BOOTED STAND-ALONE, THE LOCATION +; OF THE SYSTEM BINARY IMAGE ON THE DISK MUST NOT BE CHANGED; +; THEREFORE, IF THE DISK IS TO BE USED TO RUN STAND-ALONE, DO NOT +; USE RT-11 TO MOVE 'FORTH.DAT' TO ANOTHER PLACE ON THE DISK. +; +; +; +; +; +; NOTE THAT THE RT-11 AND RSX-11M SYSTEMS DO NOT ECHO CHARACTERS +; WHICH ARE INPUT FROM THE TERMINAL. INSTEAD, THEY LET THE OPERATING +; SYSTEM (RT-11 OR RSX-11M) ECHO THEM. THIS IS DONE SO THAT TYPING +; CONVENTIONS WILL BE THE SAME AS THE USER IS FAMILIAR WITH. ALSO, +; TO AVOID SWAPPING DELAYS, THE RSX VERSION OF 'KEY' READS A LINE OF +; CHARACTERS AT A TIME. +; +; +; +; +; CHANGE THESE LINES TO CONTROL CONDITIONAL ASSEMBLY: +; +RT11=1 ; COMMENTED OUT UNLESS RT-11 +;RSX11=1 ; COMMENTED OUT UNLESS RSX11M +;ALONE=1 ; COMMENTED OUT UNLESS STAND-ALONE +;EIS=1 ; COMMENTED OUT UNLESS HARDWARE MULTIPLY-DIVIDE +;LINKS=1 ; COMMENTED OUT UNLESS SUBROUTINE LINKAGE FROM +; FORTH TO OTHER LANGUAGES +; +;.PAGE +; **************************************************************** +; +; VARIATIONS FROM F.I.G. MODEL +; +; **************************************************************** +; +; +; 'FIRST' AND 'LIMIT' HAVE BEEN MADE USER VARIABLES, NOT CONSTANTS. +; THEREFORE WHEN THEY ARE USED, 'FIRST @' AND 'LIMIT @' ARE +; REQUIRED. +; +; ';CODE' AND 'FORTH' ARE NOT PURE CODE, SO THEY WERE MOVED TO THE +; END OF THE DICTIONARY. THIS IS SO THE BULK OF THE DICTIONARY +; COULD BE PUT IN PROM OR USED RE-ENTRANTLY. +; +; THE MACHINE-INDEPENDENT I/O SECTION WAS MOVED TO NEAR THE END OF +; THE DICTIONARY, BECAUSE IT IS NOT ALWAYS PURE CODE, AND ALSO TO +; ALLOW THE I/O TO BE REDEFINED WITHOUT REASSEMBLY. +; +; THIS SYSTEM MUST TEST FOR FIRST-TIME-THROUGH TERMINAL AND DISK +; I/O, TO AVOID ERRONEOUS ATTEMPT TO OPEN FILES TWICE AT LATER COLD +; STARTS. IT CLEARS DISK BUFFERS AT COLD START. +; +; CHANGES IN V 1.3.1 (PH): +; - CHANGED VERSION NUMBER FROM "1.3" TO "1.3.1". +; - CHANGED CPU FROM "11" (OCTAL) TO "11." (DECIMAL). +; - ADDED "BIC #177600,(S)" TO CLEAR PARITY BIT & UPPER +; BYTE FOR STANDALONE TERMINAL ASCII OUTPUT. +; - CHANGED ".WORD" TO ".BLKW" IN DSKBUF AREA SO STANDALONE +; BINARY IMAGE CAN BE VERIFIED TO FIT IN 017400 BYTES, +; TO GUARANTEE ROOM FOR THE 2-SECTOR BOOT LOADER. +; +.PAGE +; **************************************************************** +; +; SET UP REGISTERS AND MACROS. +; +; **************************************************************** +; +; +W=%2 ; TEMPORARY USED BY 'NEXT' MACRO (THE INNER INTERPRETER) +U=%3 ; POINTER TO THE USER AREA +IP=%4 ; FORTH INSTRUCTION COUNTER +S=%5 ; FORTH STACK POINTER +RP=SP ; FORTH RETURN-STACK POINTER +; +; NOTE - CODE ROUTINES CAN USE REGISTERS 0, 1, 4, AND 5, WITHOUT +; RESTORING THEM. +; +; +; MACRO DEFINITIONS +; +; +; +; THE 'HEAD' MACRO CREATES A FORTH DICTIONARY HEADER. ITS ARGUMENTS ARE: +; (1) LENGTH BYTE - THE LENGTH OF THE NAME BEING DEFINED. THE SIGN BIT +; OF THE LENGTH BYTE MUST BE SET, SO THAT THE SYSTEM WILL RECOGNIZE +; THE END OF A VARIABLE-LENGTH NAME FIELD; THEREFORE THE LENGTH BYTE +; IS GIVEN AS 200 OCTAL PLUS THE LENGTH. IF THE OPERATION IS +; IMMEDIATE, THE BIT NEXT TO THE SIGN BIT IS ALSO SET, SO THE LENGTH +; BYTE IS GIVEN AS 300 OCTAL PLUS THE LENGTH. +; (2) NAME - THE NAME OF THE OPERATION BEING DEFINED. +; (3) LCHAR - THE ASCII VALUE OF THE LAST CHARACTER OF THE NAME, WITH THE +; SIGN BIT SET. THE NAME FIELD MUST HAVE AN EVEN LENGTH (INCLUDING +; THE LENGTH BYTE), SO IF THE NUMBER OF CHARACTERS IN THE NAME IS +; EVEN, 'LCHAR' WILL BE GIVEN AS 240 (200 PLUS CODE FOR A SPACE). +; (4) LABEL - THE ASSEMBLY-LANGUAGE LABEL ASSOCIATED WITH THE 'CODE FIELD' +; OF THIS DICTIONARY HEADER. THESE LABELS ARE USED IN THE PRECOMPILED- +; FORTH SECTION OF THE SYSTEM. WHEN POSSIBLE, THE FORTH OPERATION +; NAME ITSELF IS USED AS THE ASSEMBLY LABEL; OTHERWISE AN ABBREVIATION +; IS USED. BY CONVENTION, THESE NAMES ARE LIMITED TO FIVE CHARACTERS, +; FOR CONSISTENCY AMONG VARIOUS ASSEMBLERS FOR DIFFERENT MICROPROCESSORS. +; (THE FORTH IMPLEMENTATION TEAM USES THE SAME LABELS IN ALL OF ITS +; VERSIONS.) +; (5) CODE - POINTER TO THE MACHINE-LANGUAGE "CODE ROUTINE" ASSOCIATED +; WITH THIS OPERATION TYPE OR DATA TYPE. E.G. FOR ANY COLON DEFINITION, +; THIS ARGUMENT IS 'DOCOL', THE LABEL OF A FIVE-INSTRUCTION ASSEMBLY +; ROUTINE WHICH USES THE RETURN STACK TO HANDLE THE NESTED EXECUTION +; OF ANOTHER LEVEL OF FORTH OPERATIONS. FOR ANY CONSTANT, THIS CODE +; ROUTINE IS 'DOCON', AND SIMILARLY FOR ALL OTHER DATA TYPES. +; THE CODE ARGUMENT MAY BE OMITTED. IN THAT CASE, THE 'HEAD' +; MACRO LEAVES THE CODE FIELD POINTING TWO BYTES BEYOND ITSELF, WHERE +; MACHINE-LANGUAGE CODE MUST BEGIN - AND THE OPERATION SO DEFINED IS +; CALLED A "PRIMITIVE". THE "NUCLEUS SECTION" OF THIS VERSION OF +; FORTH CONTAINS ABOUT 45 PRIMITIVES, FROM WHICH THE WHOLE SYSTEM +; IS BUILT; IN EFFECT, THESE PRIMITIVES DEFINE THE VIRTUAL FORTH +; MACHINE. (A FEW OPERATIONS IN THE "PRECOMPILED FORTH" SECTION +; OF THE SYSTEM HAVE BEEN REPLACED WITH PRIMITIVES, TO OPTIMIZE +; EXECUTION SPEED. AND WHEN A FORTH ASSEMBLER IS ADDED TO THIS +; SYSTEM, USERS WILL BE ABLE TO DEFINE THEIR OWN PRIMITIVES DIRECTLY +; IN FORTH, IMMEDIATELY READY FOR EXECUTION.) +; +; THE 'HEAD' MACRO CREATES A FORTH HEADER CONSISTING OF +; LENGTH BYTE - SIGN BIT SET +; NAME OF THE OPERATION - VARIABLE LENGTH - SIGN BIT SET ON LAST CHAR. +; LINK FIELD, WHICH POINTS TO THE BEGINNING OF THE PREVIOUS DICTIONARY +; HEADER (USED AT COMPILE TIME) +; CODE POINTER. +; +LINK=0 ; LAST LINK FIELD IS 0, INDICATING END OF THE DICTIONARY. + +; + .MACRO HEAD,LENGTH,NAME,LCHAR,LABEL,CODE +LINK2=. + .BYTE LENGTH + .ASCII ^NAME^ + .EVEN +.=.-1 + .BYTE LCHAR ; LAST CHARACTER OF NAME (OR BLANK FILL), + ; PASSED IN OCTAL, WITH HIGH BIT SET. + .WORD LINK +LINK=LINK2 +LABEL: .IF NB CODE + .WORD CODE + .IFF + .WORD .+2 + .ENDC + .ENDM +; +; +; +; THE 'NEXT' MACRO TRANSFERS CONTROL FROM ONE FORTH OPERATION TO THE +; 'CODE ROUTINE' OF THE NEXT. NOTICE THAT ONLY TWO INSTRUCTION +; EXECUTIONS ARE REQUIRED TO TRANSFER CONTROL FROM USEFUL OPERATIONS +; OF ONE FORTH PRIMITIVE TO THOSE OF THE NEXT. +; + .MACRO NEXT + MOV (IP)+,W + JMP @(W)+ + .ENDM +; +; +; MACRO CALLS +; +; + .IFDF RT11 + .MCALL .RCTRLO,.TTYIN,.TTINR,.TTYOUT,.EXIT,.TRPSET + .MCALL .SETTOP,.DSTATUS,.FETCH,.LOOKUP,.READW,.WRITW + .ENDC +; +; + .IFDF RSX11 + .MCALL QIOW$C,EXIT$S,ALUN$C,ASTX$S,SVTK$S + .MCALL FDBDF$,FDRC$A,FDBK$A,FDOP$A,FSRSZ$ + .MCALL OPEN$M,READ$,WRITE$,WAIT$,CLOSE$ + .MCALL QIOW$ + .ENDC +.PAGE +; **************************************************************** +; +; START-UP TABLE +; +; **************************************************************** +; +; AT STARTUP, MOST OF THESE VALUES ARE MOVED INTO THE USER AREA +; (STARTING AT 'XDP:'); THEY ARE NORMALLY ACCESSED THERE. THE VALUES +; HERE ARE NOT USUALLY CHANGED, BUT THEY MAY BE CHANGED E.G. TO +; CONTROL WHAT HAPPENS AT COLD START. THIS TABLE COULD BE MOVED OUT OF +; LOW MEMORY IF NECESSARY FOR ROM SYSTEMS. +; +; +; +GFORTH:: ; GLOBAL LABEL - NORMALLY NOT USED +ORIGIN: JMP CENT ; COLD START ENTRY POINT + JMP WENT ; WARM START ENTRY ADDRESS +; NOTE - COLD START WIPES OUT ANY NEW DICTIONARY DEFINITIONS, AND +; THEN DOES A WARM START. WARM START CLEANS UP STACKS, TERMINAL +; BUFFER, ETC. + .WORD 11. ; CPU + .WORD 0 ; REVISION + .WORD TASK-10 ; POINTER TO LATEST WORD DEFINED + .WORD 10 ; BACKSPACE CHARACTER + .WORD XUP ; POINTER TO USER AREA +; NOTE - THE USER AREA IS A HOOK IN THIS SYSTEM TO ALLOW MULTITASKING +; TO BE ADDED LATER. + .WORD XS0 ; POINTER TO BEGINNING OF THE STACK + .WORD XR0 ; POINTER TO BEGINNING OF RETURN STACK + .WORD XTIB ; POINTER TO TERMINAL INPUT BUFFER + .WORD 37 ; MAXIMUM NAME-FIELD WIDTH, NORMALLY 31 + .WORD 0 ; WARNING MODE; 0=ERROR #, 1=DISK MESSAGE +; NOTE - WARNING MODE INITIALIZED TO ZERO, IN CASE DISK ISN'T UP. + .WORD XDP ; FENCE TO PROTECT AGAINST ACCIDENTAL + ; 'FORGET' OF THE SYSTEM. + .WORD XDP ; POINTER TO NEXT AVAILABLE DICTIONARY + ; LOCATION (RETURNED BY 'HERE'). + .WORD XXVOC ; POINTER TO INITIAL VOCABULARY LINK + .WORD DSKBUF ; INITIALIZE 'FIRST' + .WORD ENDBUF ; INITIALIZE 'LIMIT' + .WORD 0 ; AVAILABLE + .WORD 0 ; AVAILABLE +; +.PAGE +; **************************************************************** +; +; NUCLEUS +; +; **************************************************************** +; +; +; +; THE NUCLEUS CONTAINS THE PRIMITIVES FROM WHICH THE SYSTEM IS BUILT. +; +; +; +; + HEAD 203,LIT,324,LIT ; ***** LIT +; USED ONLY BY COMPILER. PUSH FOLLOWING LITERAL ONTO STACK. + MOV (IP)+,-(S) + NEXT +; + HEAD 207,EXECUTE,305,EXEC ; ***** EXECUTE +; EXECUTE FORTH WORD WHOSE CODE ADDRESS IS ON STACK + MOV (S)+,W + JMP @(W)+ +; +; + HEAD 206,BRANCH,240,BRAN ; ***** BRANCH +; USED ONLY BY COMPILER. FORTH BRANCH TO ADDRESS WHICH FOLLOWS. + ADD (IP),IP + NEXT +; + HEAD 207,0BRANCH,310,ZBRAN ; ***** 0BRANCH +; USED ONLY BY COMPILER. FORTH BRANCH IF TOP OF STACK +; IS ZERO (FALSE). + TST (S)+ + BNE 3$ + ADD (IP),IP + NEXT +3$: ADD #2,IP + NEXT +; + HEAD 206,(LOOP),240,XLOOP ; ***** (LOOP) +; USED ONLY BY COMPILER. INCREMENT LOOP INDEX BY 1, BRANCH +; IF BELOW LIMIT. + INC (RP) + CMP (RP),2(RP) + BGE 1$ + ADD (IP),IP + NEXT +1$: ADD #4,RP + ADD #2,IP + NEXT +; + HEAD 207,(+LOOP),251,XPLOO ; ***** (+LOOP) +; USED ONLY BY COMPILER. INCREMENT LOOP INDEX BY TOP OF STACK, +; MAYBE BRANCH. + ADD (S),(RP) + TST (S)+ + BLT 2$ + CMP 2(RP),(RP) + BLE 1$ + ADD (IP),IP + NEXT +1$: ADD #4,RP + ADD #2,IP + NEXT +2$: CMP (RP),2(RP) ; HANDLE NEGATIVE INCREMENT + BLE 1$ + ADD (IP),IP + NEXT +; + HEAD 204,(DO),240,XDO ; ***** (DO) +; USED ONLY BY COMPILER. SET UP 'DO' LIMIT AND INDEX. + MOV 2(S),-(RP) + MOV (S),-(RP) + ADD #4,S + NEXT +; + HEAD 201,I,311,I ; ***** I +; RETURN CURRENT LOOP INDEX TO STACK. + MOV (RP),-(S) + NEXT +; + HEAD 205,DIGIT,324,DIGIT ; ***** DIGIT +; USED BY COMPILER. +; ( ASCII-DIGIT BASE ==> DIGIT-VALUE TRUE (OR FALSE)) + SUB #60,2(S) ; VALID DIGIT IS ASCII 60 - + CMP 2(S),#11 ; IF GREATER THAN 9, + BLE 1$ + SUB #7,2(S) ; SUBTRACT 7. + CMP 2(S),#12 ; AND THEN IF <10 (A) + BLT 2$ ; ERROR +1$: TST 2(S) ; IF LESS THAN ZERO, ERROR + BLT 2$ + CMP 2(S),(S) ; OR IF NOT LESS THAN BASE, ERR + BGE 2$ + MOV #1,(S) ; VALID RETURN + NEXT +2$: ADD #2,S + CLR (S) ; ERROR - RETURN '0' FLAG + NEXT +; +; + HEAD 206,(FIND),240,PFIND ; ***** (FIND) +; USED BY COMPILER. FIND A WORD IN THE DICTIONARY. +; ( STRING-ADDRESS NFA ==> PFA LENGTH TRUE (OR FALSE)). +; STRING-ADDRESS IS ADDRESS OF THE LENGTH BYTE OF THE +; STRING BEING SOUGHT. NFA IS NAME-FIELD ADDRESS OF +; WORD IN DICTIONARY WHERE SEARCH BEGINS. PFA IS +; PARAMETER-FIELD ADDRESS OF THE DICTIONARY ENTRY +; WHICH IS FOUND. IF WORD NOT FOUND, ONLY ONE RESULT +; (0, FALSE) IS RETURNED. +; SETUP - GET ARGS, PRESERVE NEEDED REGISTERS + MOV (S)+,R0 ; DICTIONARY ADDRESS + MOV (S)+,R1 ; STRING ADDRESS + MOV R5,-(RP) ; PRESERVE REGISTERS + MOV R4,-(RP) + MOV R3,-(RP) + CLR -(RP) ; SPACE TO STORE LENGTH BYTE +; PREPARE R2 FOR FAST COMPARE + MOV (R1),R2 + BIC #100200,R2 +; +FCOMP: +; FAST TEST TO ELIMINATE MOST WORDS +; COMPARE FIRST WORD TO SPECIALLY PREPARED R2 +; THEN INCREMENT TO FIND END OF NAME. +FAST: MOV (R0),R3 + BIC #100300,R3 + CMP R2,R3 + BEQ NOFAST ; NO FAST ELIMINATION POSSIBLE +XMATCH: TST (R0)+ ; BRANCH HERE IF NO MATCH THIS TIME + BPL XMATCH +; R0 NOW POINTS TO LINK + TST (R0) + BEQ FAILED + MOV (R0),R0 + BR FCOMP +; END OF FAST ELIMINATION TEST +; +NOFAST: MOV (R0),(RP) ; SAVE LENGTH BYTE + MOV R1,R5 ; SET R5 + BR NOFST1 +; NOW DO THE MAIN LOOP TO CHECK FOR MATCH +MLOOP: TST (R5)+ + MOV (R5),R4 + MOV (R0),R3 + BIC #100000,R3 + CMP R3,R4 + BNE XMATCH +NOFST1: BIT #100000,(R0)+ + BEQ MLOOP +; IF GET HERE, FOUND IT. + MOV (RP)+,R2 ; POP AND SAVE LENGTH BYTE + MOV (RP)+,R3 ; RESTORE REGISTERS + MOV (RP)+,R4 + MOV (RP)+,R5 + ADD #4,R0 ; GET PARAMETER FIELD ADDRESS + MOV R0,-(S) + BIC #177400,R2 ; R2 CONTAINS LENGTH BYTE + MOV R2,-(S) + MOV #1,-(S) + NEXT +FAILED: TST (RP)+ ; POP LENGTH BYTE + MOV (RP)+,R3 ; RESTORE REGISTERS + MOV (RP)+,R4 + MOV (RP)+,R5 + CLR -(S) ; REPLACE LENGTH BYTE WITH + ; FAILURE FLAG. + NEXT ; WE ARE DONE - FAILURE TO FIND +; + HEAD 207,ENCLOSE,305,ENCL ; ***** ENCLOSE +; USED BY COMPILER. BREAK NEXT WORD OUT OF INPUT BUFFER. +; ( START-ADDRESS DELIMITER ==> ADDRESS OFFSET END NEXT-CHARACTER) + MOV (S),R0 ; DELIMITER + MOV 2(S),R1 ; STARTING ADDRESS + SUB #4,S ; MAKE SPACE FOR RESULTS +ENC1: CMPB (R1)+,R0 + BEQ ENC1 ; SKIP OVER LEADING DELIMITERS + SUB #1,R1 + MOV R1,4(S) +ENC2: TSTB (R1) ; TEST FOR NULL + BEQ ENC4 + CMPB (R1)+,R0 ; NOT NULL, SO FIND END OF TOKEN + BNE ENC2 + MOV R1,(S) + SUB #1,R1 +ENC3: MOV R1,2(S) ; FINISH UP AND RETURN + MOV 6(S),R1 + SUB R1,(S) + SUB R1,2(S) + SUB R1,4(S) + NEXT +ENC4: MOV R1,(S) ; HANDLE NULL CASE + CMP R1,4(S) + BNE ENC3 + ADD #1,R1 + BR ENC3 + +; +; +; THE NEXT 4 HEADERS POINT TO INSTALLATION-DEPENDENT TERMINAL I/O +; ROUTINES. +; +; + HEAD 204,EMIT,240,EMIT,PEMIT ; ***** EMIT +; + HEAD 203,KEY,331,KEY,PKEY ; ***** KEY +; + HEAD 211,?TERMINAL,314,QTERM,PQTER ; ***** ?TERMINAL +; + HEAD 202,CR,240,CR,PCR ; ***** CR +; +; +; +; +; + HEAD 205,CMOVE,305,CMOVE ; ***** CMOVE +; MOVE BYTES IN MEMORY. ( FROM TO N ==>) + TST (S) + BEQ 2$ ; NO MOVE + MOV 2(S),R0 + MOV 4(S),R1 +1$: MOVB (R1)+,(R0)+ + DEC (S) + BNE 1$ +2$: ADD #6,S + NEXT +; +; + HEAD 202,U*,240,USTAR ; ***** U* +; ( N1 N2 ==> PRODUCT). PRODUCT IS 32-BIT DOUBLE INTEGER, +; HIGH WORD TOP. +; THIS MUST BE UNSIGNED MULTIPLICATION. + JSR PC,UMULT + NEXT +UMULT: +; THE VALUES TO MULTIPLY ARE ON THE STACK. + MOV (S)+,R2 + MOV #20,-(RP) ; SET LOOP COUNT + CLR R0 + CLR R1 +2$: ROL R1 + ROL R0 + ROL R2 + BCC 1$ + ADD (S),R1 + ADC R0 +1$: DEC (RP) + BNE 2$ + MOV R1,(S) + MOV R0,-(S) + TST (RP)+ ; POP TEMPORARY + RTS PC +; + HEAD 202,U/,240,USLAS ; ***** U/ +; THIS DIVISION MUST BE UNSIGNED. + JSR PC,UDIV + NEXT +UDIV: +; THE VALUES TO DIVIDE ARE ON THE STACK + MOV (S)+,R2 ; DIVISOR + MOV (S)+,R0 + MOV (S)+,R1 + MOV #20,-(S) ; LOOP COUNT +1$: ASL R1 + ROL R0 + BEQ 2$ ; NO NEED TO SUBTRACT + SUB R2,R0 + INC R1 + BCC 2$ + ADD R2,R0 ; MUST RESTORE + DEC R1 +2$: DEC (S) ; LOOP SIXTEEN TIMES + BNE 1$ + TST (S)+ ; POP TO DISCARD COUNT + MOV R0,-(S) ; REMAINDER + MOV R1,-(S) ; QUOTIENT + RTS PC +; + HEAD 203,AND,304,AND ; ***** AND +; BITWISE AND. ( N1 N2 ==> N3). + COM (S) + BIC (S)+,(S) + NEXT +; + HEAD 202,OR,240,OR ; ***** OR + BIS (S)+,(S) + NEXT +; + HEAD 203,XOR,322,XOR ; ***** XOR + .IFDF EIS + MOV (S)+,R0 + XOR R0,(S) + .IFF + MOV (S),-(RP) + BIC 2(S),(RP) + BIC (S)+,(S) + BIS (RP)+,(S) + .ENDC + NEXT +; + HEAD 203,SP@,300,SPAT ; ***** SP@ + MOV S,R1 + MOV R1,-(S) + NEXT +; + HEAD 203,SP!,241,SPSTO ; ***** SP! + MOV 6(U),S ; OFFSET 6 IN USER AREA + + NEXT +; + HEAD 203,RP!,241,RPSTO ; ***** RP! + MOV ORIGIN+24,RP + NEXT +; + HEAD 202,<;S>,240,SEMIS ; ***** ;S + MOV (RP)+,IP + NEXT +; + HEAD 205,LEAVE,305,LEAVE ; ***** LEAVE + MOV (RP),2(RP) + NEXT +; + HEAD 202,^/>R/,240,TOR ; ***** >R + MOV (S)+,-(RP) + NEXT +; + HEAD 202,R>,240,FROMR ; ***** R> + MOV (RP)+,-(S) + NEXT +; + HEAD 201,R,322,R ; ***** R + MOV (RP),-(S) + NEXT +; + HEAD 202,0=,240,ZEQU ; ***** 0= + TST (S) + BEQ 1$ + CLR (S) + BR 2$ +1$: MOV #1,(S) +2$: + NEXT +; + HEAD 202,0<,240,ZLESS ; ***** 0< + TST (S) + BMI 1$ + CLR (S) + BR 2$ +1$: MOV #1,(S) +2$: + NEXT +; + HEAD 201,+,253,PLUS ; ***** + + ADD (S)+,(S) + NEXT +; + HEAD 202,D+,240,DPLUS ; ***** D+ + ADD 2(S),6(S) ; ADD LOW + ADC 4(S) + ADD (S),4(S) ; ADD HIGH + ADD #4,S + NEXT +; + HEAD 205,MINUS,323,MINUS ; ***** MINUS +; CHANGE SIGN. + NEG (S) + NEXT +; + HEAD 206,DMINUS,240,DMINU ; ***** DMINUS +; CHANGE SIGN OF DOUBLE INTEGER WORD ON STACK. + NEG (S) + NEG 2(S) + SBC (S) + NEXT +; + HEAD 204,OVER,240,OVER ; ***** OVER +; ( N1 N2 ==> N1 N2 N1) + MOV 2(S),-(S) + NEXT +; + HEAD 204,DROP,240,DROP ; ***** DROP + ADD #2,S + NEXT +; + HEAD 204,SWAP,240,SWAP ; ***** SWAP + MOV 2(S),R1 + MOV (S),2(S) + MOV R1,(S) + NEXT +; + HEAD 203,DUP,320,DUP ; ***** DUP + MOV (S),-(S) + NEXT +; + HEAD 202,+!,240,PSTOR ; ***** +! +; ADD NUMBER SECOND ON STACK TO ADDRESS ON TOP. + ADD 2(S),@(S) + ADD #4,S + NEXT +; + HEAD 206,TOGGLE,240,TOGGL ; ***** TOGGLE +; ( BYTE-ADDRESS BIT-PATTERN ==> ) EXCLUSIVE-OR INTO MEMORY BYTE. + MOV 2(S),-(S) ; PUSH THE BYTE + MOVB @(S),(S) ; TO BE TOGGLED +; AVOID USING 'XOR' INSTRUCTION - NOT AVAILABLE ON ALL PDP-11 + MOV (S),-(RP) + BIC 2(S),(RP) + BIC (S)+,(S) + BIS (RP)+,(S) + MOV 2(S),-(S) ; SET UP RETURN ADDRESS + MOVB 2(S),@(S) ; PUT THE TOGGLED BYTE BACK TO MEM. + ADD #6,S ; ADJUST STACK POINTER + NEXT +; + HEAD 201,@,300,AT ; ***** @ + MOV @(S),(S) + NEXT +; + HEAD 202,C@,240,CAT ; ***** C@ + MOVB @(S),R1 + BIC #177400,R1 + MOV R1,(S) + NEXT +; + HEAD 201,!,241,STORE ; ***** ! + MOV 2(S),@(S) + ADD #4,S + NEXT +; + HEAD 202,C!,240,CSTOR ; ***** C! + MOVB 2(S),@(S) + ADD #4,S + NEXT +; +; +.PAGE +; **************************************************************** +; +; PRE-COMPILED FORTH SECTION +; +; **************************************************************** +; +; +; +; NOTE - A FEW OF THE FOLLOWING OPERATIONS HAVE BEEN +; CONVERTED TO CODE FOR SPEED. HOWEVER, THE WORD ORDER +; IN THE DICTIONARY HAS NOT BEEN CHANGED. +; + HEAD 301,:,272,COLON,DOCOL ; ***** : + .WORD QEXEC,SCSP,CURR,AT,CONT,STORE,CREAT,RBRAC,PSCOD +DOCOL: MOV IP,-(RP) + MOV W,IP + NEXT +; + HEAD 301,<;>,273,SEMI,DOCOL ; ***** ; + .WORD QCSP,COMP,SEMIS,SMUDG,LBRAC,SEMIS +; + HEAD 210,CONSTANT,240,CON,DOCOL ; ***** CONSTANT + .WORD CREAT,SMUDG,COMMA,PSCOD +DOCON: MOV (W),-(S) + NEXT +; + HEAD 210,VARIABLE,240,VAR,DOCOL ; ***** VARIABLE + .WORD CON,PSCOD +DOVAR: MOV W,-(S) + NEXT +; + HEAD 204,USER,240,USER,DOCOL ; ***** USER +; CREATE A NEW USER VARIABLE. ( N ==> ). + .WORD CON,PSCOD +DOUSE: MOV (W),-(S) + ADD U,(S) + NEXT +; +; +; +; CONSTANTS +; + HEAD 201,0,260,ZERO,DOCON ; ***** 0 + .WORD 0 +; + HEAD 201,1,261,ONE,DOCON ; ***** 1 + .WORD 1 +; + HEAD 201,2,262,TWO,DOCON ; ***** 2 + .WORD 2 +; + HEAD 201,3,263,THREE,DOCON ; ***** 3 + .WORD 3 +; + HEAD 202,BL,240,BL,DOCON ; ***** BL +; BLANK. + .WORD 40 +; + HEAD 203,C/L,314,CL,DOCON ; ***** C/L +; # OF CHARACTERS PER LINE + .WORD 100 +; +; 'FIRST' AND 'LIMIT' MOVED TO USER AREA +; + HEAD 205,B/BUF,306,BBUF,DOCON ; ***** B/BUF +; BYTES PER DISK-BLOCK BUFFER. + .WORD 1024. +; + HEAD 205,B/SCR,322,BSCR,DOCON ; ***** B/SCR +; DISK BLOCKS PER FORTH SCREEN. + .WORD 1 +; + HEAD 207,+ORIGIN,316,PORIG,DOCOL ; ***** +ORIGIN +; RETURNS ADDRESS, GIVEN OFFSET FROM ORIGIN. + .WORD LIT,ORIGIN,PLUS,SEMIS +; +; USER VARIABLES +; + HEAD 202,S0,240,SZERO,DOUSE ; ***** S0 +; STACK ORIGIN. + .WORD 6 +; + HEAD 202,R0,240,RZERO,DOUSE ; ***** R0 +; RETURN STACK ORIGIN. + .WORD 10 +; + HEAD 203,TIB,302,TIB,DOUSE ; ***** TIB +; TERMINAL INPUT BUFFER. + .WORD 12 +; + HEAD 205,WIDTH,310,WIDTH,DOUSE ; ***** WIDTH +; MAXIMUM NAME LENGTH (DEFAULT, 31 CHARACTERS). + .WORD 14 +; + HEAD 207,WARNING,307,WARN,DOUSE ; ***** WARNING +; WARNING MODE (DEFAULT, GIVE MESSAGE NUMBER AT ERROR +; OR WARNING CONDITION, DON'T GO TO DISK FOR MESSAGE). + .WORD 16 +; + HEAD 205,FENCE,305,FENCE,DOUSE ; ***** FENCE +; PREVENTS 'FORGET' BELOW THIS 'FENCE' SETTING. + .WORD 20 +; + HEAD 202,DP,240,DP,DOUSE ; ***** DP +; DICTIONARY POINTER TO NEXT AVAILABLE SPACE. + .WORD 22 +; + HEAD 210,VOC-LINK,240,VOCL,DOUSE ; ***** VOC-LINK +; VOCABULARY LINK (MAINLY FOR FUTURE USE). + .WORD 24 +; + HEAD 205,FIRST,324,FIRST,DOUSE ; ***** FIRST +; ADDRESS OF BEGINNING OF DISK BUFFER. + .WORD 26 +; + HEAD 205,LIMIT,324,LIMIT,DOUSE ; ***** LIMIT +; ADDRESS JUST BEYOND END OF DISK BUFFERS. + .WORD 30 +; +; POSITIONS 32 AND 34 ARE AVAILABLE FOR EXPANSION. +; THEY ARE INITIALIZED FROM BOOT-UP TABLE, AT COLD START. +; + HEAD 203,BLK,313,BLK,DOUSE ; ***** BLK +; CURRENT DISK BLOCK BEING LOADED (0=TERMINAL) + .WORD 36 +; + HEAD 202,IN,240,IN,DOUSE ; ***** IN +; OFFSET IN TERMINAL INPUT BUFFER. + .WORD 40 +; + HEAD 203,OUT,324,OUT,DOUSE ; ***** OUT +; OFFSET IN OUTPUT LINE. + .WORD 42 +; + HEAD 203,SCR,322,SCR,DOUSE ; ***** SCR +; CURRENT FORTH DISK SCREEN. + .WORD 44 +; + HEAD 206,OFFSET,240,OFSET,DOUSE ; ***** OFFSET +; OFFSET TO GET TO ANOTHER DISK DRIVE. + .WORD 46 +; + HEAD 207,CONTEXT,324,CONT,DOUSE ; ***** CONTEXT + .WORD 50 +; + HEAD 207,CURRENT,324,CURR,DOUSE ; ***** CURRENT + .WORD 52 +; + HEAD 205,STATE,305,STATE,DOUSE ; ***** STATE + .WORD 54 +; + HEAD 204,BASE,240,BASE,DOUSE ; ***** BASE + .WORD 56 +; + HEAD 203,DPL,314,DPL,DOUSE ; ***** DPL +; OFFSET OF DECIMAL POINT AFTER DOUBLE-INTEGER INPUT. + .WORD 60 +; + HEAD 203,FLD,304,FLD,DOUSE ; ***** FLD +; OUTPUT FIELD WIDTH. + .WORD 62 +; + HEAD 203,CSP,320,CSP,DOUSE ; ***** CSP +; USED BY COMPILER TO HOLD CURRENT STACK POSITION, +; FOR ERROR CHECKING. + .WORD 64 +; + HEAD 202,R#,240,RNUM,DOUSE ; ***** R# +; CURSOR POSITION (FOR SOME EDITORS). + .WORD 66 +; + HEAD 203,HLD,304,HLD,DOUSE ; ***** HLD +; POINTS TO LAST CHARACTER HELD IN 'PAD' + .WORD 70 +; + HEAD 203,USE,305,USE,DOUSE ; ***** USE + .WORD 72 +; + HEAD 204,PREV,240,PREV,DOUSE ; ***** PREV + .WORD 74 +; +; +; END OF USER AREA +; +; + HEAD 202,1+,240,ONEP ; ***** 1+ + INC (S) + NEXT +; + HEAD 202,2+,240,TWOP ; ***** 2+ + ADD #2,(S) + NEXT +; + HEAD 204,HERE,240,HERE,DOCOL ; ***** HERE + .WORD DP,AT,SEMIS +; + HEAD 205,ALLOT,324,ALLOT,DOCOL ; ***** ALLOT + .WORD DP,PSTOR,SEMIS +; + HEAD 201,<,>,254,COMMA,DOCOL ; ***** , + .WORD HERE,STORE,TWO,ALLOT,SEMIS +; +; THIS SYSTEM DOES NOT USE 'C,' +; + HEAD 201,-,255,SUB ; ***** - + SUB (S)+,(S) + NEXT +; + HEAD 201,=,275,EQUAL ; ***** = + CMP 2(S),(S)+ + BEQ 1$ + CLR (S) + BR 2$ +1$: MOV #1,(S) +2$: NEXT +; + HEAD 201,^//,276,GREAT ; ***** > + CMP 2(S),(S)+ + BGT 1$ + CLR (S) + BR 2$ +1$: MOV #1,(S) +2$: NEXT +; + HEAD 203,ROT,324,ROT ; ***** ROT + MOV (S),R0 + MOV 4(S),(S) + MOV 2(S),4(S) + MOV R0,2(S) + NEXT +; + HEAD 205,SPACE,305,SPACE,DOCOL ; ***** SPACE + .WORD LIT,40,EMIT,SEMIS +; + HEAD 204,-DUP,240,DDUP ; ***** -DUP + TST (S) + BEQ 1$ + MOV (S),-(S) +1$: NEXT +; + HEAD 210,TRAVERSE,240,TRAV,DOCOL ; ***** TRAVERSE +; MOVE (FORWARDS OR BACKWARDS) ACROSS A (VARIABLE LENGTH) +; DICTIONARY NAME FIELD. + .WORD SWAP +XXN1: .WORD OVER,PLUS,LIT,177,OVER,CAT,LESS,ZBRAN,XXN1-. + .WORD SWAP,DROP,SEMIS +; + HEAD 206,LATEST,240,LATES,DOCOL ; ***** LATEST + .WORD CURR,AT,AT,SEMIS +; +; THE NEXT 4 OPERATORS CAN DEPEND ON COMPUTER WORD SIZE. +; THEY CONVERT ADDRESSES WITHIN THE NAME FIELDS OF FORTH +; DICTIONARY ENTRIES. +; + HEAD 203,LFA,301,LFA,DOCOL ; ***** LFA + .WORD LIT,4,SUB,SEMIS +; + HEAD 203,CFA,301,CFA,DOCOL ; ***** CFA + .WORD TWO,SUB,SEMIS +; + HEAD 203,NFA,301,NFA,DOCOL ; ***** NFA + .WORD LIT,5,SUB,LIT,-1,TRAV,SEMIS +; + HEAD 203,PFA,301,PFA,DOCOL ; ***** PFA + .WORD ONE,TRAV,LIT,5,PLUS,SEMIS +; +; THE NEXT 7 OPERATIONS ARE USED BY THE COMPILER, FOR +; COMPILE-TIME SYNTAX-ERROR CHECKS. +; + HEAD 204,!CSP,240,SCSP,DOCOL ; ***** !CSP + .WORD SPAT,CSP,STORE,SEMIS +; + HEAD 206,?ERROR,240,QERR,DOCOL ; ***** ?ERROR + .WORD SWAP,ZBRAN,XXN2-.,ERROR,BRAN,XXN3-. +XXN2: .WORD DROP +XXN3: .WORD SEMIS +; + HEAD 205,?COMP,320,QCOMP,DOCOL ; ***** ?COMP + .WORD STATE,AT,ZEQU,LIT,21,QERR,SEMIS +; + HEAD 205,?EXEC,303,QEXEC,DOCOL ; ***** ?EXEC + .WORD STATE,AT,LIT,22,QERR,SEMIS +; + HEAD 206,?PAIRS,240,QPAIR,DOCOL ; ***** ?PAIRS + .WORD SUB,LIT,23,QERR,SEMIS +; + HEAD 204,?CSP,240,QCSP,DOCOL ; ***** ?CSP + .WORD SPAT,CSP,AT,SUB,LIT,24,QERR,SEMIS +; + HEAD 210,?LOADING,240,QLOAD,DOCOL ; ***** ?LOADING + .WORD BLK,AT,ZEQU,LIT,26,QERR,SEMIS +; + HEAD 207,COMPILE,305,COMP,DOCOL ; ***** COMPILE +; COMPILE THE EXECUTION ADDRESS FOLLOWING. + .WORD QCOMP,FROMR,DUP,TWOP,TOR,AT,COMMA,SEMIS +; + HEAD 301,[,333,LBRAC,DOCOL ; ***** [ +; STOP COMPILATION, ENTER EXECUTION STATE. + .WORD ZERO,STATE,STORE,SEMIS +; + HEAD 201,],335,RBRAC,DOCOL ; ***** ] +; ENTER COMPILATION STATE. + .WORD LIT,300,STATE,STORE,SEMIS +; + HEAD 206,SMUDGE,240,SMUDG,DOCOL ; ***** SMUDGE +; ALTER LATEST WORD NAME (SO THAT DICTIONARY SEARCH +; WON'T FIND A PARTIALLY-COMPLETE ENTRY. + .WORD LATES,LIT,40,TOGGL,SEMIS +; + HEAD 203,HEX,330,HEX,DOCOL ; ***** HEX + .WORD LIT,20,BASE,STORE,SEMIS +; + HEAD 207,DECIMAL,314,DEC,DOCOL ; ***** DECIMAL + .WORD LIT,12,BASE,STORE,SEMIS +; + HEAD 205,OCTAL,314,OCTAL,DOCOL ; ***** OCTAL + .WORD LIT,10,BASE,STORE,SEMIS +; + HEAD 207,<(;CODE)>,251,PSCOD,DOCOL ; ***** (;CODE) +; USED ONLY BY COMPILER; COMPILED BY ';CODE'. + .WORD FROMR,LATES,PFA,CFA,STORE,SEMIS +; +; +; ***** THE DEFINITION OF ';CODE' WAS MOVED TO THE END OF +; THE DICTIONARY, BECAUSE IT IS NOT PURE CODE (IT IS PATCHED +; WHEN A FORTH ASSEMBLER IS LOADED). + +; + HEAD 207,^/,276,DOES,DOCOL ; ***** DOES> + .WORD FROMR,LATES,PFA,STORE,PSCOD +DODOE: MOV IP,-(RP) + MOV (W)+,IP + MOV W,-(S) + NEXT +; + HEAD 205,COUNT,324,COUNT,DOCOL ; ***** COUNT +; CONVERT STRING TO THE FORMAT USED BY 'TYPE'. + .WORD DUP,ONEP,SWAP,CAT,SEMIS +; + HEAD 204,TYPE,240,TYPE,DOCOL ; ***** TYPE + .WORD DDUP,ZBRAN,XXL2-.,OVER,PLUS,SWAP,XDO +XXL1: .WORD I,CAT,EMIT,XLOOP,XXL1-.,BRAN,XXL3-. +XXL2: .WORD DROP +XXL3: .WORD SEMIS +; + HEAD 206,=CELLS,240,ECELL,DOCOL ; ***** =CELLS +; NOTE - I NEED THIS, TO FORCE EVEN ADDRESS. + .WORD DUP,ONE,AND,PLUS,SEMIS +; + HEAD 211,-TRAILING,307,DTRAI,DOCOL ; ***** -TRAILING + .WORD DUP,ZERO,XDO +XXW6: .WORD OVER,OVER,PLUS,ONE,SUB,CAT + .WORD BL,SUB,ZBRAN,XXW7-.,LEAVE,BRAN,XXWA-. +XXW7: .WORD ONE,SUB +XXWA: .WORD XLOOP,XXW6-.,SEMIS +; + HEAD 204,(."),240,PDOTQ,DOCOL ; ***** (.") +; USED ONLY BY COMPILER. COMPILED BY '."' + .WORD R,COUNT,DUP,ONEP,ECELL + .WORD FROMR,PLUS,TOR,TYPE,SEMIS +; + HEAD 302,.",240,DOTQ,DOCOL ; ***** ." +; TYPE ASCII MESSAGE. + .WORD LIT,34.,STATE,AT,ZBRAN,XXL6-. + .WORD COMP,PDOTQ,WORD,HERE,CAT,ONEP,ECELL + .WORD ALLOT,BRAN,XXL7-. +XXL6: .WORD WORD,HERE,COUNT,TYPE +XXL7: .WORD SEMIS +; + HEAD 206,?ALIGN,240,QALIG,DOCOL ; ***** ?ALIGN + .WORD HERE,ONE,AND,ALLOT,SEMIS +; + HEAD 206,EXPECT,240,EXPEC,DOCOL ; ***** EXPECT +; READ N CHARACTERS TO MEMORY (AND TERMINATE WITH NULLS). +; ( ADDRESS N ==>). + .WORD OVER,PLUS,OVER,XDO +XXK1: .WORD KEY,DUP,LIT,16,PORIG,AT,EQUAL,ZBRAN,XXK2-. + .WORD DROP,LIT,10,OVER,I,EQUAL,DUP,FROMR + .WORD TWO,SUB,PLUS,TOR,SUB,BRAN,XXK3-. +XXK2: .WORD DUP,LIT,15,EQUAL,ZBRAN,XXK4-. + .WORD LEAVE,DROP,BL,ZERO,BRAN,XXK5-. +XXK4: .WORD DUP +XXK5: .WORD I,CSTOR,ZERO,I,ONEP,CSTOR,ZERO,I,TWOP,CSTOR +; NOTE DIFFERENCE FOR STAND-ALONE, BELOW + .IFDF ALONE +XXK3: .WORD EMIT,XLOOP,XXK1-.,DROP,SEMIS + .IFF +XXK3: .WORD DROP,XLOOP,XXK1-.,DROP,SEMIS + .ENDC +; + HEAD 205,QUERY,331,QUERY,DOCOL ; ***** QUERY + .WORD TIB,AT,LIT,120,EXPEC,ZERO,IN,STORE,SEMIS +; + HEAD 301,X,200,NULL,DOCOL ; ***** THE NULL +; THE NULL OPERATION (ASCII 0) STOPS INTERPRETATION/COMPILATION +; AT END OF A TERMINAL INPUT LINE, OR A DISK SCREEN. ALL DISK +; BUFFERS MUST TERMINATE WITH NULLS, AND 'EXPECT' PLACES NULLS +; AFTER EACH TERMINAL INPUT LINE. +; NOTE THAT THE 'X' IN THE HEADER ABOVE WILL BE CHANGED TO A NULL. + .WORD BLK,AT + .WORD ZBRAN,XXJ2-.,ONE,BLK,PSTOR,ZERO,IN,STORE + .WORD BLK,AT,BSCR,MOD,ZEQU,ZBRAN,XXJ1-.,QEXEC,FROMR,DROP +XXJ1: .WORD BRAN,XXJ4-. +XXJ2: .WORD FROMR,DROP +XXJ4: .WORD SEMIS +; + HEAD 204,FILL,240,FILL,DOCOL ; ***** FILL + .WORD SWAP,TOR,OVER,CSTOR,DUP,ONEP,FROMR + .WORD ONE,SUB,CMOVE,SEMIS +; + HEAD 205,ERASE,305,ERASE,DOCOL ; ***** ERASE + .WORD ZERO,FILL,SEMIS +; + HEAD 206,BLANKS,240,BLANK,DOCOL ; ***** BLANKS + .WORD BL,FILL,SEMIS +; + HEAD 204,HOLD,240,HOLD,DOCOL ; ***** HOLD + .WORD LIT,-1,HLD,PSTOR,HLD,AT,CSTOR,SEMIS +; + HEAD 203,PAD,304,PAD,DOCOL ; ***** PAD + .WORD HERE,LIT,104,PLUS,SEMIS +; + HEAD 204,WORD,240,WORD,DOCOL ; ***** WORD + .WORD BLK,AT,ZBRAN,XXI1-.,BLK,AT,BLOCK,BRAN,XXI2-. +XXI1: .WORD TIB,AT +XXI2: .WORD IN,AT,PLUS,SWAP,ENCL,HERE,LIT,42,BLANK,IN + .WORD PSTOR,OVER,SUB,TOR,R,HERE,CSTOR,PLUS + .WORD HERE,ONEP,FROMR,CMOVE,SEMIS +; +; + HEAD 210,(NUMBER),240,PNUMB,DOCOL ; ***** (NUMBER) +XXF3: .WORD ONEP,DUP,TOR,CAT,BASE,AT,DIGIT + .WORD ZBRAN,XXG4-.,SWAP,BASE,AT,USTAR,DROP + .WORD ROT,BASE,AT,USTAR,DPLUS + .WORD DPL,AT,ONEP,ZBRAN,XXG5-.,ONE,DPL,PSTOR +XXG5: .WORD FROMR,BRAN,XXF3-. +XXG4: .WORD FROMR,SEMIS +; + HEAD 206,NUMBER,240,NUMB,DOCOL ; ***** NUMBER + .WORD ZERO,ZERO,ROT,DUP,ONEP,CAT,LIT,55,EQUAL + .WORD DUP,TOR,PLUS,LIT,-1 +XXF6: .WORD DPL,STORE,PNUMB,DUP,CAT,BL,SUB + .WORD ZBRAN,XXF7-.,DUP,CAT,LIT,56,SUB + .WORD ZERO,QERR,ZERO,BRAN,XXF6-. +XXF7: .WORD DROP,FROMR,ZBRAN,XXFA-.,DMINU +XXFA: .WORD SEMIS +; + HEAD 205,-FIND,304,DFIND,DOCOL ; ***** -FIND + .WORD BL,WORD,HERE,COUNT,UPPER,HERE,CONT,AT,AT,PFIND + .WORD DUP,ZEQU,ZBRAN,XXE3-.,DROP,HERE,LATES,PFIND +XXE3: .WORD SEMIS +; + HEAD 205,UPPER,322,UPPER,DOCOL ; ***** UPPER +; SETS STRINGS TO UPPER CASE - TO ALLOW +; LOWER AS WELL AS UPPER CASE FROM TERMINAL. + .WORD OVER,PLUS,SWAP,XDO +XXE2: .WORD I,CAT,LIT,140,GREAT,I,CAT,LIT,173,LESS + .WORD AND,ZBRAN,XXE1-.,I,LIT,40,TOGGL +XXE1: .WORD XLOOP,XXE2-.,SEMIS +; + HEAD 207,(ABORT),251,PABOR,DOCOL ; ***** (ABORT) + .WORD ABORT,SEMIS +; + HEAD 205,ERROR,322,ERROR,DOCOL ; ***** ERROR + .WORD WARN,AT,ZLESS,ZBRAN,XXN4-.,PABOR +XXN4: .WORD HERE,COUNT,TYPE,PDOTQ + .BYTE 3 + .ASCII / ? / + .EVEN + .WORD MESS,SPSTO,IN,AT,BLK,AT,QUIT,SEMIS +; + HEAD 203,ID.,256,IDDOT,DOCOL ; ***** ID. + .WORD PAD,LIT,40,LIT,137,FILL,DUP + .WORD PFA,LFA,OVER,SUB,PAD,SWAP,CMOVE + .WORD PAD,COUNT,LIT,37,AND,TYPE,SPACE,SEMIS +; + HEAD 206,CREATE,240,CREAT,DOCOL ; ***** CREATE + .WORD DFIND,ZBRAN,XXD2-.,DROP,NFA,IDDOT + .WORD LIT,4,MESS,SPACE +XXD2: .WORD HERE,DUP,CAT,WIDTH,AT,MIN,ONEP,ALLOT + .WORD QALIG,DUP,LIT,240,TOGGL,HERE,ONE,SUB + .WORD LIT,200,TOGGL,LATES,COMMA,CURR,AT,STORE + .WORD HERE,TWOP,COMMA,SEMIS +; + HEAD 311,[COMPILE],335,BCOMP,DOCOL ; ***** [COMPILE] + .WORD DFIND,ZEQU,ZERO,QERR,DROP,CFA,COMMA,SEMIS +; + HEAD 307,LITERAL,314,LITER,DOCOL ; ***** LITERAL + .WORD STATE,AT,ZBRAN,XXD6-.,COMP,LIT,COMMA +XXD6: .WORD SEMIS +; + HEAD 310,DLITERAL,240,DLITE,DOCOL ; ***** DLITERAL + .WORD STATE,AT,ZBRAN,XXN5-.,SWAP,LITER,LITER +XXN5: .WORD SEMIS +; + HEAD 202,U<,240,ULESS,DOCOL ; ***** U< +; UNSIGNED LESS-THAN, NEEDED FOR '?STACK' +; : U< >R 0 R> 0 DMINUS D+ SWAP DROP 0< ; + .WORD TOR,ZERO,FROMR,ZERO,DMINU,DPLUS + .WORD SWAP,DROP,ZLESS,SEMIS +; + HEAD 206,?STACK,240,QSTAC,DOCOL ; ***** ?STACK +; ERROR CHECK. + .WORD SZERO,AT,TWO,SUB,SPAT,ULESS,ONE,QERR + .WORD SPAT,HERE,LIT,200,PLUS,ULESS,TWO,QERR + .WORD SEMIS +; + HEAD 211,INTERPRET,324,INTER,DOCOL ; ***** INTERPRET +XXE4: .WORD DFIND + .WORD ZBRAN,XXEA-.,STATE,AT,LESS + .WORD ZBRAN,XXE5-.,CFA,COMMA,BRAN,XXE6-. +XXE5: .WORD CFA,EXEC +XXE6: .WORD QSTAC,BRAN,XXE7-. +XXEA: .WORD HERE,NUMB,DPL,AT,ONEP,ZBRAN,XXF4-.,DLITE,BRAN,XXF5-. +XXF4: .WORD DROP,LITER +XXF5: .WORD QSTAC +XXE7: .WORD BRAN,XXE4-. +; + HEAD 211,IMMEDIATE,305,IMMED,DOCOL ; ***** IMMEDIATE + .WORD LATES,LIT,100,TOGGL,SEMIS +; + HEAD 212,VOCABULARY,240,VOCAB,DOCOL ; ***** VOCABULARY + .WORD BUILD,LIT,120201,COMMA,CURR,AT,CFA,COMMA + .WORD HERE,VOCL,AT,COMMA,VOCL,STORE,DOES +DOVOC: .WORD TWOP,CONT,STORE,SEMIS +; +; +; ***** THE DEFINITION OF 'FORTH' WAS MOVED TO NEAR THE END OF THE +; DICTIONARY, BECAUSE IT IS NOT PURE CODE. +; +; + HEAD 213,DEFINITIONS,323,DEFIN,DOCOL ; ***** DEFINITIONS + .WORD CONT,AT,CURR,STORE,SEMIS +; + HEAD 301,(,250,PAREN,DOCOL ; ***** ( + .WORD LIT,51,WORD,SEMIS +; + HEAD 204,QUIT,240,QUIT,DOCOL ; ***** QUIT + .WORD ZERO,BLK,STORE,LBRAC +XXB1: .WORD RPSTO,CR,QUERY,INTER,STATE,AT + .WORD ZEQU,ZBRAN,XXB2-.,PDOTQ + .BYTE 3 + .ASCII / OK/ + .EVEN +XXB2: .WORD BRAN,XXB1-. +; + HEAD 205,ABORT,324,ABORT,DOCOL ; ***** ABORT + .WORD SPSTO,DEC,SPACE + .WORD CR,PDOTQ + .BYTE 23 + .ASCII /FIG-FORTH V 1.3.1 / + .EVEN + .WORD FORTH,DEFIN,QUIT +; +; COLD AND WARM STARTS +; + HEAD 204,COLD,240,COLD ; ***** COLD +CENT: ; COLD START ENTRY POINT + MOV ORIGIN+14,FORTH+6 ; SET 'FORTH' VOCABULARY FROM STARTUP TABLE + MOV ORIGIN+20,U ; INITIALIZE USER POINTER +; NOTE - FOR SMALLER STAND-ALONE BOOT, INITIALIZE AREAS IN +; HIGH MEMORY WHICH MUST BE INITIALIZED. +; CLEAR DISK BUFFERS ON FIRST TIME THROUGH + MOV ORIGIN+42,R0 ; 'FIRST' - BEGINNING OF DISK BUFFERS + MOV ORIGIN+44,R1 ; 'LIMIT' - JUST BEYOND DISK BUFFERS +1$: CLR (R0)+ + CMP R0,R1 + BLT 1$ +; NOW INITIALIZE 'OUT', 'OFFSET', 'USE' AND 'PREV' + CLR 42(U) ; CLEAR 'OUT' + CLR 46(U) ; CLEAR 'OFFSET' + MOV ORIGIN+42,72(U) ; TO 'USE' + MOV ORIGIN+42,74(U) ; TO 'PREV' +; END OF SPECIAL HIGH-MEMORY INITIALIZE + MOV #30,R1 ; ON COLD START, MOVE 24. BYTES + BR W2 +WENT: ; WARM START ENTRY POINT + MOV #12,R1 ; ON WARM START, MOVE TEN BYTES +W2: MOV #ORIGIN+22,R5 ; START MOVING FROM HERE + MOV ORIGIN+20,R0 ; MOVE TO THE USER AREA + ADD #6,R0 ; PLUS 6 + ADD R5,R1 ; COMPUTE LOOP STOP ADDRESS +1$: MOV (R5)+,(R0)+ + CMP R5,R1 + BLT 1$ + MOV ORIGIN+24,RP ; INITIALIZE THE RETURN-STACK POINTER +; NOW SET FORTH'S INSTRUCTION COUNTER, AND GO + MOV #GO,IP ; START EXECUTION WITH 'ABORT' + NEXT +; NOTE - NORMALLY THE ABOVE INSTRUCTION WOULD BE 'MOV #ABORT+2,IP'. +; IT HAS BEEN CHANGED HERE TO ALLOW USER TO PATCH A DIFFERENT +; START-UP. BUT THE SYSTEM WON'T WORK UNTIL SOME OF THE WORK +; OF 'ABORT' HAS BEEN DONE, SO THAT WORK IS REPEATED. THE USER +; CAN PATCH OVER THE 'ABORT' AND THE ZEROS. +; +GO: .WORD SPSTO,DEC,FORTH,DEFIN,ABORT,0,0,0 +; +; +; +; +; + HEAD 204,S->D,240,STOD ; ***** S->D + CLR -(S) ; SIGN EXTEND WITH ZEROS + TST 2(S) ; BUT IF NEGATIVE, + BPL 1$ + DEC (S) ; CHANGE THE ZEROS TO ONES +1$: NEXT +; +; NOTE - THIS SYSTEM DOESN'T NEED THE OPERATIONS '+-' AND 'D+-', +; BECAUSE 'M*' AND 'M/' ARE DEFINED IN CODE. +; + HEAD 203,ABS,323,ABS,DOCOL ; ***** ABS + .WORD DUP,ZLESS,ZBRAN,XXR5-.,MINUS +XXR5: .WORD SEMIS +; + HEAD 204,DABS,240,DABS,DOCOL ; ***** DABS + .WORD DUP,ZLESS,ZBRAN,XXRB-.,DMINU +XXRB: .WORD SEMIS +; + HEAD 203,MIN,316,MIN,DOCOL ; ***** MIN + .WORD OVER,OVER,GREAT,ZBRAN,XXR7-.,SWAP +XXR7: .WORD DROP,SEMIS +; + HEAD 203,MAX,330,MAX,DOCOL ; ***** MAX + .WORD OVER,OVER,LESS,ZBRAN,XXR6-.,SWAP +XXR6: .WORD DROP,SEMIS +; + HEAD 202,M*,240,MSTAR ; ***** M* + .IFDF EIS ; HARDWARE MULTIPLY/DIVIDE? + MOV (S)+,R0 + MUL (S),R0 + MOV R1,(S) + MOV R0,-(S) + NEXT + .IFF + MOV 2(S),-(RP) ; USE RETURN STACK FOR SAVING SIGN + BPL 1$ + NEG 2(S) ; GET ABSOLUTE VALUE +1$: TST (S) + BPL 2$ + NEG (RP) ; ADJUST SIGN WHICH WAS SAVED + NEG (S) ; GET ABSOLUTE VALUE +2$: JSR PC,UMULT + TST (RP)+ ; NEGATIVE RESULT? + BPL 3$ ; NO +; IF GET HERE, NEGATE THE DOUBLE-INTEGER NUMBER ON THE STACK + COM (S) + COM 2(S) + ADD #1,2(S) + ADC (S) +3$: NEXT + .ENDC +; + HEAD 202,M/,240,MSLAS ; ***** M/ + .IFDF EIS ; HARDWARD MULTIPLY/DIVIDE? + MOV 2(S),R0 + MOV 4(S),R1 + DIV (S)+,R0 + MOV R1,2(S) + MOV R0,(S) + NEXT + .IFF + MOV 2(S),-(RP) ; SAVE DIVIDEND SIGN + BNE 5$ ; ZERO WOULDN'T INDICATE + INC (RP) ; A SIGN CHANGE. +5$: MOV (RP),-(RP) ; DUPLICATE IT + BPL 1$ +; IF GET HERE, TAKE ABSOLUTE VALUE OF DOUBLE-INTEGER DIVIDEND. + COM 2(S) + COM 4(S) + ADD #1,4(S) + ADC 2(S) +1$: TST (S) ; IS DIVISOR NEGATIVE? + BPL 2$ + NEG (RP) ; IF YES, NEGATE QUOTIENT SIGN + NEG (S) ; AND TAKE ABS. VALUE OF DIVISOR +2$: JSR PC,UDIV + TST (RP)+ ; NEGATIVE QUOTIENT? + BPL 3$ ; NO + NEG (S) ; NEGATE THE QUOTIENT +3$: TST (RP)+ ; NEGATIVE DIVIDEND? + BPL 4$ ; NEGATE THE REMAINDER + NEG 2(S) +4$: NEXT + .ENDC +; + HEAD 201,*,252,STAR,DOCOL ; ***** * + .WORD MSTAR,DROP,SEMIS +; + HEAD 204,/MOD,240,SLMOD,DOCOL ; ***** /MOD + .WORD TOR,STOD,FROMR,MSLAS,SEMIS +; + HEAD 201,/,257,SLASH,DOCOL ; ***** / + .WORD SLMOD,SWAP,DROP,SEMIS +; + HEAD 203,MOD,304,MOD,DOCOL ; ***** MOD + .WORD SLMOD,DROP,SEMIS +; + HEAD 205,*/MOD,304,SSMOD,DOCOL ; ***** */MOD + .WORD TOR,MSTAR,FROMR,MSLAS,SEMIS +; + HEAD 202,*/,240,SSLA,DOCOL ; ***** */ + .WORD SSMOD,SWAP,DROP,SEMIS +; + HEAD 205,M/MOD,304,MSMOD,DOCOL ; ***** M/MOD + .WORD TOR,ZERO,R,USLAS,FROMR + .WORD SWAP,TOR,USLAS,FROMR,SEMIS +; +; +; +; +; +.PAGE +; **************************************************************** +; +; DISK I/O (SECTION COMMON TO ALL OPERATING SYSTEMS) +; NOTE THAT EACH OPERATING SYSTEM DEFINED 'R/W' - READ +; OR WRITE A 1024-BYTE RANDOM-ACCESS BLOCK. +; +; **************************************************************** +; +; +; 'USE' AND 'PREV' MOVED TO USER AREA +; + HEAD 204,+BUF,240,PBUF,DOCOL ; ***** +BUF + .WORD BBUF,LIT,4,PLUS,PLUS,DUP,LIMIT,AT,EQUAL + .WORD ZBRAN,XXT1-.,DROP,FIRST,AT +XXT1: .WORD DUP,PREV,AT,SUB,SEMIS +; + HEAD 206,UPDATE,240,UPDAT,DOCOL ; ***** UPDATE + .WORD PREV,AT,AT,LIT,100000,OR,PREV + .WORD AT,STORE,SEMIS +; + HEAD 215,EMPTY-BUFFERS,323,MTBUF,DOCOL ; ***** EMPTY-BUFFERS + .WORD FIRST,AT,LIMIT,AT,OVER,SUB,ERASE,SEMIS +; + HEAD 205,FLUSH,310,FLUSH,DOCOL ; ***** FLUSH +; SOME SYSTEMS DEFINE THIS IN THE EDITOR, NOT HERE. + .WORD LIMIT,AT,FIRST,AT,XDO +XXTA: .WORD I,AT,ZLESS,ZBRAN,XXT7-.,I,TWOP,I,AT + .WORD LIT,77777,AND,ZERO,RW +XXT7: .WORD BBUF,LIT,4,PLUS,XPLOO,XXTA-.,MTBUF,SEMIS +; + HEAD 203,DR0,260,DRZER,DOCOL ; ***** DR0 +; SELECT DRIVE #0 - NOT USED WITH RT11 OR RSX11 + .WORD ZERO,OFSET,STORE,SEMIS +; + HEAD 203,DR1,261,DRONE,DOCOL ; ***** DR1 +; SELECT DRIVE #1 - NOT USED IN RSX11 OR RT11 + .WORD LIT,240.,OFSET,STORE,SEMIS +; + HEAD 206,BUFFER,240,BUFFE,DOCOL ; ***** BUFFER + .WORD USE,AT,DUP,TOR +XXT2: .WORD PBUF,ZBRAN,XXT2-.,USE,STORE + .WORD R,AT,ZLESS,ZBRAN,XXT3-. + .WORD R,TWOP,R,AT,LIT,77777,AND + .WORD ZERO,RW +XXT3: .WORD R,STORE,R,PREV,STORE,FROMR,TWOP,SEMIS +; + HEAD 205,BLOCK,313,BLOCK,DOCOL ; ***** BLOCK +; CHANGED TO MASK OFF THE UPDATE BIT WHEN COMPARING + .WORD OFSET,AT,PLUS,TOR + .WORD PREV,AT,DUP,AT,LIT,077777,AND,R,SUB,ZBRAN,XXT4-. +XXT5: .WORD PBUF,ZEQU,ZBRAN,XXT6-. + .WORD DROP,R,BUFFE + .WORD DUP,R,ONE,RW,TWO,SUB +XXT6: .WORD DUP,AT,LIT,077777,AND,R,SUB,ZEQU + .WORD ZBRAN,XXT5-. + .WORD DUP,PREV,STORE +XXT4: .WORD FROMR,DROP,TWOP,SEMIS +; +; +; +; +; +; + HEAD 206,(LINE),240,PLINE,DOCOL ; ***** (LINE) + .WORD TOR,CL,BBUF,SSMOD,FROMR,BSCR + .WORD STAR,PLUS,BLOCK,PLUS,CL,SEMIS +; + HEAD 205,.LINE,305,DLINE,DOCOL ; ***** .LINE + .WORD PLINE,DTRAI,TYPE,SEMIS +; + HEAD 207,MESSAGE,305,MESS,DOCOL ; ***** MESSAGE + .WORD WARN,AT,ZBRAN,XXW5-.,DDUP,ZBRAN,XXW3-.,LIT,4 + .WORD OFSET,AT,BSCR,SLASH,SUB,DLINE +XXW3: .WORD BRAN,XXW4-. +XXW5: .WORD PDOTQ + .BYTE 6 + .ASCII /MSG # / + .EVEN + .WORD DOT +XXW4: .WORD SEMIS +; + HEAD 204,LOAD,240,LOAD,DOCOL ; ***** LOAD + .WORD BLK,AT,TOR,IN,AT,TOR,ZERO,IN,STORE + .WORD BSCR,STAR,BLK,STORE,INTER,FROMR,IN,STORE + .WORD FROMR,BLK,STORE,SEMIS +; + HEAD 303,-->,276,ARROW,DOCOL ; ***** --> + .WORD QLOAD,ZERO,IN,STORE,BSCR,BLK,AT,OVER + .WORD MOD,SUB,BLK,PSTOR,SEMIS +; +; +; +; +; NOTE - THE INSTALLATION-DEPENDENT I/O IS AT THE END +; OF THE DICTIONARY - JUST BELOW 'TASK'. 'XI/O' IS THE +; PRIMITIVE READ OR WRITE OF A 512-BYTE BLOCK. +; +; +; +; +; +; +.PAGE +; **************************************************************** +; +; MISCELLANEOUS HIGHER LEVEL +; +; **************************************************************** +; +; + HEAD 301,',247,TICK,DOCOL ; ***** ' + .WORD DFIND,ZEQU,ZERO,QERR,DROP,LITER,SEMIS +; + HEAD 206,FORGET,240,FORGE,DOCOL ; ***** FORGET + .WORD CURR,AT,CONT,AT,SUB,LIT,30,QERR,TICK,DUP + .WORD FENCE,AT,LESS,LIT,25,QERR + .WORD DUP,NFA,DP,STORE,LFA,AT,CONT,AT + .WORD STORE,SEMIS +; +; +; +; + HEAD 204,BACK,240,BACK,DOCOL ; ***** BACK + .WORD HERE,SUB,COMMA,SEMIS +; + HEAD 305,BEGIN,316,BEGIN,DOCOL ; ***** BEGIN + .WORD QCOMP,HERE,ONE,SEMIS +; + HEAD 305,ENDIF,306,ENDIF,DOCOL ; ***** ENDIF + .WORD QCOMP,TWO,QPAIR,HERE,OVER,SUB,SWAP,STORE,SEMIS +; + HEAD 304,THEN,240,THEN,DOCOL ; ***** THEN + .WORD ENDIF,SEMIS +; + HEAD 302,DO,240,DO,DOCOL ; ***** DO + .WORD COMP,XDO,HERE,LIT,3,SEMIS +; + HEAD 304,LOOP,240,LOOP,DOCOL ; ***** LOOP + .WORD LIT,3,QPAIR,COMP,XLOOP,BACK,SEMIS +; + HEAD 305,+LOOP,320,PLOOP,DOCOL ; ***** +LOOP + .WORD LIT,3,QPAIR,COMP,XPLOO,BACK,SEMIS +; + HEAD 305,UNTIL,314,UNTIL,DOCOL ; ***** UNTIL + .WORD ONE,QPAIR,COMP,ZBRAN,BACK,SEMIS +; + HEAD 303,END,304,END,DOCOL ; ***** END + .WORD UNTIL,SEMIS +; + HEAD 305,AGAIN,316,AGAIN,DOCOL ; ***** AGAIN + .WORD ONE,QPAIR,COMP,BRAN,BACK,SEMIS +; + HEAD 306,REPEAT,240,REPEAT,DOCOL ; ***** REPEAT + .WORD TOR,TOR,AGAIN,FROMR,FROMR,TWO,SUB,ENDIF,SEMIS +; + HEAD 302,IF,240,IF,DOCOL ; ***** IF + .WORD COMP,ZBRAN,HERE,ZERO,COMMA,TWO,SEMIS +; + HEAD 304,ELSE,240,ELSE,DOCOL ; ***** ELSE + .WORD TWO,QPAIR,COMP,BRAN,HERE,ZERO,COMMA + .WORD SWAP,TWO,ENDIF,TWO,SEMIS +; + HEAD 305,WHILE,305,WHILE,DOCOL ; ***** WHILE + .WORD IF,TWOP,SEMIS +; +; +; +; + HEAD 206,SPACES,240,SPACS,DOCOL ; ***** SPACES + .WORD ZERO,MAX,DDUP,ZBRAN,XXR4-.,ZERO,XDO +XXRA: .WORD SPACE,XLOOP,XXRA-. +XXR4: .WORD SEMIS +; + HEAD 202,^/<#/,240,BDIGS,DOCOL ; ***** <# + .WORD PAD,HLD,STORE,SEMIS +; + HEAD 202,#>,240,EDIGS,DOCOL ; ***** #> + .WORD DROP,DROP,HLD,AT,PAD,OVER,SUB,SEMIS +; + HEAD 204,SIGN,240,SIGN,DOCOL ; ***** SIGN + .WORD ROT,ZLESS,ZBRAN,XXR1-.,LIT,55,HOLD +XXR1: .WORD SEMIS +; + HEAD 201,#,243,DIG,DOCOL ; ***** # + .WORD BASE,AT,MSMOD,ROT,LIT,11,OVER,LESS + .WORD ZBRAN,XXR2-.,LIT,7,PLUS +XXR2: .WORD LIT,60,PLUS,HOLD,SEMIS +; + HEAD 202,#S,240,DIGS,DOCOL ; ***** #S +XXR3: .WORD DIG,OVER,OVER,OR,ZEQU,ZBRAN,XXR3-.,SEMIS +; + HEAD 203,D.R,322,DDOTR,DOCOL ; ***** D.R + .WORD TOR,SWAP,OVER,DABS,BDIGS,DIGS,SIGN,EDIGS + .WORD FROMR,OVER,SUB,SPACS,TYPE,SEMIS +; + HEAD 202,.R,240,DOTR,DOCOL ; ***** .R + .WORD TOR,STOD,FROMR,DDOTR,SEMIS +; + HEAD 202,D.,240,DDOT,DOCOL ; ***** D. + .WORD ZERO,DDOTR,SPACE,SEMIS +; + HEAD 201,.,256,DOT,DOCOL ; ***** . + .WORD STOD,DDOT,SEMIS +; + HEAD 201,?,277,QUEST,DOCOL ; ***** ? + .WORD AT,DOT,SEMIS +; + HEAD 202,U.,240,UDOT,DOCOL ; ***** U. + .WORD ZERO,DDOT,SEMIS +; +; UTILITY SECTION. +; +; + HEAD 204,LIST,240,LIST,DOCOL ; ***** LIST +; ( N---. LIST GIVEN SCREEN.) + .WORD DEC,CR,DUP,SCR,STORE,PDOTQ + .BYTE 6 + .ASCII /SCR # / + .EVEN + .WORD DOT,LIT,20,ZERO,XDO +XXZ1: .WORD CR,I,THREE,DOTR,SPACE + .WORD I,SCR,AT,DLINE,XLOOP,XXZ1-.,CR,SEMIS +; + HEAD 205,INDEX,330,INDEX,DOCOL ; ***** INDEX +; LIST FIRST LINE OF A RANGE OF DISK SCREENS. + .WORD CR,ONEP,SWAP,XDO +XXZ2: .WORD CR,I,THREE,DOTR,SPACE,ZERO,I,DLINE + .WORD QTERM,ZBRAN,XXZ3-.,LEAVE +XXZ3: .WORD XLOOP,XXZ2-.,SEMIS +; + HEAD 205,TRIAD,304,TRIAD,DOCOL ; ***** TRIAD +; LIST DISK SCREENS THREE PER PAGE. + .WORD LIT,14,EMIT ; FORM FEED + .WORD THREE,SLASH,THREE,STAR,THREE + .WORD OVER,PLUS,SWAP,XDO +XXZ4: .WORD CR,I,LIST,XLOOP,XXZ4-.,CR,LIT,17,MESS,CR,SEMIS +; + HEAD 205,VLIST,324,VLIST,DOCOL ; ***** VLIST + .WORD LIT,200,OUT,STORE,CONT,AT,AT +XXZ5: .WORD OUT,AT,LIT,100,GREAT,ZBRAN,XXZ6-. + .WORD CR,ZERO,OUT,STORE +XXZ6: .WORD DUP,IDDOT,SPACE,SPACE,PFA,LFA,AT + .WORD DUP,ZEQU,QTERM,OR,ZBRAN,XXZ5-.,DROP,SEMIS +; + .IFDF LINKS + HEAD 205,VLINK,313,XVLINK,DOCON ; ***** VLINK +; THIS IS ONLY USED FOR LINKAGE FROM FORTH TO SUBROUTINES +; IN OTHER LANGUAGES. SEE USER'S GUIDE FOR DOCUMENTATION. + .WORD VLINK + .ENDC +; +; +; +; +; +; +.PAGE +; **************************************************************** +; +; INSTALLATION-DEPENDENT SECTION (TERMINAL AND DISK I/O, AND TRAPS) +; +; **************************************************************** +; +; +; *************** +; +; RSX-11M TERMINAL I/O +; +; *************** +; + .IFDF RSX11 +; + .EVEN + ; NOTE - FOR RSX-11 ON HEAVILY LOADED MACHINES, IT IS BETTER + ; FOR 'KEY' TO READ A WHOLE LINE AT A TIME, AND UNPACK IT. + ; ALSO, 'KEY' SHOULD EMIT A LINE FEED WHEN A CARRIAGE RETURN + ; HAS BEEN READ. + +PEMIT: JSR R1,ITERM ; INITIALIZE RSX? +; INCREMENT 'OUT', UNLESS A CONTROL CHARACTER BEING OUTPUT. + CMP (S),#40 ; TEST FOR CONTROL CHARACTER + BLT 1$ + INC 42(U) ; INCREMENT 'OUT' +1$: + JSR R1,XCOUT ; OUTPUT A CHARACTER + NEXT +; +PKEY: JSR R1,ITERM ; INITIALIZE RSX? + TST INTERM ; ZERO MEANS READ NEW LINE + BNE XCHAR +XLINE: MOV #XBUFF,INTERM ; READ NEW LINE + QIOW$C IO.RVB,4,4,,IOSTAT,, + ADD #XBUFF,IOSTAT+2 ; TERMINATE LINE WITH CR + MOVB #15,@IOSTAT+2 +XCHAR: TST -(S) ; DECREMENT STACK POINTER + MOVB @INTERM,(S) ; FOR THIS BYTE INSTRUCTION + INC INTERM + BIC #177600,(S) + CMP (S),#15 ; IF CR IS BEING SENT, + BNE XRETRN + CLR INTERM ; THEN READ NEW LINE NEXT TIME. + MOV #12,-(S) ; AND ALSO EMIT A LINE FEED + JSR R1,XCOUT +XRETRN: NEXT +; +PQTER: JSR R1,ITERM ; INITIALIZE RSX? + MOV QFLAG,-(S) + CLR QFLAG + NEXT +; PUT THE AST ROUTINE HERE +AST1: MOV (RP)+,QFLAG ; SET UP FOR NEXT '?TERMINAL'; + ; NOTE THAT 'RP' IS SYSTEM STACK. + CMP QFLAG,#3 ; TEST FOR ^C + BNE 1$ + MOV #GO,IP +1$: ASTX$S +; +; +PCR: JSR R1,ITERM ; INITIALIZE RSX? + MOV #15,-(S) + JSR R1,XCOUT + MOV #12,-(S) + JSR R1,XCOUT + NEXT +; +XCOUT: MOV (S)+,IOCHR + QIOW$C IO.WVB!TF.WAL,4,4,,IOSTAT,, + RTS R1 +; +ITERM: ; INITIALIZE RSX IF FIRST TIME THROUGH + CMP INTERM,#-1 ; FIRST TIME TERMINAL I/O? + BNE RSXRTS + CLR INTERM ; YES + ALUN$C 4,TI,0 ; ASSIGN LUN + QIOW$C IO.ATA,4,,,,, ; ATTACH - UNSOLICITED I/O + SVTK$S #TRAPV,#6 ; SET UP FOR TRAPS +RSXRTS:RTS R1 +INTERM: .WORD -1 ; FLAG FOR FIRST TIME TERMINAL I/O. +; DO NOT REINITIALIZE 'INTERM' AT COLD START, LEST ASSIGNMENT DONE TWICE. +IOCHR: .WORD 0 ; TEMPORARY AREA FOR 'KEY' AND 'EMIT' +QFLAG: .WORD 0 ; FOR '?TERMINAL' +XBUFF: .BLKB 82. ; TERMINAL BUFFER FOR RSX LINE I/O +; +; +; + HEAD 203,BYE,305,BYE ; ***** BYE (LOG OFF) + CLOSE$ #FDBIO ; CLOSE DISK I/O + EXIT$S +; +; +; *************** +; +; RSX11-M DISK I/O +; +; *************** +; + HEAD 204,XI/O,240,XIO ; ***** XI/O (RSX) +; PHYSICAL READ-WRITE +; ADDRESS BLOCK# FLAG ==> REPORT. READS OR WRITES A 512-BYTE BLOCK. +; FLAG 1=READ, 0=WRITE. REPORT '0'=GOOD I/O, '1'=I/O ERROR. +; IF REPORT IS '1', THEN NEXT ON STACK IS '1'=OPEN ERROR, +; '2'=READ ERROR, '3'=WRITE ERROR, '4'=WAIT ERROR, '5'=ARGUMENT +; ERROR (FLAG NOT '0' OR '1'). + CLR DSKERR ; FOR I/O ERROR REPORT + TST OPENF ; DISK FILE ALREADY OPENED? + BNE 2$ + OPEN$M #FDBIO + BCC 2$ + MOV #1,DSKERR ; ERROR IN OPEN + BR ERRR +2$: MOV #1,OPENF ; INDICATE FILE IS OPEN + CLR VIRBLK + MOV 2(S),VIRBLK+2 ; SET UP VIRTUAL BLOCK NUMBER + MOV 4(S),IOADDR ; SET UP I/O ADDRESS + TST (S) ; WAS TOP OF STACK - READ OR WRITE? + BEQ WRITE + CMP (S),#1 + BEQ READ + MOV #5,DSKERR ; ERROR, FLAG NOT EITHER '0' OR '1' + BR ERRR +READ: READ$ #FDBIO,IOADDR,,#VIRBLK,#2 + BCC WAIT + MOV #2,DSKERR ; ERROR IN READ + BR ERRR +WRITE: WRITE$ #FDBIO,IOADDR,,#VIRBLK,#2 + BCC WAIT + MOV #3,DSKERR ; ERROR IN WRITE + BR ERRR +WAIT: WAIT$ + BCC DONE + MOV #4,DSKERR ; ERROR IN WAIT + BR ERRR +DONE: ADD #6,S + CLR -(S) ; INDICATE GOOD I/O + BR DONE2 +ERRR: ADD #6,S + MOV DSKERR,-(S) ; RETURN THE ERROR INDICATOR + MOV #1,-(S) ; INDICATE ERROR IN I/O +DONE2: NEXT + FSRSZ$ 0 +FDBIO: FDBDF$ + FDRC$A FD.RWM + FDBK$A ,512.,,2,IOSTAT + FDOP$A 3,DESCR,,FO.MFY +DESCR: .WORD 0,0 ; USE DEFAULT DEVICE + .WORD 0,0 ; AND DIRECTORY. + .WORD FILSZ,FIL +FIL: .ASCII /FORTH.DAT/ +FILSZ=.-FIL + .EVEN +; +OPENF: .WORD 0 ; FLAG FOR FIRST TIME DISK I/O +; DO NOT INITIALIZE 'OPENF' AT COLD START +DSKERR: .WORD 0 ; SPACE FOR DISK ERROR MESSAGE +IOADDR: .WORD 0 ; ADDRESS FOR DISK READ/WRITE +IOSTAT: .BLKW 2 ; I/O STATUS REPORT +VIRBLK: .BLKW 2 ; VIRTUAL BLOCK NUMBER +; + HEAD 212,BLOCK-READ,240,BREAD,DOCOL ; ***** BLOCK-READ +; ( ADDRESS BLOCK# ==> REPORT). REPORT: 0=GOOD READ, ELSE ERROR + .WORD ONE,XIO,SEMIS +; + HEAD 213,BLOCK-WRITE,305,BWRIT,DOCOL ; ***** BLOCK-WRITE +; ( ADDRESS BLOCK# ==> REPORT). REPORT: 0=GOOD WRITE, ELSE ERROR + .WORD ZERO,XIO,SEMIS +; + HEAD 203,I/O,317,IO,DOCOL ; ***** I/O +; READ OR WRITE 512-BYTE BLOCK, HANDLE ERRORS. +; ( ADDRESS BLOCK# FLAG(1=READ,0=WRITE) ==> ) + .WORD DUP,ONE,EQUAL,ZBRAN,XXS1-.,DROP,BREAD + .WORD ZBRAN,XXS2-.,CR,PDOTQ + .BYTE 22 + .ASCII /DISK READ ERROR # / + .EVEN + .WORD DOT,ABORT +XXS2: .WORD BRAN,XXS3-. +XXS1: .WORD ZEQU,ZBRAN,XXS4-.,BWRIT,ZBRAN,XXS5-. + .WORD CR,PDOTQ + .BYTE 23 + .ASCII /DISK WRITE ERROR # / + .EVEN + .WORD DOT,ABORT +XXS5: +XXS4: +XXS3: .WORD SEMIS +; + HEAD 203,R/W,327,RW,DOCOL ; ***** R/W +; READ OR WRITE 1024-BYTE SCREEN. ( ADDRESS SCREEN# FLAG ==> ) +; NOTE THAT SCREEN N IS BLOCKS 2N-1 AND 2N. + .WORD TOR,TWO,STAR,OVER,OVER,ONE,SUB,R,IO + .WORD SWAP,LIT,512.,PLUS + .WORD SWAP,FROMR,IO,SEMIS +; + .ENDC +; +; *************** +; +; RT-11 TERMINAL I/O +; +; *************** +; + .IFDF RT11 +ITERM: CMP INTERM,#-1 + BNE RTRTS + CLR INTERM + .RCTRLO ; RESET CNTL-O + .TRPSET #TRAPBL,#TRAPZ +RTRTS: RTS R1 +; +PEMIT: + JSR R1,ITERM +; INCREMENT 'OUT', UNLESS A CONTROL CHARACTER BEING OUTPUT. + CMP (S),#40 ; TEST FOR CONTROL CHARACTER + BLT 1$ + INC 42(U) ; INCREMENT 'OUT' +1$: + JSR R1,XCOUT + NEXT +; +PKEY: JSR R1,ITERM + .TTYIN + BIC #177600,R0 + CMP R0,#12 ; IGNORE LINEFEED + BEQ PKEY + MOV R0,-(S) + NEXT +; +PQTER: JSR R1,ITERM + MOV 44,-(RP) ; SAVE JSW + BIS #10100,44 ; SET BITS 6 AND 12 OF JSW + .TTINR + BCC 1$ +; IF CARRY SET, NO CHARACTER - SUPPLY ZERO + CLR R0 +1$: BIC #177600,R0 + CMP R0,#12 ; IGNORE LINEFEED + BNE 2$ + CLR R0 +2$: MOV R0,-(S) + MOV (RP)+,44 ; RESTORE JSW + NEXT +; +PCR: JSR R1,ITERM + MOV #15,-(S) + JSR R1,XCOUT + MOV #12,-(S) + JSR R1,XCOUT + NEXT +; +XCOUT: MOV (S)+,R0 + .TTYOUT + RTS R1 +; +INTERM: .WORD -1 ; FLAG FOR FIRST TIME TERMINAL I/O +; DO NOT INITIALIZE 'INTERM' AT COLD START +IOCHR: .WORD 0 ; TEMPORARY AREA FOR 'KEY', 'EMIT' +; + HEAD 203,BYE,305,BYE ; ***** BYE (RT) + .EXIT +; +; +; *************** +; +; RT-11 DISK I/O +; +; *************** +; + HEAD 204,XI/O,240,XIO ; ***** XI/O (RT) + CLR DSKERR + TST OPENF + BNE RTOPEN + MOV #1,OPENF ; INDICATE FILE IS OPEN +; NOW OPEN THE FILE + .SETTOP #-2 + MOV #RTSTAT,R1 + .DSTATUS R1,#RTFILE + BCC 1$ + MOV #1,DSKERR + BR RTRET +1$: TST 4(R1) ; HANDLER IN? + BNE 2$ + .FETCH HANDLR,#RTFILE + BCC 2$ + MOV #2,DSKERR + BR RTRET +2$: .LOOKUP #LOOK1,#0,#RTFILE + BCC RTOPEN + MOV #3,DSKERR + BR RTRET +RTOPEN: ; FILE IS OPEN - NOW READ IT + MOV 2(S),R1 ; BLOCK # + DEC R1 ; BEGINS AT 1 IN FORTH + MOV 4(S),IOADDR ; BUFFER ADDRESS + TST (S) + BEQ WRITE + CMP (S),#1 + BEQ READ + MOV #5,DSKERR + BR RTRET +READ: .READW #RTBLK,#0,IOADDR,,R1 + BCC 1$ + MOV #6,DSKERR +1$: BR RTRET +WRITE: .WRITW #RTBLK,#0,IOADDR,,R1 + BCC 2$ + MOV #7,DSKERR +2$: BR RTRET +RTRET: ADD #6,S + MOV DSKERR,-(S) + TST DSKERR + BEQ 1$ + MOV #1,-(S) ; INDICATE ERROR OCCURRED +1$: NEXT +RTFILE: .RAD50 /DK FORTH DAT/ +RTBLK: .BYTE 0,10 + .WORD 0,0,256.,0 +; +OPENF: .WORD 0 ; FLAG FOR FIRST TIME DISK I/O +DSKERR: .WORD 0 ; SPACE FOR DISK ERROR MESSAGE +IOADDR: .WORD 0 ; ADDRESS FOR DISK READ/WRITE +RTSTAT: .BLKW 4 ; DISK I/O STATUS +LOOK1: .BLKW 3 ; EMT ARGUMENT BLOCK +TRAPBL: .BLKW 2 ; EMT ARGUMENT BLOCK +; + HEAD 212,BLOCK-READ,240,BREAD,DOCOL ; ***** BLOCK-READ +; ( ADDRESS BLOCK# ==> REPORT). REPORT: 0=GOOD READ, ELSE ERROR + .WORD ONE,XIO,SEMIS +; + HEAD 213,BLOCK-WRITE,305,BWRIT,DOCOL ; ***** BLOCK-WRITE +; ( ADDRESS BLOCK# ==> REPORT). REPORT: 0=GOOD WRITE, ELSE ERROR + .WORD ZERO,XIO,SEMIS +; + HEAD 203,I/O,317,IO,DOCOL ; ***** I/O +; READ OR WRITE 512-BYTE BLOCK, HANDLE ERRORS. +; ( ADDRESS BLOCK# FLAG(1=READ,0=WRITE) ==> ) + .WORD DUP,ONE,EQUAL,ZBRAN,XXS1-.,DROP,BREAD + .WORD ZBRAN,XXS2-.,CR,PDOTQ + .BYTE 22 + .ASCII /DISK READ ERROR # / + .EVEN + .WORD DOT,ABORT +XXS2: .WORD BRAN,XXS3-. +XXS1: .WORD ZEQU,ZBRAN,XXS4-.,BWRIT,ZBRAN,XXS5-. + .WORD CR,PDOTQ + .BYTE 23 + .ASCII /DISK WRITE ERROR # / + .EVEN + .WORD DOT,ABORT +XXS5: +XXS4: +XXS3: .WORD SEMIS +; + HEAD 203,R/W,327,RW,DOCOL ; ***** R/W +; READ OR WRITE 1024-BYTE SCREEN. ( ADDRESS SCREEN# FLAG ==> ) +; NOTE THAT SCREEN N IS BLOCKS 2N-1 AND 2N. + .WORD TOR,TWO,STAR,OVER,OVER,ONE,SUB,R,IO + .WORD SWAP,LIT,512.,PLUS + .WORD SWAP,FROMR,IO,SEMIS +; +; + .ENDC +; +; *************** +; +; STAND-ALONE TERMINAL I/O +; +; **************** +; + .IFDF ALONE +PEMIT: +; INCREMENT 'OUT', UNLESS A CONTROL CHARACTER BEING OUTPUT. + CMP (S),#40 ; TEST FOR CONTROL CHARACTER + BLT 1$ + INC 42(U) ; INCREMENT 'OUT' +1$: TST @#177564 + BEQ 1$ + BIC #177600,(S) ; CLEAR UPPER BITS FOR ASCII + MOV (S)+,@#177566 ; ONLY OUTPUTS LOW 8 BITS + NEXT +; +PKEY: TSTB @#177560 + BEQ PKEY + CLR @#177560 + MOVB @#177562,R1 + BIC #177600,R1 + CMP #177,R1 + BNE 1$ + MOV #10,R1 +1$: MOV R1,-(S) + NEXT +; +PQTER: TSTB @#177560 + BEQ 1$ + MOV @#177562,-(S) + BR 2$ +1$: CLR -(S) +2$: CLR @#177560 + NEXT +; +PCR: TST @#177564 + BEQ PCR + MOV #15,@#177566 +1$: TST @#177564 + BEQ 1$ + MOV #12,@#177566 + NEXT +; + HEAD 203,BYE,305,BYE ; ***** BYE (ALONE) + + HALT +; +; *************** +; +; STAND-ALONE DISK I/O +; +; *************** +; +RXCS=177170 ; CONTROL AND STATUS REGISTER +RXDB=177172 ; DATA BUFFER REGISTER +; +; + HEAD 204,NRTS,240,NRTS ; ***** NRTS +; ADDRN TRN SECN...ADDR1 TR1 SEC1 N -> FLAG +; READ N SECTORS. USES R0, R1, R2 +; THIS OPERATION IS IN CODE TO KEEP UP WITH DISK TIMING FOR +; STANDARD PDP-11 SECTOR SKEWING. + MOV (S)+,R1 ; # OF SECTORS TO READ +1$: MOV #10,R2 ; RETRY COUNT +21$: MOV #7,R0 ; 'READ' COMMAND + JSR PC,DRIV2 ; ADJUST R0 COMMAND + ; IF SECOND DRIVE + MOV R0,@#RXCS ; READ COMMAND +2$: BIT #200,@#RXCS ; WAIT FOR TRANSFER FLAG + BEQ 2$ + MOV (S),@#RXDB ; SECTOR # +3$: BIT #200,@#RXCS ; WAIT FOR TRANSFER FLAG + BEQ 3$ + MOV 2(S),@#RXDB ; TRACK # +4$: BIT #40,@#RXCS ; WAIT FOR DONE FLAG + BEQ 4$ +; CHECK FOR ERROR + TST @#RXCS + BLT 20$ ; ERROR +; + MOV #3,@#RXCS ; 'EMPTY' COMMAND +; EMPTY THE CONTROLLER'S BUFFER + MOV 4(S),R0 ; ADDRESS TO RECEIVE DATA + MOV #200,-(S) ; COUNT OF TIMES TO LOOP +6$: BIT #200,@#RXCS ; WAIT FOR TRANSFER FLAG + BEQ 6$ + MOVB @#RXDB,(R0)+ + DEC (S) ; DECREMENT THE COUNT + BNE 6$ + TST (S)+ ; POP THE COUNT +; CHECK FOR ERROR + TST @#RXCS + BGE 7$ +20$: ; ERROR, SO RE-TRY + MOV #40000,@#RXCS ; CLEAR ERROR STATUS +22$: BIT #40,@#RXCS + BEQ 22$ + DEC R2 ; RE-TRY COUNT + BGT 21$ + MOV #-1,-(S) + NEXT ; ERROR EXIT +7$: ADD #6,S ; GOOD READ, SO POP THE 3 ARGS + DEC R1 + BNE 1$ ; LOOP UNLESS ALL SECTORS READ +; + CLR -(S) ; GOOD-READ INDICATOR + NEXT ; EXIT +; +; SUBROUTINE TO ADJUST COMMAND FOR SECOND DISK DRIVE +; NOTE - 'NWTS' ALSO USES THIS SUBROUTINE. +; NOTE USE OF R0, AND OF FORTH STACK. +DRIV2: CMP 2(S),#114 ; TRACK > 76 ? + BLE 10$ + SUB #115,2(S) ; SUBTRACT 77 + BIS #20,R0 ; SET UNIT-SELECT BIT +10$: RTS PC +; + HEAD 204,NWTS,240,NWTS ; ***** NWTS +; ADDRN TRN SECN...ADDR1 TR1 SEC1 N -> FLAG +; WRITE N SECTORS. USES R0, R1, R2. + MOV (S)+,R1 ; # OF SECTORS TO BE WRITTEN +1$: MOV #10,R2 ; RE-TRY COUNT +21$: MOV #1,@#RXCS ; 'FILL' COMMAND +2$: BIT #200,@#RXCS ; WAIT FOR TRANSFER FLAG + BEQ 2$ +; NOW FILL THE BUFFER + MOV 4(S),R0 + MOV #200,-(S) ; COUNT +3$: BIT #200,@#RXCS ; WAIT FOR TRANSFER FLAG + BEQ 3$ + MOVB (R0)+,@#RXDB ; MOVE ONE BYTE + DEC (S) + BNE 3$ + TST (S)+ ; POP STACK +; CHECK FOR ERROR + TST @#RXCS + BLT 20$ ; ERROR +; + MOV #5,R0 ; 'WRITE' COMMAND + JSR PC,DRIV2 ; ADJUST IF SECOND DRIVE + MOV R0,@#RXCS ; 'WRITE' COMMAND +5$: BIT #200,@#RXCS ; WAIT FOR TRANSFER FLAG + BEQ 5$ + MOV (S),@#RXDB ; MOVE SECTOR # +6$: BIT #200,@#RXCS ; WAIT FOR TRANSFER FLAG + BEQ 6$ + MOV 2(S),@#RXDB ; MOVE TRACK # +7$: BIT #40,@#RXCS ; WAIT FOR DONE FLAG + BEQ 7$ +; CHECK FOR ERROR + TST @#RXCS + BGE 10$ +20$: ; ERROR SO RE-TRY + MOV #40000,@#RXCS ; CLEAR ERROR STATUS +22$: BIT #40,@#RXCS + BEQ 22$ + DEC R2 ; RE-TRY COUNT + BGT 21$ + MOV #-1,-(S) ; ERROR INDICATOR + NEXT ; EXIT +10$: ADD #6,S ; GOOD WRITE, SO POP THE 3 ARGS + DEC R1 + BNE 1$ ; LOOP UNLESS ALL SECTORS WRITTEN +; + CLR -(S) ; GOOD-WRITE INDICATOR + NEXT +; + HEAD 203,RTS,323,RTS,DOCOL ; ***** RTS +; ADDR TR SEC -> +; READ A SINGLE SECTOR. + .WORD ONE,NRTS,ZBRAN,1$-.,PDOTQ + .BYTE 26 + .ASCII /DISK READ ERROR IN RTS/ + .EVEN + .WORD QUIT +1$: .WORD SEMIS +; + HEAD 203,WTS,323,WTS,DOCOL ; ***** WTS +; ADDR TR SEC -> +; WRITE A SINGLE SECTOR. + .WORD ONE,NWTS,ZBRAN,1$-.,PDOTQ + .BYTE 30 + .ASCII /DISK WRITE ERROR IN WTS / + .EVEN + .WORD QUIT +1$: .WORD SEMIS +; +; + HEAD 205,SKEW1,261,SKEW1,DOCOL ; ***** SKEW1 +; SEQUENCE -> TRACK SECTOR +; HANDLE THE SECTOR SKEWING. +; NOTE - 'SEQUENCE #' IS ZERO-ORIGIN INDEX OF SECTOR (SKEWED). +; NOTE - 'SKEW1' DOES SKEWING OF ONLY ONE DRIVE; 'SKEW' GENERALIZES +; 'SKEW1' TO BOTH DRIVES. + .WORD DUP,LIT,32,SLASH,SWAP + .WORD OVER,LIT,6,STAR,OVER,DUP,PLUS,PLUS,SWAP + .WORD LIT,32,MOD,LIT,15,SLASH,PLUS + .WORD LIT,32,MOD,ONEP + .WORD SWAP,ONEP,SWAP,SEMIS +; + HEAD 206,S-SKIP,240,SSKIP,DOVAR ; ***** S-SKIP +; VARIABLE - NUMBER OF SECTORS SKIPPED AT BEGINNING OF DISK. +; DEFAULT IS 56 DECIMAL (SKIP AN RT-11 DIRECTORY). ALSO, TRACK +; ZERO IS SKIPPED, FOR COMPATIBILITY. + .WORD 56. +; + HEAD 206,S-USED,240,SUSED,DOVAR ; ***** S-USED +; VARIABLE - NUMBER OF SECTORS USED ON ON ONE DISK. +; NORMALLY, S-USED + S-SKIP = 1976. (2002. - 26. OF TR 0). + .WORD 1920. +; + HEAD 204,SKEW,240,SKEW,DOCOL ; ***** SKEW +; SEQUENCE# -> TRACK SECTOR + .WORD DUP,ONEP,SUSED,AT,GREAT,ZBRAN,1$-. + .WORD SUSED,AT,SUB,SSKIP,AT,PLUS,SKEW1 + .WORD SWAP,LIT,77.,PLUS,SWAP + .WORD BRAN,2$-. +1$: .WORD SSKIP,AT,PLUS,SKEW1 +2$: .WORD SEMIS +; + HEAD 206,NSETUP,240,NSET,DOCOL ; ***** NSETUP +; ADDR SEQUENCE# N -> ADDRN TRN SECN...ADDR1 TR1 SEC1 +; THIS PREPARES A WHOLE SCREEN (IF N=8) FOR 'NRTS' OR 'NWTS'. + .WORD ROT,OVER,LIT,128.,STAR,PLUS,ROT,ROT + .WORD OVER,PLUS,ONE,SUB,SWAP,ONE,SUB,SWAP + .WORD XDO +2$: .WORD LIT,128.,SUB,DUP,I,SKEW,ROT + .WORD LIT,-1,XPLOO,2$-. + .WORD DROP,SEMIS +; + HEAD 203,R/W,327,RW,DOCOL ; ***** R/W +; READ OR WRITE 1024-BYTE SCREEN. +; ADDR BLOCK# FLAG(R=1,W=0) -> + .WORD TOR,ONE,SUB,LIT,8.,STAR,FROMR +; CHANGE THE SCREEN # TO FIRST SEQUENCE #. +; IF READ, SETUP AND READ 8 SECTORS + .WORD ZBRAN,1$-.,LIT,8.,NSET,LIT,8.,NRTS + .WORD ZBRAN,2$-.,PDOTQ + .BYTE 20 + .ASCII /DISK READ ERROR / + .EVEN + .WORD QUIT +2$: .WORD BRAN,3$-. +; SETUP AND WRITE 8 SECTORS +1$: .WORD LIT,8.,NSET,LIT,8.,NWTS + .WORD ZBRAN,4$-.,PDOTQ + .BYTE 20 + .ASCII /DISK WRITE ERROR/ + .EVEN + .WORD QUIT +4$: +3$: .WORD SEMIS +; + .ENDC +; +; *************** +; +; TRAP RECOVERY SECTION, RSX-11M +; +; *************** +; + .IFNDF ALONE ; STAND-ALONE MUST HANDLE OWN INTERRUPTS. + HEAD 205,TRAPS,323,TRAPS,DOCOL ; ***** TRAPS + .WORD CR,PDOTQ + .BYTE 14 + .ASCII /TRAP-ERROR, / + .EVEN + .WORD DOT,SWAP,UDOT,UDOT,QUIT + .ENDC +; + .IFDF RSX11 +TRAPV: .WORD TRAP0,TRAP1,TRAP2,TRAP3,TRAP4,TRAP5 +TRAP0: CLR R1 ; TRAP # 0 + BR TRAPZ +TRAP1: MOV #1,R1 ; TRAP # 1 + ADD #6,SP ; DROP MMU INFO + BR TRAPZ +TRAP2: MOV #2,R1 + BR TRAPZ +TRAP3: MOV #3,R1 + BR TRAPZ +TRAP4: MOV #4,R1 + BR TRAPZ +TRAP5: MOV #5,R1 + BR TRAPZ +; TO RETURN FROM TRAP HANDLER, SET UP STACK, ETC. FOR FORTH 'TRAPS' +; DON'T USE RTT OR RTI. +TRAPZ: MOV (SP)+,-(S) ; PC + MOV (SP)+,-(S) ; PS + MOV R1,-(S) ; TRAP # + MOV #TRAPS+2,IP ; EXECUTE 'TRAPS' + NEXT +; + .ENDC +; **************** +; +; TRAP RECOVERY SECTION, RT-11 +; +; **************** + .IFDF RT11 +TRAPZ: BCS 1$ +; IF CARRY CLEAR, TRAP 4 + MOV #4,R1 + BR 2$ +1$: MOV #10,R1 +2$: MOV (SP)+,-(S) ; PC + MOV (SP),-(S) ; PS + MOV R1,-(S) ; TRAP # + MOV #3$,-(SP) ; SO RTI WILL RESTORE PC TO '3$' + .TRPSET #TRAPBL,#TRAPZ ; RE-SET TRAPS + RTI +3$: MOV #TRAPS+2,IP ; EXECUTE 'TRAPS' + NEXT + .ENDC +; +; +.PAGE +; NOTE - '.W' ('DW') IS USED ONLY FOR TESTING - TO GET OCTAL OUTPUT +; WHEN '.' IS NOT WORKING DURING SYSTEM DEVELOPMENT. +; +; +; HEAD 202,.W,240,DW ; ***** .W +; MOV (S),XOUT +; ROL XOUT +; ROL XOUT +; MOV XOUT,IOCHR +; ROR XOUT +; BIC #177776,IOCHR +; ADD #60,IOCHR +; MOV IOCHR,-(S) +; JSR R1,XCOUT +; MOV #5,XCOUNT +;XLP: ROL XOUT +; ROL XOUT +; ROL XOUT +; ROL XOUT +; MOV XOUT,IOCHR +; ROR XOUT +; BIC #177770,IOCHR +; ADD #60,IOCHR +; MOV IOCHR,-(S) +; JSR R1,XCOUT +; DEC XCOUNT +; BNE XLP +; MOV #40,IOCHR +; MOV IOCHR,-(S) +; JSR R1,XCOUT +; NEXT +;XOUT: .WORD 0 +;XCOUT: .WORD 0 +; +; +; +; +; +; **************************************************************** +; +; THE FOLLOWING TWO DEFINITIONS ARE NOT PURE CODE, SO THEY WERE +; MOVED HERE, NEAR THE END OF THE DICTIONARY. +; +; **************************************************************** +; + HEAD 305,<;CODE>,305,SEMIC,DOCOL ; ***** ;CODE +; CREATE NEW DATA TYPE WITH CODE ROUTINE WRITTEN IN ASSEMBLY. + .WORD QCSP,COMP,PSCOD,LBRAC,SMUDG,SEMIS +; NOTE: LATER, THE ASSEMBLER WILL PATCH THIS DEFINITION. +; + HEAD 305,FORTH,310,FORTH,DODOE ; ***** FORTH + .WORD DOVOC + .WORD 120201 ; DUMMY HEADER AT INTERSECTION + .WORD TASK-10 +XXVOC: .WORD 0 ; THE VOCABULARY LINK (FOR FUTURE USE) + HEAD 204,TASK,240,TASK,DOCOL ; ***** TASK + .WORD SEMIS +; +; +; +; +.PAGE +; **************************************************************** +; +; STACKS AND BUFFERS +; +; **************************************************************** +; +; NOTE - 'UP', 'OPENF', 'INTERM', AND DISK BUFFERS ARE +; INITIALIZED AT COLD START, OR AT FIRST TIME THROUGH. + .EVEN +XDP: ; DICTIONARY STARTS HERE + .BLKB 8000. ; FOR DICTIONARY AND COMP. STACK +; INCREASE THIS NUMBER TO USE A LARGER MEMORY SIZE. +XS0: ; START OF COMPUTATION STACK + .BLKW 2 ; IN CASE OF EMPTY STACK +; +; +; +; +; +DSKBUF: ; ROOM FOR 3 1K DISK BUFFERS +; INITIALIZE BUFFERS' UPDATE BITS, AND TERMINATING NULLS, TO ZERO. +; NOTE - THESE BUFFERS ARE CLEARED AT COLD START, ANYWAY, +; BECAUSE A STAND-ALONE BOOT MAY NOT INITIALIZE HIGH MEMORY; +; AND ALSO SO THAT THE NUMBER OR LOCATION OF BUFFERS CAN BE +; CHANGED AT RUN TIME. + .BLKW 1 + .BLKB 1024. + .BLKW 1 + .BLKW 1 + .BLKB 1024. + .BLKW 1 + .BLKW 1 + .BLKB 1024. + .BLKW 1 +ENDBUF: ; CAUTION - 'ENDBUF' - 'DSKBUF' MUST BE EXACT MULTIPLE +; OF THE BUFFER LENGTH PLUS 4. +; +; +; +; +; +; 'XTIB', 'XR0', AND 'XUP' ARE ONLY USED IN BOOT-UP TABLE; +; THEREFORE THE AREAS DEFINED HERE CAN BE MOVED AT RUN TIME. +XTIB: .BLKW 42. ; TERMINAL INPUT BUFFER + .BLKW 50. ; FOR RETURN STACK +XR0=. +XUP: .BLKW 100 ; ROOM FOR 100 USER VARIABLES +; +; + .IFDF RT11 +; DISK HANDLER GOES HERE +HANDLR: .WORD .+2 + .ENDC +; +; +; NOTE - CHANGE THE FOLLOWING LINE TO '.END' IF LINKING TO OTHER LANGUAGES. + .END ORIGIN \ No newline at end of file diff --git a/macro-asm/odt11.mac b/macro-asm/odt11.mac new file mode 100755 index 0000000..6eef81b --- /dev/null +++ b/macro-asm/odt11.mac @@ -0,0 +1,1078 @@ + .title odt11s + .enabl lc, ama + .sbttl Initialisation & command processors. + +;Borrowed from: http://dph.fluff.org/pdp11/odts.mac + +;Version of ODT to be blown into a 2716 PROM for use as console ODT on a T-11 +;system. This code started life as the DECUS offering ODT11X. It now has many +;more features resembling those of RT-11's ODT. + +;Possible additions: +; XON - XOFF recognition +; Aborting printout after ;W & ;E +;
;A Ascii string print/change + +;Conditionals are: +test=1 ;Define when testing the code under RT-11 +eis=1 ;Use the EIS instructions (Only uses SOB) +;lsi=1 ;Use MTPS/MFPS, not bus addr 177776 for the status register. + +.if ndf,eis + .macro sob r,b ;No EIS instructions - macro to allow SOB + dec r + bne b + .endm +.endc +.if ndf,lsi + st=177776 ;Not LSI - address of status register + .macro mtps status ;Not LSI - macro to allow MTPS + movb status,@#st + .endm +.endc + +.if df,test +o.break = 3 ;BREAK instruction - 3 for normal PDP-11s + .csect odt11s +.iff +o.break = 0 ;BREAK instruction - is 0 for T-11 + .asect + .=170000 +.endc + +bkp = 16 ;Number of breakpoints-1 mult. by 2 +tvec = 14 ;BREAK vector location +stm = 340 ;Priority mask - status register +tbt = 20 ;T-bit mask - status register + +rcsr = 177560 ;r c/sr Console input port addresses +rdb = 177562 ;r data buffer +tcsr = 177564 ;t c/sr Console output port addresses +tdb = 177566 ;t data buffer + +cr = 15 ;Carrage return +lf = 12 ;Line feed + +; Initialise ODT + +odt:: +.if df,test + jsr r5,svttyp ;Keep RT-11 terminal status, say hello + .asciz 'ODT11' + mov #break,@#tvec ;PC to break vector + mov #stm,@#tvec+2 ;Status word to break vector+2 +.iff + clr csr1 ;Clear 'saved' terminal status regs. +.endc + mov #reloc,sp ;Set up stack pointers to init. tables + mov sp,r0 + clr r1 ;& setup some other vectors +1$: clr -(sp) ;Clear out registers + mov #-1,(r0)+ ;Remove relocation constants +.if ndf,test + mov #break,(r1)+ ;Set up a load of vectors as BPT's + mov #stm,(r1)+ +.endc + cmp sp,#ur0 ;All registers set up yet ? + bhi 1$ ;Loop if not + mov #1000,usp ;Give the user a possible stack + movb -(r0),p ;Disallow proceed + clr s.t ;Disable single instruction & T flags + mov #7,pri ;Set default ODT priority + jmp rall ;Clear breakpoint tables + +; Output the top RAD50 char from r4. The RAD50 character set used is: +; " ABCDEFGHIJKLMNOPQRSTUVWXYZ$./0123456789" +;Note that in this encoding, '$' is 33, the ASCII code for escape. +;There's probably a nice trick in there somewhere, if I could see it :-) + +chr50o: clr r0 ;Extract a RAD50 char from r4 +1$: inc r0 ;r0 := r4/1600. r4 := r4%1600. + sub #1600.,r4 ; By repeated subtraction + bhis 1$ + add #1600.,r4 ;Correct for the extra subtract + mov #" [,-(sp) ;Convert single RAD50 char to ASCII + dec r0 ;RAD50 0 is space, is on the stack + beq 3$ +2$: swab (sp) ;Not space, add '@' to convert letters + sub #27.,r0 ;Is it really a letter ? + blo 3$ + mov #"$H,(sp) ;No, must be $./ or digit + tst r0 ;Is it $ ? + bne 2$ ;No, add '-'+27='H' for ./01234567 +3$: add (sp)+,r0 ;Convert the character + jsr r5,ftyp ;Print it out + clr r0 ;Multiply r4 by octal 50 to do the next char. + +;Routine used by RAD50 conversions, and octal numeric input. +;After calling MUL50: r4 := r4 * 50 + r0, r0 := r4 * 8 + r0, r2 := r2 + 1 +;After calling MULIN: r4 := r4 * 4 + r0, r2 := r2 + 1 + +mul50: asl r4 + asl r4 + asl r4 + add r4,r0 +mulin: asl r4 + asl r4 + add r0,r4 + inc r2 ;Count this char + rts pc + +;Accept a RAD50 char & pack into r4 + +chr50i: jsr r5,get ;Get a char + mov #40,r1 ;Put constant 40 in R1 + cmp r0,r1 ;Is char a space ? + beq 3$ ;Branch if space + blo clgl1 ;Lower than space - exit + cmp r0,#'A ;Possible letter ? + bhis 1$ + mov #11,r1 ;Not letter, change constant + cmp r0,#'$ ;$=44 + beq 3$ ;Is '$' - return 44-11=33 + cmp r0,#'. ;Not '$', range .-9 ? (.=56) + blo clgl1 ;No - exit + cmp r0,#'9 ;Is it ? + br 2$ + +1$: ; BIC R1,R0 ;Fold lower case to upper + cmp r0,#'Z ;Alphabetic ? +2$: bhi clgl1 ;Not RAD50 char - exit + sub r1,r0 +3$: sub r1,r0 + br mul50 ;Add new char to word in R4 + + +;Search for the char in R0 in the list at R1. If not there, see if octal digit +;Returns carry=0 -> not digit, char at -1(r1) +; carry=1 -> digit, value in r0 +;No return if not found & not digit + +chklst: cmpb r0,(r1)+ ;This character ? + beq 1$ ;Yes, return + tstb (r1) ;No, more to check ? + bne chklst ;Loop if so + sub #'8,r0 ;Char not in table. Octal digit ? + add #8.,r0 + bcc err0 ;Not digit, give error message +1$: rts pc + + +; Process R and ! - relocation commands + +rel: mov cad,r0 ;'R' command - possible data to relocate + tstb semic ;Are we setting or relocating ? + beq rel1 ;Branch if relocating + mov #reloc,r0 ;Get a pointer to the relocation regs + dec r3 ;Setting or removing ? + bpl 1$ ;Branch if setting + mov r3,r5 ;Removing - force value of -1 + tst r2 ;Reg number given ? + beq 2$ ;No - remove all +1$: dec r2 ;Legal relocation reg ? + bgt err ;No - multi digit number ! + asl r4 ;Ok - make word offset + mov r5,reloc(r4) ;Set value in table + br dcd + +2$: mov r5,(r0)+ ;Remove all relocation constants + cmp r0,#reloc+16. + blo 2$ + br dcd + +reladr: mov #cad,r0 ;'!' command - get address to relocate +rel1: cmp bw,#2 ;Ensure a word is open + bne err0 + mov r5,-(sp) ;Fake return address part of a JSR R5, + mov #dcd2,r5 ; which will return to DCD2 + asl r4 ;Make address of wanted relocation + add #reloc,r4 + mov (r0),-(sp) ;Yes - fake first part of CADA + jsr r5,eqtype ;Type an '=' + dec r2 ;Was specific register requested ? + bpl 2$ + jmp cada1 ;No - let CADA choose one, returns to dcd2 + +2$: bne err0 ;Yes - check only 1 digit + mov r4,r0 + jmp cadain ;Output relocated address, return to dcd2 + + +clgl1: tst (sp)+ + br clgl + + +; Process X - RAD50 + +rad50: jsr r5,tstwrd ;Check word mode, put address in r2 + jsr r5,eqtype ;Is word mode. Type the '=' + mov (r2),r4 ;Get the contents of that location + mov #4,r3 ;Set up the character counter +1$: jsr pc,chr50o ;Print a RAD50 character + sob r3,1$ ;4 of 'em. The 4th will be a space + clr r2 ;Reset r2 to count input characters +2$: jsr pc,chr50i ;Get a char, & convert to RAD50 + cmp r2,#3 ;Loop 'till 3 characters packed + blo 2$ ;Leave r2 with number exists indicator + jsr r5,type ;Got all 3, delimit with a space + .asciz ' ' + br scan + +; Special name handler +; Depends upon the explicit order of the two tables TL and UR0 + +regt: jsr r5,get ;Special name, get one more character + mov #tl,r1 ;Table start address + jsr pc,chklst ;See if it's a known character + mov r0,r4 ;Move digit value (if there) + bcs 1$ ;Branch if octal digit + sub #tl-7,r1 ;Letter - make small integer + mov r1,r4 ;In R4 +1$: inc r2 ;Indicate char found + asl r4 ;Make into word offset + add #ur0,r4 ;Form address of wanted item + cmp r4,#reloc ;Is it a breakpoint reg ? + blos 2$ + mov #adr1,r4 ;Yes - correct address +2$: jsr r5,type ;Give 'open' character + .asciz '/' + br wrd ;Then go & open it + + +tcls: jsr pc,clse ;Close current cell +tstwrd: cmp #2,bw ;Only word mode allowed + bne err0 ;Branch if error + mov cad,r2 ;Current address in R2 + rts r5 + +err0: br err ;Show the error + + +; Process S - single instruction mode + +sngl: movb r2,s ;Set the flag as requested + br dcd + + +; @ Handler - open absolute location + +orab: jsr r5,tcls ;Test word mode and close + mov (r2),r2 ;Get absolute address + br pcs + +; > Handler - follow relative branch (NOT SOB !) + +orrb: jsr r5,tcls ;Test and close + movb (r2),r1 ;Compute new address + inc r1 + asl r1 ;R2=2(@R2+1) + add r1,r2 ; +PC + br pcs + +; _ Handler - open indexed on the PC + +orpc: jsr r5,tcls ;Test word mode and close + add (r2)+,r2 ;Compute +pcs: mov cad,dot ;Save current position + mov r2,cad ;Update CAD + br op2 ;Go finish up + .sbttl Command decoder. +; All registers may be used (R0-R5), + +err: jsr r5,type ; ? to be typed + .asciz '?' +dcd: clr bw ;Close all + jsr r5,crtype ;Type * + .asciz '*' +dcd2: clr semic ;Clear semi-colon flag & SEQ + clr r3 ;R3 is a save register for R2 + clr r5 ;R5 is a save register for R4 +dcd1: clr offset ;Clear out relocation offset +dcdr: clrb negate ;No '-' sign received yet + clr r4 ; R4 contains the converted octal + clr r2 ; R2 is the digit count/number found flag +scan: mov #ur0,sp ;Reset stack to be safe + jsr r5,get ;Get a char, return in R0 +clgl: cmpb r0,#'- ;Check for negative number + bne 1$ ;Not - check normal things + tst r2 ;'-' must preceed all digits + bne err ;It doesn't... so complain + incb negate ;Negative - flag it + br scan ;Then get the next char +1$: mov #lgch,r1 ;Point at command char table + jsr pc,chklst ;Lookup char in list + bcc 2$ ;Letter or octal digit ? + asl r4 ;Digit - build number + jsr pc,mulin + br scan ;Loop for next char + +2$: asrb negate ;Letter. Was there a '-' sign ? + bcc 3$ ;Skip this if not + neg r4 ;'-' - negate value +3$: add offset,r4 ;Add in relocation offset + asl r1 ;Multiply by two + jmp @lgdr-2-<2*lgch>(r1) ;Go to proper routine +; Registers on dispatch: +; R0 - Character code of command R3 - Digits in arg before ';' +; R1 - Offset into dispatch table R4 - Value of arg after ';' +; R2 - Digits in arg after ';' R5 - Value of arg before ';' + +; Comma processor +comma: sob r2,err ;Error if not single digit relocation reg + sub offset,r4 ;In case twit relocated it + asl r4 ;Make into word offset + mov reloc(r4),r2 ;Extract relocation constant + mov r2,offset ;Store it + inc r2 ;Was it legal ? + beq dcd1 ;No - zap it + br dcdr ;Yes - keep it + +; Semi-colon processor +semi: mov r2,r3 ;A semi-colon has been received + mov r4,r5 ;Numeric flag to R3, contents to R5 + incb semic ;Set semi-colon flag + br dcd1 ;Go back for more + .sbttl Command processors. +; Process C - constant reg. + +const: tst r3 ;Setting or using ? + beq 1$ + mov r5,creg ;Setting + jsr r5,eqtype ;Say "=nnnnnn" to show the value + mov #2,bw ;Say this is word mode + mov r5,r0 ;Get the word to print + jsr r5,cadv ;Print the word in r0 + br dcd + +1$: mov creg,r4 ;Using - get value + bisb (pc),r2 ;Say more than 1 digit there + br scan + +; Process / and \ - open word or byte + +wrd: mov #2,bw ;Open word + br wb1 +byt1: rol r4 ;Get the address back +byt: mov #1,bw ;Open byte +wb1: tst r2 ;Get value if R2 is non-zero + beq wrd1 ;Skip otherwise + mov r4,cad ;Put value in CAD +wrd1: mov cad,r4 ;Get current address + cmp #1,bw ;Check byte mode + beq 2$ ;Jump if byte + asr r4 ;Move one bit to carry + bcs byt1 ;Jump if odd address + mov @cad,r0 ;Get contents of word + br 3$ +2$: movb (r4),r0 ;Get contents of byte +3$: jsr r5,cadv ;Go get and type out @CAD + br dcd2 ;Go back to decoder + +; Process carriage return + +cret: jsr pc,clse ;Close location +dcda: br dcd ;Return to decoder + +; Process , open next word + +old: incb seq ;Set need DOT to CAD move +op1: mov bw,r0 ; received +err2: beq err ;Error if nothing is open + jsr pc,clse ;Close present cell + tstb seq ;See if < command + beq 1$ ;Branch if not + mov dot,cad ;Go to the former stream +1$: add r0,cad ;Generate new address +op2: jsr r5,crlf ; + mov bw,-(sp) ;Save bw + mov #2,bw ;Set to type full word address + mov cad,r0 ;Number to type + jsr r5,cada ;Type out address + mov (sp),bw ;Restore bw + mov #"\/,r0 ;Things to type + dec (sp)+ ;Is it byte mode? + beq 1$ ;Jump if yes + swab r0 ;Type a / +1$: jsr r5,ftyp ;Or a \ + br wrd1 ;Go process it + +; Process ^, open previous word + +back: mov bw,r0 ; ^ received + beq err2 ;Error if nothing open + jsr pc,clse + sub r0,cad ;Generate new address + br op2 ;Go do the rest + +; B Handler - set and remove breakpoints + +bkpt: mov #breakc,r0 + asl r4 ;Multiply number by two + tst r3 + beq remb ;If R3 is zero go remove breakpoint + asr r5 ;Get one bit to carry + bcs err1 ;Badness if odd address + asl r5 ;Restore one bit + add #adr1,r4 + dec r2 + beq set1 ;Jump if specific cell + bpl err1 ;Too many digits +set: cmp r0,(r4) ;Is this cell free? + beq set1 ;Jump if yes + cmp r4,#bkp+adr1 ;Are we at the end of our rope + bhis err1 ;Yes, there is nothing free + tst (r4)+ ;Increment by two + br set +set1: mov r5,(r4) ;Set breakpoint + br dcda ;Return +remb: dec r2 ;How many reg # digits ? + bmi rall ;None - remove all + bne err1 ;More than 1 - error + mov r0,adr1(r4) ;Clear breakpoint + clr ct(r4) ;Clear count also + br dcda +rall: clr r4 +1$: mov #breakc,adr1(r4) ;Reset bkpt + mov #break,uin(r4) ;Reset contents of table + clr ct(r4) ;Clear count + tst (r4)+ ;Increment by two + cmp r4,#bkp+2 ;All done? + blos 1$ ;Loop if not +dcdb: br dcda + +; Process O, compute offset + +ofst: cmp #2,bw ;Check word mode + bne err1 ;Error if not correct mode + jsr r5,type ;Type one blank as a separator + .asciz ' ' + tst r3 ;Was semi-colon typed? + beq err1 ;No, call it an error + sub cad,r5 ;Compute + dec r5 + dec r5 ; 16 bit offset + mov r5,r0 + jsr r5,cadv ;Number in R0 - word mode + mov r5,r0 + asr r0 ;Divide by two + bcs 1$ ;Error if odd + cmp #-200,r0 ;Compare with -200 + bgt 1$ ;Do not type if out of range + cmp #177,r0 ;Compare with +177 + blt 1$ ;Do not type if out of range + dec bw ;Set temporary byte mode + jsr r5,cadv ;Number in R0 - byte mode + inc bw ;Restore word mode +1$: jmp dcd2 ;All done + +; Common routine for ;F and ;I, word and byte area fill + +fill: mov #msk+2,r0 ;Address of parameters + mov (r0)+,r1 ;Start of block to fill + mov (r0)+,r2 ;End address + mov (r0),r0 ;Value to fill with +fillit: cmp r1,r2 ;Any more to fill ? + bhi dcdb ;No, all done + jmp (r5) ;Yes, move the correct data size + +; Searches - $MSK has the mask +; $MSK+2 has the FWA +; $MSK+4 has the LWA +;Registers: +; R0 - Contents of location R3 - Effective address referenced +; R1 - Word/addr mode flag R4 - Search mask +; R2 - Address of location R5 - Object to search for + +eff: inc r1 ;Set effective search + br wds + +wsch: clr r1 ;Set word search +wds: tst r3 ;Check for object found + beq err1 ;Error if no object + mov #2,bw ;Set word mode + mov msk+2,r2 ;Set origin + mov msk,r4 ;Set mask + com r4 +wds2: cmp r2,msk+4 ;Is the search all done? + bhi dcdb ; yes + tstb @#rcsr ;User hitting the keyboard ? + bmi dcdb ;Yes, stop search + mov #"/@,-(sp) ;Possible address/contents seperators + mov (r2),r0 ;Get object + tst r1 ;Which search mode ? + bne eff1 ;Branch if effective search + bic r4,r0 ;Apply mask to data + bic r4,r5 ; and test word + cmp r0,r5 ;Now compare the two +wds3: bne wds4 ;Re-loop if no match + mov r4,-(sp) ;Registers R2,R4, and R5 are safe + jsr r5,crlf + mov r2,r0 ;Get ready to type + jsr r5,cada ; type address + mov 2(sp),r0 ;Get seperator + jsr r5,ftyp ; type it + mov (r2),r0 ;Get contents + jsr r5,cadv ;Type contents + mov (sp)+,r4 ; restore R4 +wds4: cmp (r2)+,(sp)+ ;Increment to next cell, drop seperator + br wds2 ; and return + +err1: jmp err ;Intermediate help + +; Process ;I, byte area fill + +bfill: jsr r5,fill ;Do all the common stuff + movb r0,(r1)+ ;Fill in another byte + br fillit ;The rest is common too + +.if ndf,test +.iif gt,.-172000 .error .-172000 Restart vectors overwritten + . = 172000 +power: jmp odt +reset: br break +.endc + +; Process ;F, word area fill + +wfill: jsr r5,fill ;Do all the common stuff + mov r0,(r1)+ ;Fill in another word + br fillit ;The rest is common too + +; More of the search commands... effective address search part + +eff1: swab (sp) ;Set '@' as seperator + cmp r0,r5 ; Is (X)=K? + beq wds3 ;Type if equal + mov #"_>,(sp) ;Change seperators + mov r0,r3 ;(X) to R3 + inc r3 + inc r3 ;(X)+2 + add r2,r3 ;(X)+X+2 + cmp r3,r5 ;Is (X)+X+2=K? + beq wds3 ;Branch if equal + swab (sp) ;Make '>' seperator + movb r0,r0 ;Sign extend the offset + inc r0 + asl r0 ;Multiply by two + add r2,r0 ;Add PC + cmp r0,r5 ;Is the result a proper rel. branch? + br wds3 + +; Process G - go + +go: tst r3 ;Was K; typed? + beq err1 ;Type ? if not + movb #bkp+3,p ;Clear proceed + asr r5 ;Check low order bit + bcs err1 ;Error if odd number + asl r5 ;Restore word + mov r5,upc ;Set up new PC + jsr r5,rstt ;Set high priority & restore teletype +tbit: clrb t ;Clear T-flag + tstb s ;See if we need a T bit + bne go2 ;If so go now + mov #bkp,r4 ;Restore all breakpoints (0-7) +1$: mov @adr1(r4),uin(r4) ;Save contents +.iif ne,o.break mov #o.break,@adr1(r4) ;Replace with 'BPT' trap +.iif eq,o.break clr @adr1(r4) ;Replace with 'HALT' trap + dec r4 + dec r4 + bge 1$ ;Loop until done +go2: mov (sp)+,r0 ;Restore registers R0-R6 + mov (sp)+,r1 + mov (sp)+,r2 + mov (sp)+,r3 + mov (sp)+,r4 + mov (sp)+,r5 + mov (sp),sp ;Restore user stack + mov ust,-(sp) ; and status + + bic #tbt,(sp) ;Clear the T bit + mtps (sp) ;Enable user priority interrupts + tst s.t ;Either S or T flags set ? + beq 1$ ;No, carry on + bis #tbt,(sp) ;Yes, set the T bit +;Even on a T-11 we must grab the BPT trap vector when the T bit is set + mov #break,@#tvec ;Force correct break vector PC + mov #stm,@#tvec+2 ;Status word to break vector+2 + +1$: mov upc,-(sp) ;Restore users PC + rtt ;Then do some of the users prog. + +; Process P - proceed; Only allowed after a breakpoint + +proc: movb p,r0 ;Check legality of proceed + blt err1 ;Not legal + tst r2 ;Check for illegal count + bne err1 ;Jump if illegal + tst r3 ;Was count specified? + beq pr1 ;No + mov r5,ct(r0) ;Yes, put away count +pr1: jsr r5,rstt ;Force high priority & restore tty +c1: cmpb p,#bkp ;See if a real breakpoint or a fake + bhi tbit ;Branch if fake + tstb s ;See if single instruction mode + bne tbit ;If so exit now + incb t ;Set T-bit flag + br go2 + .sbttl Breakpoint trap handler & I/O routines. + +; Breakpoint handler + +break: mov (sp)+,upc ;Priority is 7 upon entry + mov (sp)+,ust ;Save status and PC + movb #bkp+3,p ;Tell ;P that we can continue + +; Save registers R0-R6; internal stack + + mov sp,usp ;Save user stack address + mov #usp,sp ;Set to internal stack + mov r5,-(sp) ;Save registers + mov r4,-(sp) ;0 + mov r3,-(sp) ; + mov r2,-(sp) ; thru + mov r1,-(sp) ; + mov r0,-(sp) ; 5 + + movb t,r4 ;Check for T-bit set + beq 1$ ;Jump if not set + decb r4 ;Check prog. didn't mangle it + beq tbit ;OK - good T bit trap + jsr r5,crtype ;Tell user all is corrupt + .asciz '#' + +; Remove breakpoints 0-7, in the opposite order of setting + +1$: tstb s ;See if single instruction is going + bne 3$ ;Skip if so + clr r4 ;Remove all breakpoints +2$: mov uin(r4),@adr1(r4) ;Clear breakpoint + tst (r4)+ + cmp r4,#bkp + blos 2$ ;Re-loop until done +3$: mov upc,r5 ;Get PC, it points to the BREAK + tstb s ;See if it was single instruction fun + bne 5$ ;If so handle there + tst -(r5) + mov r5,upc + mov #bkp,r4 ;Get a counter +4$: cmp r5,adr1(r4) ;Compare with list + beq 6$ ;Jump if found + dec r4 + dec r4 + bge 4$ ;Re-loop until found + jsr r5,svttyp ;Lower priority & save teletype status + .asciz 'BE ' ;Output "BE " for bad entry + mov r5,r0 + add #2,upc ;Pop over the adjustment above + br 7$ ; or continue +5$: movb #bkp+2,r4 ;Set break point high + 1 + mov r5,adr1(r4) ;Store next PC value for type out +6$: movb r4,p ;Allow proceed + dec ct(r4) + bgt c1 ;Jump if repeat + mov #1,ct(r4) ;Reset count to 1 + jsr r5,svttyp ;Lower priority & save tty status; R4 is safe + .asciz 'B' ;Type "B" + movb p,r0 ;Convert breakpoint number to ascii + asr r0 + add #"0;,R0 ;Type number; + jsr r5,typ2 + movb p,r4 + mov adr1(r4),r0 ;Get address of break +7$: mov #2,bw ;Set word mode + jsr r5,cada ;Type address + jmp dcd ;Go to decoder + +; Type out address in r0, relocating as required. + +cada: tst format ;Is relocation allowed ? + bne cadv ;Skip this if not + mov r0,-(sp) ;Save address to be output +cada1: mov #reloc,r4 ;Point at relocation regs + mov #zero,r0 ;Start assuming no relocation +1$: cmp (sp),(r4) ;This relocation possible ? + blo 2$ ;Skip if too big + cmp (r4),(r0) ;Possible - better than current ? + blos 2$ ;Skip if not + mov r4,r0 ;Better - save it +2$: tst (r4)+ ;Advance to next value + cmp r4,#reloc+16. ;If there is one + blo 1$ ;Loop 'till all values tried +cadain: sub (r0),(sp) ;Now relocate address + sub #reloc,r0 ;Which relocation reg ? + blo 1$ ;None - don't print one + asr r0 ;Convert it to digit + add #"0,,r0 + jsr r5,typ2 ;Type it & a comma +1$: mov (sp)+,r0 ;Get offset, fall through & type + +; Type out contents of word or byte with one trailing space. Word is in R0 + +cadv: mov #6,r3 ;# of digits + mov #-2,r4 ;# of bits first-3 + cmp #1,bw ;See if word mode + bne spc ;Branch if so + movb r0,-(sp) ;Save byte for character output + asr r3 ;Only do 3 digits + inc r4 ;Do 2 bits first + swab r0 ;And turn R0 around + jsr r5,spc ;Output in octal + jsr r5,eqtype ;Then as = + bic #177600,(sp) ;Strip parity bit + cmpb (sp),#40 ;Control char ? + bhis 1$ ;Branch if not + mov #'?,(sp) ;Yes - unprintable +1$: mov #20000,r0 ;Get a trailing space + bis (sp)+,r0 ;And char + br typ2 ;Type it & return + +spc: mov r0,-(sp) ;Save R0 +1$: add #3,r4 ;Compute the number of bits to do + clr r0 +2$: rol (sp) ;Get a bit + rol r0 ;Store it away + sob r4,2$ ;Loop once for each bit + add #"0 ,r0 ;Convert to ascii, put space in high byte + jsr r5,ftyp ;Type digit + sob r3,1$ ;Loop if more digits + tst (sp)+ ;Get rid of junk +styp: swab r0 ;Setup trailing space & fall thru FTYP + +; Type only one character (contained in R0) + +ftyp: tstb @#tcsr + bpl .-4 + movb r0,@#tdb +typ1: rts r5 + +crlf: mov #5015,r0 ; + jsr r5,typ2 + clr r0 ;Fill with 2 nulls + +typ2: jsr r5,ftyp + br styp + +eqtype: mov #'=,r0 ;Type an '=' sign + br ftyp + +; General character input routine -- ODT11S +; Character input goes to R0 + +ttget: tstb @#rcsr + bpl ttget + movb @#rdb,r0 + rts pc + +get: jsr pc,ttget ;Get character + inc r0 ;Make give an illegal code + bic #177600,r0 ;Strip off parity from character + dec r0 ;Restore all chars except + beq get ;Ignore nulls + cmpb r0,#lf ;See if a + beq 3$ ;If so save the paper + cmpb r0,#140 ;See if upper case + blos 2$ ;OK if so + sub #40,r0 ;Fold lower case to upper +2$: jsr r5,ftyp ;Echo character + cmpb #40,r0 ;Check for spaces + beq get ;Ignore spaces +3$: rts r5 + +; Lower priority, save teletype status & print a message + +svttyp: movb pri,r0 ;Check if priority + bpl 1$ ; is as same as user pgm + movb ust,r0 ;Pick up user ust if so + br 2$ +1$: asrb r0 ;Shift low order bits of actual priority + rorb r0 ; into + rorb r0 ; high order + rorb r0 ; position +2$: bic #tbt,r0 ;Clear "T" bit + mtps r0 ;Put the status away where it belongs +; + movb @#rcsr,csr1 ;Save r c/sr + movb @#tcsr,csr2 ;Save t c/sr + clrb @#rcsr ;Clear enable and maintenance + clrb @#tcsr ; bits in both c/sr + +; General string/character output routine + +crtype: jsr r5,crlf ;Preceed with +type: movb (r5)+,r0 ;Get a char + beq typ1 ;Exit when done + jsr r5,ftyp ;Type one character + br type ;Loop until done + +; Set high priority & restore teletype status + +rstt: mtps #stm ;Set high priority + jsr r5,crlf ;Take a new line + tstb @#tcsr ;Wait ready + bpl .-4 ; on printer + bit #4000,@#rcsr ;Check busy flag + beq 1$ ;Skip ready loop if not busy + tstb @#rcsr ;Wait ready + bpl .-4 ; on reader +1$: movb csr1,@#rcsr ;Restore + movb csr2,@#tcsr ; the status registers + rts r5 + +; Close word or byte and exit, +; Upon entering, R2 has numeric flag, R4 has contents + +clse: tst r2 ;If no number was typed there is + beq 2$ ;No change to the open cell + cmp #1,bw + beq 1$ ;Jump if byte mode + bhi 2$ ;Jump if already closed + mov r4,@cad ;Store word + br 2$ +1$: movb r4,@cad ;Store byte +2$: rts pc + .sbttl Cassette load & dump routines. + +;Process L - load a program from tape + +;Input format: + +;Frame +; 1 001 +; 2 000 +; 3 Low order byte count (Includes all except checksum, +; 4 High order byte count even the 001 000.) +; 5 Low order load address +; 6 High order load address +; 7.. Data bytes +; XXX Checksum (Includes all the block - even the 001) +; +;The checksum is calculated such that when all the bytes have +;been added up, the low byte of the sum will be zero. +; +;If the byte count is 6, the load address specified will be +;taken to be the start address of the program. If the address +;is even the program will be started, otherwise ODT will be +;re-entered. If (count > 6), the data block will be loaded. +; +;This is the format used by DEC for paper tapes, and can be +;produced using the RT-11 LINK/LDA command. + +;Register use: +; R0 - contents of byte R3 - checksum +; R1 - load address R4 - Word from L.GWRD; start address +; R2 - byte count R5 - read subr pointer + +load: jsr pc,ttget ;Wait for dummy char before starting + mov r3,-(sp) ;See if any relocation wanted + beq 1$ ;Branch if not + mov r5,(sp) ;Wanted - get relocation address + bne 1$ ;If given + mov l.addr,(sp) ;None given - place after last tape +1$: mov #l.read,r5 ;Setup read routine address + clr r1 ;Clear out load address + +;Look for a block start + +l.ld2: clr r3 ;Initialise ckecksum + jsr pc,(r5) ;Read a frame + sob r0,l.ld2 ;Loop 'till byte=1 (block start) + jsr pc,(r5) ;Found '1' - skip next byte + +;Get byte count & address, relocate address, if end goto L.JMP + + jsr pc,l.gwrd ;Get byte count word + mov r4,r2 ;Save it + sub #4,r2 ;Correct it after reading 001,000,count + jsr pc,l.gwrd ;Get load address + add (sp),r4 ;Plus relocation factor + tst r2 ;Is it an end block ? + beq l.jmp ;Branch if so + mov r4,r1 ;Keep as load address + +;Read in the remainder of the data block + +l.ld3: jsr pc,(r5) ;Read a frame + bge l.ld4 ;Branch if more data there + tstb r3 ;Look at checksum + beq l.ld2 ;Good sum - find next block +l.bad: o.break ;Checksum error + br l.ld2 + +l.ld4: movb r0,(r1)+ ;Put byte in memory + br l.ld3 + +;Input a frame, decrement byte count, accumulate checksum + +l.read: bis #1,@#rcsr ;Set cassette & reader run + jsr pc,ttget ;Then wait for char + bic #177400,r0 ;Strip off silly bits + add r0,r3 ;Accumulate checksum + dec r2 ;Update byte count + rts pc + +;Assemble a full word of data + +l.gwrd: jsr pc,(r5) ;Get low byte + mov r0,r4 ;Save it + jsr pc,(r5) ;Then high byte + swab r0 ; in high bits + bis r0,r4 ;Assemble full word + rts pc + +l.jmp: inc r2 ;;Get the constant 1 + inc r1 ;;Round up load address +;; cmpb (r1)+,(r2)+ ;r2=1, increment r1 + bic r2,r1 + mov r1,l.addr ;Save next free memory address + jsr pc,(r5) ;Read the checksum + tstb r3 ;Good checksum ? + bne l.bad ;Branch if error + bic r2,r4 ;Round down to word boundry + mov r4,upc ;Save start as users PC + movb #bkp+3,p ;Allow proceed from here + +dcdc: jmp dcd + +; Process D - dump memory out to tape in a format compatible with +; the L command above. + +;Register use: +;R0 - Word / byte to punch R3 - Address of D.PW routine +;R1 - Checksum R4 - 1st address beyond block to punch +;R2 - Spare R5 - Address of data to punch + +dump: jsr pc,ttget ;Wait for dummy char + mov #d.pw,r3 ;Load address of word read routine + clr r1 ;Clear out checksum word + clr r0 ;Form header word of '1' + inc r0 + jsr r5,(r3) ;Then punch it + mov r4,r0 ;Move to o/p reg + add #6,r0 ;Account for header words + sub r5,r0 ;Make # bytes to punch + jsr r5,(r3) ;Output block size + mov r5,r0 ;Then block address + jsr r5,(r3) +2$: cmp r5,r4 ;Any bytes to o/p ? + bhis 3$ ;Go for checksum if not + movb (r5)+,r0 ;Yes - get byte + jsr r5,d.pb ;And punch it + br 2$ +3$: movb r1,r0 ;Get checksum + jsr r5,(r3) ;Punch it, 3 bytes of rubbish & return + jsr r5,(r3) + br dcdc + +d.pw: jsr r5,d.pb ;Punch low byte + swab r0 ;Get high one, fall thru & punch + +d.pb: sub r0,r1 ;Accumulate checksum + jmp ftyp ;Output byte & return + .sbttl Tables. + +lgdr: op1 ; modify, close, open next + cret ; close + reladr ; ! relocate address + regt ; $ register ops + comma ; , relocate data + wrd ; / open word + semi ; ; introduce second argument + old ; < return to old sequence and open + orrb ; > open related, rel. branch + orab ; @ open related, absolute + bkpt ; B breakpoints + const ; C constant reg access + dump ; D dump to tape + eff ; E search effective address + wfill ; F fill a block of words + go ; G go to address k + bfill ; I fill a block of bytes + load ; L load tape + ofst ; O offset + proc ; P proceed + rel ; R set relocation constant + sngl ; S single instruction mode + wsch ; W search word + rad50 ; X radix 50 + byt ; \ open byte + back ; ^ open previous + orpc ; _ open related, index - PC + +tl: .byte 'S ;Do 1 ; User status reg. + .byte 'P ;not 2 ; Priority for ODT + .byte 'M ; 3 ; Search mask + .byte 'L ;change 4 ; Lower limit + .byte 'U ; 5 ; Upper limit + .byte 'C ;the 6 ; Constant reg. + .byte 'A ; 7 ; Load address + .byte 'F ;order 8 ; Format reg. + .byte 'R ; 9 ; Relocation regs. + .byte 'B ;here 10 ; Breakpoints + .byte 0 ;List terminator + +lgch: .byte lf, cr, '!, '$, ',, '/, ';, '<, '> + .byte '@, 'B, 'C, 'D, 'E, 'F, 'G, 'I, 'L + .byte 'O, 'P, 'R, 'S, 'W, 'X, '\, '^, '_ +.iif ne, <.-tl&1> .byte 0 ;List terminator. Use ZERO below if poss. +zero: +.iif ne,o.break .word 0 ;If HALT (=0) trap in use, use it as the zero. +breakc: o.break ;Trace trap prototype. HALTs get used as ZERO +; +.if ndf,test +;.iif gt,.-174000 .error .-174000 ODT too big to fit in ROM +.endc + .sbttl Workspace. + +.if df,test + .even + .blkb 120 ;ODT's stack immediately follows ODT +.iff + .=174000+210 +.endc + +;The order of the following entries is critical + + +ur0: .blkw 6 ;User R0-R5 +usp: .blkw 1 ;User SP +upc: .blkw 1 ;User PC +ust: .blkw 1 ;User ST +pri: .blkw 1 ;ODT priority (7) +msk: .blkw 1 ;Mask + .blkw 1 ;Low limit + .blkw 1 ;High limit +creg: .blkw 1 ;Constant reg +l.addr: .blkw 1 ;Next free load address +format: .blkw 1 ;Format reg +reloc: .blkw 8. ;Relocation regs + +; Break point lists, adr1 = address of breakpoint, ct = count, +; uin = contents + +adr1: .blkb bkp+4 +ct: .blkb bkp+4 +uin: .blkb bkp+4 + + +offset: .blkw 1 ;Relocation offset +bw: .blkw 1 ; =0 - all closed, + ; =1 - byte open, + ; =2 - word open +cad: .blkw 1 ; Current address +dot: .blkw 1 ; Origin address +semic: .blkb 1 ;Semi-colon flag +seq: .blkb 1 ;Flag for < command +s.t: .blkw 1 ;s & t together as a single 16 bit word + s = s.t ;Single instruction flag, non 0 if active, 0 if not. + ;No breakpoints may be set in single instruction mode. + t = s.t + 1 ;T-bit flag, doing the instruction under a breakpoint +p: .blkb 1 ;Proceed flag = -2 if manual entry + ; -1 if no proceed allowed + ; 0-7 if proceed allowed +negate: .blkb 1 ;Negate pending flag for numeric input +;NB. csr1 and csr2 get cleared together by a word clear - do not split. +csr1: .blkb 1 ;Save cell - r c/sr +csr2: .blkb 1 ;Save cell - t c/sr + + .end ; odt \ No newline at end of file diff --git a/pdp11-45.html b/pdp11-45.html new file mode 100755 index 0000000..3f2397e --- /dev/null +++ b/pdp11-45.html @@ -0,0 +1,1502 @@ + + + + + Javascript PDP 11/45 Emulator v1.8 + + + + + + + + + + +
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
ADDRESS  
+
+
+
+
+
+
+
+
+
+
+
+ + +
+
+
+
+
+
USER I
+
+
SUPER I
+
+
KERNEL I
+
+
PROG PHY
+
+
+
+
USER D
+
+
SUPER D
+
+
KERNEL D
+
+
CONS PHY
+
+
+ +
+
+
+
+
+ + +
+
+
+
+
+
DATA PATHS
+
+
U ADRS   FPP/CPU  
+
+
+
+
BUS REGISTER
+
+
DISPLAY REGISTER
+
+
+
+
+
+
+
+ +
+
POWER
+
LOCK
+
+
+
+
+
OFF
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
 
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
DATA
+
+
+
+
+
+
+
+
+
+
+ + +
+ pdp11-45 +
+ +
+
ADRS ERR +
+
+
RUN +
+
+
PAUSE +
+
+
MASTER +
+
+
USER +
+
+
SUPER +
+
+
KERNEL +
+
+
DATA +
+
+
+ + +
+ +
+
+
17
+
16
+
15
+
+
+
14
+
13
+
12
+
+
+
11
+
10
+
9
+
+
+
8
+
7
+
6
+
+
+
5
+
4
+
3
+
+
+
2
+
1
+
0
+
+
+

LOAD
ADRS
+
EXAM
+
CONT
+

ENABLE
HALT
+

S INST
S BUS
+
START
+
DEP
+

REG
EXAM
+

REG
DEP
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+ +
+ + + + + + + + + + + + DEL to Ctrl/H + +
+
+
+ + + +

PDP 11/45 Emulator v1.8   October 2017

+

+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

+ +

List of guest OS's:

+ + + + + + + + + + + + + + + +
DiskOSComment
RK0Unix V5 Boot using: unix then login as root
RK1RT11 v4.0 The lightest/fastest OS here
RK2RSTS V06C-03 Boot and login as 1,2 with password SYSTEM or as 11,70 using PDP
RK3XXDP Diagnostic OS and utilities
RK4RT-11 3B Distribution for RT-11 Version 3B
RK5RT-11 V5.4F Distribution for RT-11 Version 5.4F
RL0BSD 2.9 Boot using: rl(0,0)rlunix   CTRL/D to get to multiuser
RL1RSX 11M v3.2 Fails on a PDP 11/45
RL2RSTS/E v7.0 Option: <LF> Suboption: <LF> ... Login as 1,2 using SYSTEM or 11,70 using PDP
RL3XXDP Larger version of diagnostics - including PDP 11/70 utilities
RP0ULTRIX-11 V3.1 CTRL/D to enter multiuser mode. Login as root with no password
RP1BSD 2.11 Fails on a PDP 11/45
RP2RSTS/E v9.6 Answer boot questions and login as 1,2 (password SYSTEM) or 11,70 (no password)
RP3RSX 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 1 +

Youtube video 2 + + +

Example boot of Unix V5

+
+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
+...
+
+ +

Example boot of RT11 v4.0

+
+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
+...
+
+ + +

Example boot of RSTS V06C-03

+
+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
+
+
+

Example boot of XXDP

+
+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
+....
+
+ + +

Example boot of BSD 2.9

+
+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
+...
+
+ + +

Example boot of RSTS/E v7.0

+
+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
+
+ +

Example boot of XXDP

+
+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
+...
+
+ + +

Example boot of ULTRIX-11 System V3.1

+
+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
+#
+
+ +

Example boot of RSX 11M 4.6

+
+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
+
+ +

Example boot of RSTS V9.6

+
+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...
+
+
+
+ + +

Bugs?

+

+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 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + 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 @@ + + + + + Javascript PDP 11/70 Emulator v1.8 + + + + + + + + + + +

+ pdp11-70 +
+
+
+
+ +
+
+
+
+
USER D
+
+
USER I
+
+
+
+
SUPER D
+
+
SUPER I
+
+
+
+
KERNEL D
+
+
KERNEL I
+
+
+
+
CONS PHY
+
+
PROG PHY
+
+
+
+
+
+
+ +
+
+
+
+
DATA PATHS
+
+
U ADDRS
+
+
+
+
BUS REG
+
+
DISPLAY REGISTER
+
+
+
+
+
+
+ +
+
+
PAR
ERR +
+
+
ADRS
ERR +
+
+
+
+
RUN +
+
+
PAUSE +
+
+
MASTER +
+
+
+
+
USER +
+
+
SUPER +
+
+
KERNEL +
+
+
+
+
DATA +
+
+
16 +
+
+
18 +
+
+
22 +
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
ADDRESS  
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
PARITY
+
HIGH +
+
+
LOW +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
DATA  
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
21
+
+
+
20
+
19
+
18
+
+
+
17
+
16
+
15
+
+
+
14
+
13
+
12
+
+
+
11
+
10
+
9
+
+
+
8
+
7
+
6
+
+
+
5
+
4
+
3
+
+
+
2
+
1
+
0
+
+
+

LOAD
ADRS
+
EXAM
+
DEP
+
CONT
+

ENABLE
HALT
+

S INST
S BUS
+
START
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+ +
+ + + + + + + + + + + + DEL to Ctrl/H + +
+
+
+ + +

PDP 11/70 Emulator v1.8   October 2017

+

+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
+
+
+This emulator then loads in the BOOT.MAC code to location 140000 and begins execution there. +

+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

+ +

List of guest OS's:

+ + + + + + + + + + + + + + + + + +
DiskOSComment
RK0Unix V5 Boot using: unix then login as root
RK1RT11 v4.0 The lightest/fastest OS here
RK2RSTS V06C-03 Boot and login as 1,2 with password SYSTEM or as 11,70 using PDP
RK3XXDP Diagnostic OS and utilities
RK4RT-11 3B Distribution for RT-11 Version 3B
RK5RT-11 V5.4F Distribution for RT-11 Version 5.4F
TM0RSTS 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.
RL0BSD 2.9 Boot using: rl(0,0)rlunix   CTRL/D to get to multiuser
RL1RSX 11M v3.2 Login as 1,2 with password SYSTEM
RL2RSTS/E v7.0 Option: <LF> Suboption: <LF> ... Login as 1,2 using SYSTEM or 11,70 using PDP
RL3XXDP Larger version of diagnostics - including PDP 11/70 utilities
RP0ULTRIX-11 V3.1 CTRL/D to enter multiuser mode. Login as root with no password
RP1BSD 2.11 Will autoboot and enter multiuser mode. Login as root with no password
RP2RSTS/E v9.6 Answer boot questions and login as 1,2 (password SYSTEM) or 11,70 (no password)
RP3RSX 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 1 +

Youtube video 2 + + +

Example boot of Unix V5

+
+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
+...
+
+ +

Example boot of RT11 v4.0

+
+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
+...
+
+ +

Example boot of RSTS V06C-03

+
+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
+
+
+ +

Example boot of XXDP

+
+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
+....
+
+ + +

Example boot of RSTS 4B-17

+
+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
+
+ +

Example boot of BSD 2.9

+
+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
+...
+
+ + +

Example boot of RSX 11M v3.2

+
+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
+>
+
+ + +

Example boot of RSTS/E v7.0

+
+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
+
+
+
+ +

Example boot of XXDP

+
+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
+...
+
+
+ + +

Example boot of ULTRIX-11 System V3.1

+
+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#
+
+ +

Example boot of BSD 2.11

+
+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 
+# 
+
+ + + +

Example boot of RSTS V9.6

+
+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
+
+
+ +

Example boot of RSX 11M 4.6

+
+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 
+>
+
+ +

Bugs?

+

+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