What Happened to RT-11 SJ or Where Has TT.SYS Gone? Bob Schor Department of Otolaryngology University of Pittsburgh 200 Lothrop Street Pittsburgh, PA 15213 (412) 647-2116 bschor@vms.cis.pitt.edu Perhaps the most noticable change in RT-11 with the introduction of Version 5.6 and Version 5.7 is the whole-scale renaming of the various monitors. Three new monitors have been added, and one long-time old friend, RT-11 SJ, the Single Job Monitor, has gone away. The purpose of this talk is to explore why it has gone, review the consequences for the user, and try to demonstrate the old saying "When Life Hands You a Lemon, Make Lemonade". First, where has it gone? RT-11 SJ was the "Single Job" monitor in the new 5.6 monitor scheme, it has been replaced by RT-11 SB (there will be more on names in another talk). What were the functionalities of SJ, its pluses and minuses, which have contributed to making it such a favorite of many users in the Real Time and Data Acquisition environment? SJ = Single Job, you were guaranteed to be the only job running, which meant that you never had to worry about some other program (other than the operating system) corrupting yours. You ran in Kernel mode, meaning you had all of the power of the PDP-11 at your disposal  you could even HALT the machine! The monitor was small, and fast, giving you access to a reasonable chunk of the 56kB of memory (plus full access to the 8kB I/O page) in the machine. Even the non-essential parts of the terminal device (the part used to do .READs and .WRITEs) were split out into TT.SYS so that user programs which didn't do "regular" I/O to the terminal would have more space. But there were problems. The major problem involved Completion Routines. There were cases, particularly likely to occur while using VM:, in which the system could get thoroughly hung. The only solution involved bringing in 75-80% of the FB functionality, so the developers decided to put in 100% and solve this and other possible problems in a clean manner. SB is the resulting product. Some of the problems in SJ arose from the schizoid nature of its development. As features and capabilities were designed into the evolving monitor set (first FB, then XM), attempts were made to "build in" as many of these into SJ as sensible, given the different underlying monitor structures (and the fact that SJ really was optimized for small size and quick response, structure-be-damned). The development of the later monitors benefitted from knowledge of SJ development, and did many things better (and perhaps "right"). One particular difference is that information about job context in later monitors is kept in a separate impure area, allowing for controlled access; in SJ, this information tends to be scattered throughout the monitor. One further SJ problem is that interrupts just sort of "happened"  the monitor did not deal with them in as robust a method as with FB/XM. These fundamental monitor differences between SJ, on the one hand, and FB/XM, on the other hand, were reflected in the existence of two monitor sources, RMONSJ and RMONFB; the latter built both FB and XM using a conditional code switch which basically turned on memory management (MMG$T). The solution to the "broken SJ" problem, and the evolutionary pressure which created SB, was to further conditionalize RMON to specify the number of supported jobs; if the answer was 1, you had SB (and, incidently, XB and ZB basically came for free). Because only one job needs to be supported, the structure of the monitor's impure area is particularly simple, and the monitor itself can be simplified (as it doesn't need code to switch context, but can leave its pointer looking at the guaranteed job context). Context-switching mainly goes away. Some size comparisons. The examples below are for a monitor running out of VM, as well as the more conventional configuration running out of DU. RT-11 v5.5 RT-11 v5.6 SJ FB XM SB FB XM VM 128 128 86 128 128 86 RMON 2516 5076 8056 3913 5148 8248 USR (2065) (2065) 2577 (2065) (2065) 2577 BkGd 25772 23212 17697 24375 23140 17505 DU 915 915 144 1000 1000 154 RMON 2516 5076 8056 3913 5148 8248 USR (2065) (2065) 2577 (2065) (20650) 2577 BkGd 24985 22425 17639 23503 22268 17437 The above chart contains the bad news; the smallest monitor, RT-11SB, has grown in size by about 1400 words (the other monitors have also grown slightly, but nowhere nearly as much). It is therefore possible that code that used to run under RT-11SJ will not run (immediately) under SB because of lack of memory. This brings up the entire issue of code migration. There are two sorts of migration issues to consider. First, aside from size and speed, are there any other ways that SB differs from SJ, and could thus cause migration headaches? Second, what if size or speed really is an issue? The good news is that almost all programs which ran under SJ should run under SB (assuming the code still fits in memory). Also, all programs which ran under FB should definitely run under SB! One subtle change in SB lies in $CNFG1; the low bit, BMON$, used to distinguish between SJ and FB. Under 5.6, this defaults to 1, i.e. "FB". Should this matter to your program, the developers have handily provided a SET MODE SJ switch to change this bit back to 0. The "correct" way to distinguish SB from FB is to look at $JOBS and see if it is 1 or a larger number. However, if your program actually went in and played around with internal monitor tables, particularly if it didn't use the RMON offsets to do so, it is likely that it may fail under SB, with its reorganized (and rationalized) structure. The general goal of the entire 5.6 development effort was portability of old software without modification. "Vanilla" programs, those which handle interrupts and mapping through system calls, rather than directly, and which do i/o through the monitor, should immediately run. It is probably true (but we have no direct data) that programs which attempt to push the CPU to its limit in terms of speed while processing interrupts will probably run somewhat slower under SB; there is more overhead associated with the (correct, safe) processing of interrupts. How about speed? This is harder. If this is really an issue, there are some things to try. One is to use VM: as a system device, or for i/o (depends on where the bottleneck lies). Another is to buy a faster processor! How about space? Assuming it doesn't fit under SB, try it under XB. While this is an even larger (and somewhat slower) monitor, it allows you to use VBGEXE to create a virtual environment where you have direct access to 56kB (and, in some cases, 64kB) of memory. The reason for this gain in memory is because all of the monitor stuff now runs in Kernel mode, and your program moves to a separate User Space. VBGEXE/XM support three memory models. [Those familiar with TSX+ will recognize some of this]. One is 56kB of User space + direct access to the I/O page. Another is 56kB + a copy of RMON tables in the top 8kB, allowing very rapid access through the $SYPTR at location 54. The third is to simply have 64kB of User space directly. Note that in any of these environments, you've got more space available than under any unmapped monitor, even SJ. What are the drawbacks? One may be speed -- if you are right at the edge, going from SB to XB might push you over. See "speed" discussion above. How about problems in your program? The big issue is interrupts. If your program deals directly with interrupts, that is, if it contains an Interrupt Service Routine, you have to do a bit of work to run in a virtual environment. The first problem is fielding the interrupt. When an interrupt occurs, you vector to a location in low memory in Kernel Space, regardless of the mode you were in when the interrupt occurs. So problem #1 is getting vectors into Kernel Memory. .PROTECT will correctly give you the status of the Kernel vector and will protect the vector; you could then use .PEEK/.POKE to load it and point it to your ISR. The next question is in which mode do you wish to execute the interrupt code, the choices being Kernel Mode (i.e. not in your program's address space) or User Mode ( i.e. code within your program). It might sound like User Mode is the logical choice for a User ISR, but there is a problem which boils down to a bit of hardware lore. Recall that when executing an RTI to return from interrupt, the PS, used to send you back to the previous mode, is updated from the value on the stack. If you check the description of RTI, you will notice that if you are in User Mode, you can only return to User mode; Supervisor gets you back to Supervisor or User; Kernel gets you back to anywhere. The problem now is, in which mode does your interrupt code execute? You control User Mode mapping, yet if you put the ISR in User Mode, you have to guarantee that no User Interrupt occurs while running Kernel Mode code (such stuff as USR, handler functions, monitor calls, etc.) or else you will return in the wrong mode (you will return in User Mode, yet were interrupted in Kernel Mode; since User mapping <> Kernel mapping, you won't wind up where you should). This problem can be (and has been) solved; an example is the code required for Completion Routines. The solution is to place special code in (User) location 0 which executes an internal EMT called .ASTX, A Synchronous Trap eXit. This gets us back into Kernel mode, within the monitor, which realizes we're returning from a Completion Routine, and can look up the PS/PC saved on entering the Routine. Now the RTI, exited from Kernel Mode, can return us properly to any mode necessary. The obvious solution is to put the interrupt code in Kernel Space, and the recommended way of doing this is to write a handler which does the interrupt processing for you. At the cost of learning about handlers and learning some possibly-unfamiliar data structures and methods, this solution has a number of secondary benefits. First, it gets even more of your user program out of User Space (remember, we're trying to save memory) and puts it into Kernel Space (which is where .FETCH will store it). Second, routines have been built into the monitor that handle a lot of the dirty work for you. For example, you generally need to get the 22-bit address of the user buffer to hand to the hardware device -- RT-11 automatically translates the user buffer address for you (using PAR1 to point to it, and giving you the relocation constant and displacement bias). In addition, address translation routines (formerly handled by the ATX pseudohandler) are built into the monitor for non-standard requests, such as .SPFUNs (allowing you to pass arbitrary "buffer-like" parameters and translate those of interest to you). There are all kinds of clever tricks you could do to speed things up using a handler. For example, you could bypass the address translation mechanism inherent in .READ/.WRITE processing by using a series of .SPFUNs to give the handler specialized information. You could, for example, use one .SPFUN to tell the handler "Here is where my buffer lives, and it will stay here for the duration of the program. Go set up the DMA registers accordingly." You use another one to say "Now, using already-initialized a ddresses, go start the transfer." Yet another could be used to mess with flags in the User Space to give you the Moral Equivalent of .WAIT. If you want to be still more devious, you can directly interact with the handler in the following way. First, .DSTAT will give you the (Kernel) address of the handler after it has been loaded into (Kernel) memory by .FETCH. You can now read and write into the handler using .PEEK/.POKE. You can even call handler routines using the .CALLK mechanism. An interesting dichotomy arose in discussing the "SB Memory" problem with the RT-11 Developers. In suggesting the mapped monitors and VBGEXE, their view of what was happening differed from mine. I naturally saw the process as "shrinking the monitor", or more precisely, getting the monitor (and USR and handlers) out of my address space. They saw it as "cleaning up Kernel Space" by getting rid of the User Program. I was viewing memory, which to me meant User Space, as the scarce resource; they were looking at scarce Kernel memory. By banishing the User, more memory exists for handlers, the USR, specialized buffers, even special purpose routines which the user can .CallK (if you really get squeezed ...). OK, so suppose we do all this, and now want to use VBGEXE. How do we do it? There are two bits, NOVBG$ and VBGEX$, in the Job Definition Word ($JSX, location 4 of the .SAV image) which interact with VBG. One forbids the job running under VBG (the BOOT command and RESORC, for example, set this bit). Under the guidelines given above, user programs which have interrupt service routines should set this bit! (It is cleared by default). The other bit gives implicit permission for VBG to run the job. If you have SET RUN VBGEXE, then any program with this permission bit set will automatically use VBGEXE to load the program. Finally, if neither bit is set, VBG will be used if you explicitly request it using the V or VRUN commands. Note that running under VBGEXE, with the additional memory it provides, can produce some dramatic improvements in your programs. One of the first programs to have the VBGEX$ bit set was MACRO -- under VBG, large assemblies run faster by an order of magnitude! All that now remains is to answer the last question asked in the title, namely Where Has TT.SYS Gone? By now, you all realize the answer to the question, namely that its functionality has all been absorbed into the monitor, just as it was in FB (and XM). However, RT-11 Version 5.6 (but not Version 5.7) actually had a copy of TT.SYS! What gives? It turns out that there was still a Version 5.5 monitor included on the 5.6 kit  RT11MT, the monitor which is loaded as part of a bootable magtape. Space is very tight for bootable tape, and the larger 5.6 monitor could not be so simply shoe-horned into memory. Since the entire purpose of RT11MT was the creation of a bootable disk system, the 5.6 solution was to use the smaller 5.5 MT monitor for this purpose. In version 5.7, however, the tape bootstrap code was changed to allow the entire 5.7 monitor to be read in. So there are no more 5.5 SJ monitors, hence no longer a need for TT.SYS.