TOPS-20 Monitor Calls User's Guide

Chapter 8 Using extended addressing

The term "extended addressing" refers to the size of the addresses that TOPS-20 uses on the DECSYSTEM-20 Extended KL10 processor. Older versions of TOPS-20 (Release 4.1 and before) used 18-bit addresses; newer versions (Release 5 and after) use 30-bit addresses.

This chapter discusses the two main activities associated with using TOPS-20 monitor calls with extended addressing:

  1. Writing new programs for execution in sections of memory other than section 0
  2. Converting existing programs so that they can be executed in sections other than section 0

This chapter also contains information on hardware instructions and macros useful to MACRO programmers who use extended addressing.

The discussion in this chapter depends heavily on the material in the DECsystem-10/DECSYSTEM-20 Processor Reference Manual. Refer to that manual for a description of the format of 30-bit addresses, the algorithm the processor uses to calculate effective addresses, and the way that individual machine instructions work.

8.1 Overview

The TOPS-20 extended address space contains 32 (decimal) sections. Each section contains 512 pages of 512 words each (256K words). An 18-bit address, called a local address, can reference any word in a given section. A 30-bit, or global, address can reference any word in any section of memory.

In contrast, TOPS-20 V4.1 and earlier provided an 18-bit, 256K-word address space. The Program Counter (PC) register was 18 bits. For each instruction executed, the first action taken was the computation of an 18-bit effective address. The algorithm for calculating the effective address (including indexing and indirecting rules) was the same for all instructions.

Because the TOPS-20 virtual address space is limited to 32 sections, and section numbers longer than 5 bits are illegal, legal addresses are effectively limited to 23 bits. When addressing data, you can view this 32-section address space as one large memory area.

From the point of view of program execution, however, memory is divided into 32 discrete sections. A program can have code in more than one section of memory, and it can execute that code (assuming the constraints discussed below), but it must change sections explicitly, as discussed below.

Compatibility for existing programs is provided by section 0. A program running in section 0 behaves as though it were being executed on a system without extended addressing, except for certain instructions such as XJRSTF. For more information on the actions of specific instructions, see the DECsystem-10/DECSYSTEM-20 Processor Reference Manual.

8.2 Addressing memory and ACS

The extended format PC contains a section field and a word-within-section field. When an instruction is executed, only the word field is incremented. Column overflow is never carried from the word field to the section field. If the last word of a section is executed, and it is not a jump instruction, then the next instruction is fetched from word 0 of the same section. Thus a program can only change sections explicitly, by means of a PUSHJ, JRST, XJRST or XJRSTF instruction, and only an XJRST or an XJRSTF can change control from section 0 to another section.

Because a whole word cannot contain a 30-bit address and the program flags, the PC and flags are a two-word entity. The flag bits are in the first word, and the figure below represents the second word. Figure 8-1 shows the format of the address fields of the PC.

             0     5 6              17 18                    35
             --------------------------------------------------
             ! un-  !      section   !    word-within-        !
             ! used !      number    !    section             !
             --------------------------------------------------
Figure 8-1: Program Counter Address Fields

The word (word-within-section) field consists of 18 bits and thus represents a 256K-word address space similar to the single-section address space of release 4 and earlier. The section number field is 12 bits, of which only the right-hand five bits can be nonzero because section numbers greater than 31 are illegal. The leftmost seven bits of the section number field must be zero. This provides room to address 32 separate sections, each of 256K words.

Each section is further divided into pages of 512 words, just as in earlier releases. The paging facilities allow the monitor to determine the existence and protection of each section.

The PC section field determines what section a program is running in. If the section field contains zero, the program is running in section 0. Most extended addressing features are not available to a program running in section 0. All quantities (including addresses), when calculated from section 0, are considered to be local (18 bits).

  1. A program executing in section 0 cannot address memory in any other section. (One-word global byte pointers are an exception to this rule. Refer to Chapter 1 of the TOPS-20 Monitor Calls Reference Manual for more information.)
  2. The program cannot jump from section 0 to another section unless it uses a monitor call or the XJRST or XJRSTF instruction.
  3. The program runs exactly as it would run on a machine without extended addressing.

If the section field contains a number from 1 to 31 (decimal) inclusive, the program is executing in a nonzero section (a section other than section 0). The hardware considers addresses to be 30 bits, and the program can use extended addressing features.

A local address is defined as an 18-bit address in the same section as the program counter (PC) of the instruction. Local addresses are relative to the PC section. A global address is a 30-bit address, which therefore supplies its own section number.

The following paragraphs explain the way effective addresses are calculated in nonzero sections. In addition, see the description in the DECsystem-10/DECSYSTEM-20 Processor Reference Manual.

8.2.1 Instruction Format

The format of a machine instruction is the same as on an unextended machine. The effective address calculation depends on the address field (Y, 18 bits), the index field (X, 4 bits), and the indirect field (I, 1 bit). Figure 8-2 shows these fields.

             0       8 9  12  13 14   17 18                   35
             ---------------------------------------------------
             !        !     !   !       !                      !
             ! OPCODE ! AC  ! I !   X   !           Y          !
             !        !     !   !       !                      !
             ---------------------------------------------------
Figure 8-2: Instruction Word Address Fields

If I and X are 0, the instruction uses neither indexing nor indirection, so the effective address is Y (18 bits). The section number, since it is not specified in the address, is taken from the section field of the PC. The PC section field contains the number of the section from which the instruction was fetched. Such an 18-bit address is called a local address.

The following is an example of an instruction whose I, X and Y fields evaluate to an 18-bit effective address.

        3,,400/    MOVEM T,1000

The effective address is word 1000 of the current section. The section from which the instruction is fetched is section 3, so the instruction moves the contents of register T into memory word 3,,1000.

8.2.2 Indexing

The first step in the effective address calculation is indexing. If the X field is nonzero, indexing is used. The calculation of the effective address then depends on the contents of the specified index register. Indexing may be local or global as follows:

8.2.3 Indirection

If the I field contains 1, the instruction specifies indirection. An indirect word is fetched from the address determined by Y and X. Two types of indirect word exist, Instruction Format Indirect Word (IFIW) and Extended Format Indirect Word (EFIW). They are described in the following section.

8.2.3.1 Instruction Format Indirect Word (IFIW)

This word contains Y, X, and I fields of the same size and in the same position as instructions (in bits 13-35). Bit 0 must be 1, and bit 1 must be 0; bits 2-12 are not used.

Figure 8-3 shows an instruction format indirect word.

              0 1 2         12 13 14  17 18                     35
             -----------------------------------------------------
             ! ! !            !  !     !                         !
             !1!0! (not used) !I !  X  !             Y           !
             ! ! !            !  !     !                         !
             -----------------------------------------------------
Figure 8-3: Instruction Format Indirect Word

The effective address calculation continues with the quantities in this word just as for the original instruction. Indexing can be specified and can be local or global depending on the left half of the index. Further indirection can also be specified.

Note that the default section for any local addresses produced from this indirect word is the section from which the word itself was fetched. This means that the default section can change during the course of an effective address calculation that uses indirection. The default section is always the section from which the last address word was fetched.

8.2.3.2 Extended Format Indirect Word (EFIW)

This word also contains Y, X, and I fields, but in a different format. Figure 8-4 shows an extended format indirect word.

              0 1 2   5 6            17 18                     35
             -----------------------------------------------------
             ! ! !     ! <-------------!- Y -------------------> !
             !0!I!  X  !   (section)   !          (word)         !
             ! ! !     !               !                         !
             -----------------------------------------------------
Figure 8-4: Extended Format Indirect Word

If indexing is specified in this indirect word (bits 2-5 nonzero), the contents of the entire index register are added to the 30-bit Y to produce a global address. This type of indirect word never produces a local address. The type of address calculation used does not depend on the contents of the index register specified in the X field.

Hence either Y or Y(X) can be used as an address or an offset within the extended address space, just as is done in the 18-bit address space. If further indirection is specified (bit 1 set), the next indirect word is fetched from Y as modified by indexing (if any). The next indirect word can be in instruction format or extended format, and its interpretation does not depend on the format of the previous indirect word.

8.2.3.3 Macros for Indirection

The system file MACSYM.MAC contains several convenient macros for constructing indirect words. Macro LFIWM generates local (instruction format) indirect words. Both the macros EP. and GFIWM may be used to generate global (extended format) indirect words.

8.2.4 AC References

A local address in the range 0-17 (octal) references the hardware ACs as memory. This is true in every section of memory.

A global address in section 1 in the range 1,,0 to 1,,17 (octal) also refers to the hardware ACs. A global address in any other section refers to memory. This means that the following behavior occurs.

  1. Addresses in the range 0-17 reference ACs as expected. The instruction
                     MOVE 2,3
    
    fetches the contents of hardware register 3 regardless of what section the instruction executes in.
  2. To make a global reference to an AC, the global address must contain a section number of 0 or 1.
  3. Arrays can cross section boundaries. Global addresses specifying any section except section 1 always refer to memory, never to the hardware ACs. For this reason, incrementing the address 6,,777777, for example, yields address 7,,000000, which is a memory location.
  4. The ACs are regarded as local to any section; a local address (0-17) references the ACs from any section. Hence, a jump instruction which yields a local effective address of 0-17 in any section will cause code to be executed from the ACs.

8.2.5 Extended Addressing Examples

These instructions make local references within the current PC section:

             3,,400/   MOVE T,1000              ; fetches from 3,,1000
                       JRST 2000                ; jumps to 3,,2000

The following instructions scan table TABL, which is in the current section:

                       MOVSI X,-SIZ
             LP:       CAMN T,TABL(X)           ; TABL in current section
                       JRST FOUND
                       AOBJN X,LP

The following instructions scan table TABL, which is in section TSEC, by using a global address:

                       MOVEI X,0
             LP:       CAMN T,@[GFIWM TSEC,TABL(X)] ; extended format
                       JRST FOUND
                       CAIGE X,SIZ-1
                       AOJA X,LP

Similarly, the EP. macro can be used for the same purpose:

                       MOVEI X,0
             LP:       CAMN T,@[EP.<TSEC>B17!TABL(X)]
                       JRST FOUND
                       CAIGE X,SIZ-1
                       AOJA X,LP

The following examples illustrate various aspects of indexing and indirection in effective address calculation:

             4/100
             6,,1000/MOVE 1,@2000
             6,,2000/LFIWM @4000
             6,,4000/LFIWM 200(4)

Effective address = 300 in section 6

             6,,SUB/   MOVE 1,@[LFIWM @ZZZ]

             6,,ZZZ:   LFIWM @XXX
             XXX:      LFIWM ARRAY(4)

Effective address = ARRAY+100 in section 6

             6/14,,ADDRX
             11,,ROU/MOVE 1,@[LFIWM (6)]

             14,,ADDRX: LFIWM 100

Effective address = 14,,100

8.2.6 Immediate Instructions

Each effective address calculation yields a 30-bit address, defaulting the section if necessary. Immediate instructions use only the low-order 18 bits of this as their operand, however, and set the high-order 18 bits to 0. Hence instructions such as MOVEI and CAI produce identical results regardless of the section in which they are executed.

Two immediate instructions retain the section field of their effective addresses. These are:

8.2.6.1 XMOVEI

The XMOVEI instruction loads the 30-bit effective address into the AC, and sets bits 0-5 to 0. If no indexing or indirection is used, the number of the current section is copied from the PC to the AC. This instruction can replace MOVEI when a global address is needed.

The following example shows the use of the XMOVEI instruction in a subroutine call. The subroutine is in section XSEC, but the argument list is in the same section as the calling program.

        XMOVEI AP,ARGLIST
        PUSHJ P,@[GFIWM XSEC,SUBR]

The subroutine can reference the arguments with the following instruction.

        MOVE T,@1(AP)

To construct the addresses of arguments, the subroutine can use the following instruction.

        XMOVEI T,@2(AP)

The last two instructions assume that register AP contains the argument list pointer. If the address the calling program placed in AP is an IFIW, the section number in the effective address is that of the calling program. If the address the calling program placed in AP is an EFIW, the section number in the effective address of the argument block is determined by the section number the calling program placed in AP.

The argument list would be found in the caller's section because of the global address in AP. The section of the effective address is determined by the caller, and is implicitly the same as the caller if an IFIW is used as the arglist pointer, or is explicitly given if an EFIW is used.

8.2.6.2 XHLLI

The XHLLI instruction replaces the left half of the accumulator with the section number of the PC, and places zero in the right half of the AC. This instruction is useful for constructing global addresses.

8.2.7 Other Instructions

The instructions discussed here are affected by extended addressing, but not necessarily in the way that their effective addresses are calculated. In addition to the material presented here, see the DECsystem-10/DECSYSTEM-20 Processor Reference Manual regarding the following instructions: LUUOs, BLT, XBLT, XCT, XJRSTF, XJEN, XPCW, SFM.

8.2.7.1 Instructions that Affect the PC

These instructions are PUSHJ, POPJ, JRST. PUSHJ stores a 30-bit PC address, but stores no flags. It sets bits 0-5 of the destination word to 0.

POPJ restores a 30-bit PC address from the stack, but does not restore the flags. It also sets bits 0-5 of the destination word to 0.

The JSA and JRA instructions can be used only within a section. In section 0 the JSP and JSR instructions can store flags,,PC but then cannot transfer out of section 0. The JSP and JSR instructions can store flags,,PC in nonzero sections and then can transfer into any other section (if the address is specified with indexing or indirection).

8.2.7.2 Stack Instructions

PUSHJ, POPJ, PUSH, POP, and ADJSP. These instructions use a local or global address for the stack according to the contents of the stack pointer. Whether the stack address is local or global depends on the same rules as those that govern indexing in effective address calculation. (See section 8.2.2.) It is always best to use the ADJSP instruction when working with stacks. This instruction works in any section and will indicate when a pushdown overflow error occurs.

In brief, if the left half of the stack pointer is zero or negative (prior to incrementing or decrementing), the pointer references a local address and the address in its right half is the address of the current item in the stack. The stack pointer is incremented or decremented by adding or subtracting one from both sides, respectively.

If the left half of the stack pointer is positive, the entire word is taken as a global address. The stack pointer is incremented by adding 1, and decremented by subtracting 1.

A stack that contains global addresses can be used the same way a local stack is used. The global stack, however, can contain pointers to routines in other sections.

To protect against stack overflow and underflow, make the pages before and after the stack inaccessible. This method must be used because a global stack has no room for a count in the left half of the pointer.

8.2.7.3 Byte Instructions

To reference a byte in another section, you must use either a one-word, or a two-word, global byte pointer. Both global and local byte pointers are legal arguments to monitor calls from nonzero sections but there are some restrictions on the use of one-word global byte pointers from section 0. See Section 8.3 for further information.

Chapter 1 of the TOPS-20 Monitor Calls Reference Manual describes one-word global byte pointers. The DECsystem-10/DECSYSTEM-20 Processor Reference Manual describes two-word global byte pointers.

8.3 USING MONITOR CALLS

If a program runs in a single section, even though that section is not section 0, most monitor calls execute exactly the way they do in section 0. This is because when no section number is specified, the current section is the default.

The GTFDB% call, for example, requires that AC3 contain the address of the block in which to store the data it obtains from the file data block. This address can be an 18-bit address regardless of what section the monitor call is made from. When the monitor sees that the address is local, it obtains the section number from the PC of the process that makes the call.

The same is true of calls that accept page numbers. If a 9-bit page number is passed as an argument, the monitor obtains the section number from the PC of the process that made the call. Monitor calls arguments are discussed in Chapter 1 of the TOPS-20 Monitor Calls Reference Manual.

It is sometimes desirable to specify addresses in section 0 when executing a JSYS from a nonzero section. The bit PM%EPN for PMAP%, and FH%EPN for JSYSs that accept fork handles, prevent the current section (the section in which a program is running) from being the target section for the monitor call's arguments.

Another restriction on arguments passed to monitor calls executed in sections other than section 0 concerns universal device designators, which have the format 5xxxxx,,xxxxxx or 6xxxxx,,xxxxxx (.DVDES). Universal device designators are not legal except in section 0. This is because of the existence of one-word global byte pointers, which can have the same format.

Thus monitor calls that accept either a device designator or a byte pointer when called from section 0 do not accept universal device designators in any other section. Other device designators, such as .TTDES (0,,4xxxxx), can be used in any section. Conversely, these monitor calls that can accept either universal device designators or byte pointers do not accept one-word global byte pointers in section 0.

The calls SIR% and RIR% should not be used in sections other than section 0. These calls work in other sections only if all the code associated with these calls exists in the same section as the code that makes the call.

For example, if an SIR% call is executed in section 4, it executes correctly if and only if the code that generates the interrupts, the interrupt-processing routines, and all associated tables are also located in section 4. Thus, in programs intended to run in a section other than section 0, the XSIR% and XRIR% calls, described in Chapter 4, should be used in place of SIR% and RIR%. In general, it is recommended that the extended form of monitor calls be used since this form works in any section, including section 0.

8.3.1 Mapping Memory

The PMAP% monitor call accepts an 18-bit page number, half of which is a section number. Thus PMAP% can be used to map a page from one section to another. If the destination section does not exist, that section will be created.

The SMAP% monitor call maps one or more sections of memory. It works like the PMAP call, but maps sections instead of pages. If the destination section does not exist, SMAP% creates the section.

Access to the sections in a process map is determined by the same algorithm that determines access to a page within a given section. If a process section and a page in that section have different accesses, the access privileges are ANDed together. The process requesting access to the page gains access only if it has access rights at least equal to the ANDed protections.

For example, if a process has read access to a section and maps a page into that section for which the process has read and write access, the page is mapped, but the process gets only read access to the mapped page.

The following sections describe the SMAP% functions.

8.3.1.1 Mapping File Sections to a Process

This function maps one or more sections of a file to a process. All pages that exist in the source sections are mapped to the destination sections. Access to the mapped pages is determined by ANDing the access allowed to the file and the access specified in the SMAP% call.

Although files do not actually have section boundaries, this monitor call views them as having sections that consist of 512 contiguous pages. Each file section starts with a page number that is an integer multiple of 512.

This call cannot map a process memory section to a file. To map a process section to a file, use the PMAP% monitor call to map the section page-by-page.

This function of the SMAP% call requires three words of arguments, as follows:

AC1: source identifier: JFN,,file section number
AC2: destination identifier: fork handle,,process section number
AC3: flags,,count

The flags determine access to the destination section, and the count is the number of contiguous sections to be mapped. The count must be between 0 and 37 (octal). The flags are as follows.

B2(SM%RD) Allow read access
B3(SM%WR) Allow write access
B4(SM%EX) Allow execute access
B18-35 The number of sections to map. This number must be between 1 and 37 (octal).
8.3.1.2 Mapping Process Sections to a Process

The SMAP% monitor call also maps sections from one process to another process. In addition, you can map one section of a process to another section of the same process. The SMAP% call maps all pages that exist in the source section to corresponding pages in the destination section.

If you map a source section into a destination section with SM%IND set, SMAP% creates the destination section using an indirect pointer. This means that the destination section will contain all pages that exist in the source section, and the contents of the destination pages will be identical to the contents of the source pages.

Furthermore, after SMAP% has mapped the destination section, changes that occur in the source section map cause the same changes to be made in the destination section map. This ensures that both the source section and the destination section contain the same data.

If SM%IND is not set, SMAP% creates the new section using a shared pointer. After SMAP% maps the destination section, changes that occur in the source section's map do not cause any change in the destination section's map. Thus after a short time the source and destination sections might contain different data.

If you request a shared pointer (SM%IND not set) to the destination section, what happens depends on the contents of the source section when the SMAP% call executes. The outcome is one of the following.

  1. If the source section does not exist, the SMAP% call creates the section.
  2. If the source is a private section, a mapping to the private section is established, and the destination process is co-owner of the private section.
  3. If the source section contains a file section, the source section is mapped to the destination section.
  4. If the source section map is made by means of an indirect section pointer, SMAP% follows that pointer until the source section is found to be nonexistent, a private section, or a section of a file.

This SMAP% function requires three words of arguments in AC1 through AC3.

AC1: Source identifier: fork handle,,section number
AC2: Destination identifier: fork handle,,section number
AC3: access flags,,the number of contiguous sections to map.

The number of sections mapped, the number in the right half of AC3, must be between 1 and 37.

The flags determine access to the destination section. The flags are as follows:

B2(SM%RD) Allow read access
B3(SM%WR) Allow write access
B4(SM%EX) Allow execute access
B6(SM%IND) Map the destination section using an indirect section pointer. Once the destination section map is created, the indirect section pointer causes the destination section map to change in exactly the same way that the source section map changes.
B18-35 Count of the number of contiguous sections to be mapped.
8.3.1.3 Creating Sections

Before you can use a nonzero section of memory, you must create it. If your program references a nonzero section of memory that does not exist (that is not mapped), the instruction that makes the reference fails.

This SMAP% function requires three words of arguments in AC1 through AC3, as follows:

AC1: 0
AC2: destination identifier: fork handle,,section number
AC3: flags,,count

The flags determine access to the destination section, and the count is the number of contiguous private sections to be created. This count must be between 1 and 37.

The flags in the left half of AC3 are as follows:

B2(SM%RD) Allow read access
B3(SM%WR) Allow write access
B4(SM%EX) Allow execute access
B6(SM%IND) Create the section using an indirect pointer
B18-35 The number of sections to create. This number must be between 1 and 37. All created sections are contiguous.
8.3.1.4 Unmapping a Process Section

You can use the SMAP% monitor call to unmap one or more sections of memory in a process. The contents of the section are lost.

If the section contains pages mapped from a file, this function does not cause the unmapped sections to be written back to the file from which they were mapped. Such pages must be mapped to the file by means of the PMAP% call.

This function requires three words of arguments in AC1 through AC3, as follows.

AC1: -1
AC2: Destination identifier: fork handle,,section number
AC3: 0,,count
The count is the number of contiguous sections to be unmapped. This number must be between 1 and 37.

8.3.2 Starting a Process in Any Section

You can use most of the calls described in Chapter 5 to control programs that run in a nonzero section. The SFORK% monitor call is an exception, and will not start a program in a nonzero section.

The XSFRK% monitor call starts a process in any section of memory. If the process is frozen (by means of the FFORK% call), XSFRK% changes the double-word PC, but does not resume execution of the process. To resume the execution of any frozen fork, use the RFORK% call.

The XSFRK% call requires three words of arguments in AC1 through AC3.

AC1: flags,,process handle

Flags:

SF%CON(1B0) continue a process that has halted. If SF%CON is set, the address in AC3 is ignored and the process continues from where it was halted.
AC2: PC flags,,0
AC3: address to which this call is to set the PC

The XSFRK% call also starts a process in section 0. To do so, set the left half of AC3 to zero and the right half of AC3 to the address in section 0 at which you want the process to start.

Most other calls consider an address with a zero in the left half to be a local address. The XSFRK% call, however, uses the contents of AC3 to set the PC. A PC with zero in the left half indicates an address in section 0.

8.3.3 Setting the Entry Vector in Any Section

The SEVEC% monitor call has room in its argument ACs for only a half-word address, so it cannot be used to set a process entry vector to an address in a nonzero section. The XSVEC% call, on the other hand, uses an AC for the address of the entry vector, and another AC for the length of the entry vector, and can specify an entry vector in any section of memory.

The XSVEC% call requires three words of arguments in AC1 through AC3.

AC1: process handle
AC2: length of the entry vector, or 0
AC3: address of the beginning of the entry vector

The length of the entry vector specified in AC2 must be less than 1000 words. If AC2 contains 0, TOPS-20 assumes a default length of two words.

8.3.4 Obtaining Information About a Process

Although the monitor calls described in Chapter 5 work in any section of memory, several of them can only return information about the section in which they are executed. The following paragraphs describe the monitor calls you can use to obtain information about any section of memory.

8.3.4.1 Memory Access Information

Several kinds of information about memory are important. Among them are whether a page or section exists (is mapped), and, if so, what the access to a page or section is. The RSMAP% and XRMAP% calls provide this information.

The RSMAP% monitor call reads a section map, and provides information about the mapping of one section of the address space of a process. RSMAP% requires one word of arguments in AC1: a fork handle in the left half, and a section number in the right half. It returns the access information in AC2.

The map information that RSMAP% returns in AC1 can be the following:

-1 no current mapping present (the section does not exist)
0 the mapping is a private section
n,,m where n is a fork handle or a JFN, and m is a section number. If n is a fork handle, the mapping is an indirect or shared mapping to another fork's section. If n is a JFN, the mapping is a shared mapping to a file section.

The access information bits returned in AC2 are the following:

B2(SM%RD) Read access is allowed
B3(SM%WR) Write access is allowed
B4(SM%EX) Execute access is allowed
B5(PA%PEX) The section exists
B6(SM%IND) The section was created using an indirect pointer.

Although the RSMAP% call does not return information on individual pages, the data it does return is useful in preventing error returns from the XRMAP% monitor call.

The XRMAP% call returns access information on a page or group of pages in any section of memory. Although the RMAP% call returns access data about a page in the current section, and you can use the RSMAP% call in any section of memory, you must use the XRMAP% call to obtain information about pages in any section other than the current section.

The XRMAP% call requires two words of arguments in AC1 and AC2.

AC1: process handle,,0
AC2: address of the argument block

The argument block addressed by AC2 has the following format:

          !=======================================================!
          !   Length of the argument block, including this word   !
          !=======================================================!
          ! number of pages in this group on which to return data !
          !-------------------------------------------------------!
          !        number of the first page in this group         !
          !-------------------------------------------------------!
          !       address at which to return the data block       !
          !=======================================================!
          \                           .                           \
          \                           .                           \
          \                           .                           \
          !=======================================================!
          ! number of pages in this group on which to return data !
          !-------------------------------------------------------!
          !        number of the first page in this group         !
          !-------------------------------------------------------!
          !       address at which to return the data block       !
          !=======================================================!

The number of words in the argument block is three times the number of groups of pages for which you want access data, plus one. Each group of pages requires three arguments: the number of pages in the group, the number of the first page in the group, and the address at which the monitor is to return the access data.

Note that the address to which the monitor returns data should be in a section of memory that already exists. If it does not exist, the call will fail with an illegal memory reference.

The access information returned for each group of pages specified in the argument block is the following:

B2(RM%RD) read access allowed
B3(RM%WR) write access allowed
B4(RM%EX) execute access allowed
B5(RM%PEX) page exists
B9(RM%CPY) copy-on-write access

For each page specified in the argument block that does not exist, XRMAP% returns a -1. It also returns a zero flag word for each such page. The data block to which XRMAP% returns the access information should therefore contain twice as many words as the number of groups of pages about which you want information.

If you execute an XRMAP% call to obtain information about a page in a nonexistent section, the XRMAP% call fails with an illegal memory reference. For this reason it is recommended to execute an RSMAP% call to determine that the section exists before you use XRMAP% to obtain information about any page within that section.

8.3.4.2 Entry Vector Information

To obtain the entry vector of a process in any section of memory, use the XGVEC% call. This call returns the length of the entry vector in AC2 and the address of the entry vector in AC3.

The XGVEC% call requires one word of argument: in AC1, the handle of the fork for which you want the entry vector.

8.3.4.3 Page-Failure Information

A page-fail word, described in the DECsystem-10/DECSYSTEM-20 Processor Reference Manual, contains information that allows a program to determine the cause of a page trap and the address of the instruction that caused the trap. This information allows a program to correct the cause of the page-fail trap. Once the program has corrected the cause of the page-fail trap, the program can continue execution.

The XGTPW% call obtains the page-fail word from the monitor's data base, and returns it to the calling program's address space. The XGTRP% call requires two words of arguments in AC1 and AC2.

AC1: process handle
AC2: address of the block in which to return data

8.3.5 Program Data Vectors

Program Data Vectors (PDVs) are data structures in a process that are known to the monitor by name and location. They contain information about the program segments within a process. The PDV is a block of data that LINK writes into memory when loading and linking a program. The PDV resides in memory as a part of the program, and starts at a program data vector address (PDVA). PDVs are used to allow user programs to obtain information about other programs that can be mapped into a process. PDVs and PDVAs are manipulated by using the PDVOP% monitor call. (Refer to the TOPS-20 Monitor Calls Reference Manual for a complete description of the PDVOP% monitor call.) The PDVOP% monitor call can be used to obtain information about an execute-only process.

Certain words in the PDV (for example, .PVNAM) point to blocks of information. These words are in either IFIW or EFIW format (see Sections 8.2.3.1 and 8.2.3.2) except that they cannot use indexing, and any indirect chain pointed to by the word cannot go through an accumulator. This allows a program to find the address of a block pointed to by a PDV word by simply using an XMOVEI instruction. For example,

        XMOVEI         AC1,@.PVNAM(AC2)

puts into AC1 the global address of the name of the PDV whose PDVA is in AC2.

8.3.5.1 Manipulating PDV Addresses

For the process specified in word .POPHD of the argument block, the .POGET function of the PDVOP% monitor call returns all PDVAs within the range of addresses specified in words .POADR and .POADE of the argument block. (See the description of the PDVOP% monitor call in the TOPS-20 Monitor Calls Reference Manual for the format of the argument block.) The address range supplied by words .POADR and .POADE determines which PDVAs are affected by any given call.

The .POADD function of the PDVOP% monitor call adds the PDVAs specified in the data block to the system's data base for the specified process. The PDVAs must be in ascending order within the data block.

The .POREM function of the PDVOP% monitor call removes a set of PDVAs from the system's data base for the specified process. Those removed are the ones within the range specified by words .POADR and .POADE of the argument block.

8.3.5.2 PDV Names

The .PONAM function of the PDVOP% monitor call returns the ASCIZ name of a PDV in memory. Word .POADR of the argument block must contain a valid PDVA for the specified process. The name returned is the one to which word .PVNAM of the PDV points. The string returned by .PONAM is placed into the data block.

For the specified process, the .POLOC function returns in the data block all the PDVAs of PDVs with the specified name. The byte pointer in AC3 points to the PDV name. Function .POLOC is affected by .POADR/.POADE words.

The following rules apply to the assignment of PDV names. If these rules are followed, it is quite unlikely that two packages that want to run in the same process will discover a conflict in PDV names.

  1. PDV names assigned by DIGITAL will contain the character "%" at the end (or elsewhere). No PDV names assigned by users should contain the "%" character.
  2. All PDV names containing the character "." are reserved to DIGITAL for future use.
  3. The character "$" is reserved for a special use: PDV names of the form string1$string2 are reserved for the special class of use named by string1. Rules 1 and 2 still apply in this case.

As a general principle, avoid using PDV names that are insufficiently specific; use of such names invites conflicts with other packages.

8.3.5.3 Version Number

The .POVER function of the PDVOP% monitor call returns in the data block the version of a program in memory. Word .POADR must contain a valid PDVA for the specified process. The version returned is the one that word .PVVER of the PDV contains.

For more information on program data vectors, including explanations of the static memory map (pointed to by word .PVMEM) and the symbol table vector (pointed to by word .PVSYM), refer to the TOPS-20 LINK Reference Manual.

8.4 Modifying existing programs

Existing programs can be modified to run in any section of memory, including both section 0 and all other sections. The sections that follow discuss the changes that must be made to an existing program so that it runs in a single nonzero section.

8.4.1 Data Structures

Stacks, tables, and other data structures used in the past have often contained words with an address in the right half and a count in the left half. The count could be positive or negative because all programs ran only in section 0, and when the contents of a word were evaluated for Effective Address calculation or address use in section 0, only the right half was considered.

In all other sections, the entire word is considered to be an address. If the left half of the word is negative, the left half is ignored when the address is evaluated, and the address is local. Thus for a word to contain an address in the right half and a count in the left half, the count must be negative.

8.4.1.1 Index Words

Be sure the left halves of index words contain a nonpositive quantity. To use the left half of an index register to hold a count, the count must be negative. If the left half is unused, it must be zero so that the effective address is a local address. If the left half contains a positive number, the indexed address will be global.

8.4.1.2 Indirect Words

To be sure that an indirect word in a nonzero section is evaluated as a local address, always set bit 0 of the indirect word. Argument lists that produce local addresses in section 0, for example, will produce local addresses in any section if bit 0 is set.

8.4.1.3 Stack Pointers

As mentioned above, the left halves of stack pointers must contain zero or a negative number to produce local addresses. A negative number in the left half is considered to be a count. A positive number in the left half is considered to be a section number.

8.5 Writing multisection programs

Multisection programs, programs that use more than one section of memory, are similar to single-section programs that run in nonzero sections. They allow you to place tables needed for processing interrupts in any section of memory (See Chapter 4), to use very large arrays, and to write modules of code that can be dynamically mapped into a section of memory and executed.

In a single-section program, local addresses and byte pointers are sufficient to specify any word or byte in the program's address space. In a multisection program, local addresses and byte pointers cannot specify any word or byte in the program's address space. Most monitor calls use only one AC per argument, so passing two-word global byte pointers is not possible. Thus, it is necessary to:

In many multisection programs it is not necessary to keep all the arguments required by a call in the same section as the code that makes the call. Global arguments are required, and they take several forms. Chapter 1 of the TOPS-20 Monitor Calls Reference Manual gives details on the use of these arguments.

The following program computes a file checksum by XORing the words in all file pages. The program is loaded into section 0 and maps itself into section 1. It then jumps into section 1 to access the file data loaded into section 15.

           TITLE CHKSUM - COMPUTE A FILE CHECKSUM
           SEARCH MONSYM           ;STANDARD UNIVERSAL FILES
           SEARCH MACSYM
           .REQUIRE SYS:MACREL     ;GET JSERR SUPPORT ROUTINES

           STDAC.                  ;DEFINE STANDARD ACS

   ; PROGRAM CONSTANTS

           PDLSIZ==100             ;SIZE OF STACK
           CODSEC==1               ;SECTION TO MAP CODE INTO
           DATSEC==15              ;SECTION TO MAP FILE DATA INTO
           DATPAG==100             ;PAGE WITHIN DATSEC FOR FILE DATA
           PAGSIZ==1000            ;SIZE OF A PAGE

   CHKSUM: RESET%                  ;RESET THE WORLD
           MOVE P,[IOWD PDLSIZ,PDL]
           MOVE T1,[.FHSLF,,0]     ;MAP THIS FORKS SECTION 0
           MOVE T2,[.FHSLF,,CODSEC] ;TO EXTENDED CODE SECTION
           MOVX T3,SM%RD!SM%WR!SM%EX!SM%IND+1 
                                   ;INDIRECT POINTER RD,WR,EX 1 SECTION
           SMAP%
            EJSHLT                 ;UNEXPECTED FATAL ERROR
   GETFIL: SETZM FILJFN            ;SAY NO FILE SEEN
           TMSG <
   ENTER FILE SPEC TO CHECKSUM: >  ;PROMPT USER FOR FILE
           MOVX T1,GJ%SHT!GJ%OLD!GJ%FNS ;OLD FILE
           MOVE T2,[.PRIIN,,.PRIOU] ;READ FILE SPEC FROM TERMINAL
           GTJFN%                  ;GET FILE SPEC
            ERJMPR BADFIL          ;CANNOT GET FILE TELL USER
           MOVEM T1,FILJFN         ;SAVE FILE JFN
           MOVX T2,FLD(^D36,OF%BSZ)!OF%RD 
                                   ;REQUEST READ ACCESS AND 36 BIT BYTES
           OPENF%                  ;OPEN THE FILE
            ERJMPR BADFIL          ;CANNOT OPEN FILE TELL USER

           XJRST [CODSEC,,DOCHKS]  ;ENTER EXTENDED SECTION 
                                   ;AND DO CHECKSUM

   BADFIL: JSERR                   ;PRINT ERROR MESSAGE
           SKIPE T1,FILJFN         ;IS THERE A JFN
           RLJFN%                  ;YES. RELEASE IT
            EJSERR                 ;PRINT ERROR IF ANY
           JRST GETFIL             ;AND TRY TO GET FILE AGAIN

   ; THE FOLLOWING CODE RUNS IN A NONZERO SECTION AND 
   ; DOES A CHECKSUM OF THE FILE STORED IN FILJFN

   DOCHKS: SETZB Q1,Q2             ;Q1 HOLDS THE CHECKSUM. 
                                   ;INITIALLY ZERO
                                   ;Q2 IS THE CURRENT FILE PAGE NUMBER
   CHKLOP: MOVE T1,Q2              ;GET FILE PAGE NUMBER
           HRL T1,FILJFN           ;AND FILE JFN
           FFUFP%                  ;FIND FIRST USED PAGE
            ERJMPR NOPAGE          ;CAN'T GO ANALYZE ERROR
           HRRZ Q2,T1              ;REMEMBER CURRENT PAGE NUMBER
           AOS Q2                  ;USE NEXT HIGHER PAGE NEXT TIME 
           MOVE T2,[<DATSEC>B26+DATPAG] ;THROUGH LOOP TO DATA PAGE 
           HRLI T2,.FHSLF          ;IN DATA SECTION OF THIS FORK
           MOVX T3,PM%RD           ;READ ACCESS
           PMAP%                   ;MAP THE FILE PAGE
            EJSHLT                 ;UNEXPECTED FATAL ERROR
           HRLI T1,DATSEC          ;T1 IS INDEX INTO DATA PAGE. 
           HRRI T1,DATPAG*PAGSIZ   ;SETUP SECTION AND PAGE ADDRESS
           MOVEI T2,PAGSIZ         ;T2 COUNTS THE WORDS IN A PAGE

   ; THE FOLLOWING LOOP DOES THE CHECKSUM FOR A PAGE

   XORLOP: XOR Q1,(T1)             ;CHECKSUM THIS WORD
           AOS T1                  ;STEP TO NEXT WORD
           SOJG T2,XORLOP          ;DO THE WHOLE PAGE

           SETO T1,                ;UNMAP THE FILE PAGE
           MOVE T2,[<DATSEC>B26+DATPAG] ;TO DATA PAGE IN DATA 
           HRLI T2,.FHSLF          ;SECTION OF THIS FORK
           MOVX T3,PM%RD
           PMAP%
            EJSHLT                 ;UNEXPECTED FATAL ERROR
           JRST CHKLOP             ;LOOP FOR MORE PAGES

   ; HERE WHEN FFUFP% FAILS

   NOPAGE: CAIE T1,FFUFX3          ;NO USED PAGE FOUND?
           JSHLT                   ;NO. UNEXPECTED FATAL ERROR
           
   ; PRINT THE CHECKSUM AND QUIT
           TMSG <
   THE FILE CHECKSUM IS: >
           MOVX T1,.PRIOU          ;PRINT IT ON THE TERMINAL
           MOVE T2,Q1              ;GET THE CHECKSUM
           MOVX T3,NO%MAG!FLD(^D8,NO%RDX)  ;UNSIGNED OCTAL NUMBER
           NOUT%
            EJSHLT                 ;UNEXPECTED FATAL ERROR

           MOVE T1,FILJFN          ;GET FILE AGAIN
           CLOSF%                  ;CLOSE IT
            EJSHLT                 ;UNEXPECTED FATAL ERROR

           HALTF%                  ;STOP PROGRAM
           XJRST [CHKSUM]          ;JUMP BACK TO SECTION 0 AND 
                                   ;START OVER IF USER CONTINUES

   ; STORAGE

   PDL:    BLOCK PDLSIZ            ;STACK
   FILJFN: BLOCK 1                 ;FILE JFN

           END CHKSUM