CS222 Lecture: Data Structures and VAX Address Modes    10/7/91 revised 1/21/99

Materials: 

1. Transparency of address modes table in programming card
2. Transparency of possible floating literal mode values
3. Transparency/handout of all modes in use
4. Transparency/handout of translation of various Pascal constructs

I. Internal Representation of Structured Data Types
-  -------- -------------- -- ---------- ---- -----

   A. Thus far, we have dealt with VAX facilities for handling simple,
      unstructured data types: bytes, words, longwords, floating point numbers,
      etc.  These correspond directly to the primative types of higher-level
      languages - e.g. Pascal char (byte), integer (longword), and real
      (F-float).

   B. We now turn to a consideration of how some of the structured types are
      represented and manipulated.  In particular, we will deal with records,
      arrays, and pointers.  (We save sets and files for later.)

   C. The data type record is represented internally by representing each of its
      components, in turn, in successive memory locations.

      1. Example:

         Pascal:        var
                            student: record
                                id: integer;
                                gpa: real
                            end;

         MACRO:         STUDENT:        .BLKL   1
                                        .BLKF   1

      2. Individual fields are accessed by adding a constant offset to the base
         address of the record.  Example:

         Pascal         student.gpa := 0.0;

         MACRO:         CLRF    STUDENT+4

                        (Note that the offset is 4 because the first field
                         occupies 4 bytes.  Note, too, that the assembler does
                         the address arithmetic at assembly time.)

      3. Sometimes, additional, invisible fields are included within a record
         to align fields on certain address boundaries.

         a. Example: Consider the following record structure:

                        var
                            grade_point_info: record
                                grade: char;
                                value: real
                            end;

            There is some performance advantage to be gained by having a
            floating point number begin on a longword address boundary.  Indeed,
            some machines (but not the VAX) require this.  Thus, the record
            might be stored as follows:

                        GRADE_POINT_INFO:       .BLKB   1
                                                .BLKB   3       ; Filler
                                                .BLKF   1

         b. The inclusion or exclusion of such filler fields is the essence of
            the distinction between the Pascal types record and packed record.
            (On the VAX, the filler fields are not included, however, regardless
            of whether the record is declared packed.  The keyword packed does
            have some significance, however, when it comes to certain data
            items that occupy less than a full byte.)

   D. In similar fashion, an array type is represented by representing each of
      its elements in successive locations in memory.

      1. Example:

         Pascal:        var
                            grades: array[1..100] of real;

         MACRO:         GRADES: .BLKF   100

      2. Access to an individual element of an array requires a RUN-TIME
         address calculation, using a formula like:

         Address of element i = base_address + element_size * (i - lower_limit)

      3. Actually using such a computed address requires us to do one of three
         things:

         a. If the subscript value is a constant, then we can do all the needed
            computation at assembly time.

            Example:    grades[10] := 0.0;

                        CLRF    GRADES+36       ; 36 because grades[1] is at 0,
                                                ; grades[2] at 4 .. grades[10] 
                                                ; at 36

            Or let the assembler do the computation:

                        CLRF    GRADES+<4*<10-1>>

            - Note use of <> instead of parentheses (required, since parentheses
              have another meaning.)

            - Note need to parenthesize more fully than normal.  MACRO evaluates
              arithmetic expressions left to right - it does not have rules of
              operator precedence.  (So the more natural construct,
              GRADES+4*<10-1> would be taken as <GRADES+4>*<10-1>!

         b. If the subscript contains a variable, then we can modify the address
            field of the instruction to contain the computed address.

            Example:    grades[i] := 0.0;

                                        SUBL3   #1, I, R0
                                        MULL2   #4, R0
                                        ADDL2   R0, CLR_INST + 2
                        CLR_INST:       CLRF    @#GRADES

            i. This was the approach taken on the VonNeumann machine and some
               other early computers.

           ii. It is now frowned upon, for several reasons:

               - Self-modifying code must be reinitialized before each use
                 (e.g., in the above, the address field of CLR_INST must be
                 reset to point to the base address of GRADES if the above
                 sequence is contained in a loop.)

               - Self-modifying code is notoriously difficult to debug.

               - Self-modifying code cannot be shared by multiple users on a
                 multi-user system.

               - Self-modifying code cannot be stored in read-only memory 
                 (which is often used to contain the code for microprocessor
                 controlled systems.)

         c. The preferred approach is to make use of a special addressing mode
            that allows use of an address computed at run time.  One such mode 
            is called indirect addressing.

            Example: Addressing mode 6 on the VAX is called register-deferred
                     mode.  It uses the specified register as the ADDRESS (in
                     memory) of the operand.  It can be used, in conjunction 
                     with the MOVA instruction (which moves the ADDRESS of its
                     operand), to achieve the same result as above as follows:

                                        MOVAF   GRADES, R0
                                        SUBL3   #1, I, R1
                                        MULL2   #4, R1
                                        ADDL2   R1, R0
                                        CLRF    (R0)

                     Actually, this could be done somewhat more efficiently as
                     follows:

                                        MOVAF   GRADES-4, R0
                                        MULL3   #4, I, R1
                                        ADDL2   R1, R0
                                        CLRF    (R0)

         d. We will see later in this lecture that the VAX has a number of
            different addressing modes that can be used for working with
            arrays, to deal with a variety of different common operations
            that occur.

   E. Finally, a pointer type is represented in memory by a longword containing
      the address of an item.  Indirect addressing can be used to follow the
      pointer (DEREFERENCE IT) to get to the item.

      Example:  

      Pascal:           var
                            p: ^integer;

                        ...

                            p^ := 0;

      MACRO:            MOVL    P, R0
                        CLRL    (R0)

                        (Actually, for this case there is an addressing mode
                         that would allow the above to be done by a single
                         instruction.  We will learn this one shortly.)

   F. Of course, we can combine these representations to build more complex
      structures.

      Example:

      Pascal:   type
                    student = record
                        id: integer;
                        name: packed array[1..12] of char;
                        gpa: real
                    end;

                var
                    class_list: array[1..100] of ^student;

                ...

                        class_list[i]^.gpa := 0.0;

     MACRO:     CLASS_LIST:     .BLKA   100

                ...

                                MOVAL   CLASS_LIST-4, R0
                                MULL3   #4, I, R1
                                ADDL2   R1, R0
                                MOVL    (R0), R0
                                ADDL2   #16, R0
                                CLRF    (R0)

II. VAX Addressing modes
--  --- ---------- -----

   A. In discussing structured data types, we saw the need for a new addressing
      mode, called REGISTER-DEFERRED.  We now turn to a thorough study of the
      various addressing modes.  Many will turn out to be useful in dealing
      with various sorts of manipulations on structured data types.

   B. Recall the general format of a VAX operand specifier, used for all
      instructions EXCEPT branches:

        _______________
        | mode | reg. |       (sometimes followed by 1 or more additional bytes)
        ---------------

      1. With a 4 bit mode specifier, 16 modes are possible.

      2. Each of these modes can, in principle, be used with any of the 16
         general registers.  However, two registers are special when used as
         the register in an operand specifier.

         R14 = SP (stack pointer)
         R15 = PC (program counter)

      3. Therefore, we will first discuss the meaning of the different mode
         specifiers when used with a general register (R0 .. R13), and then
         when used with the PC and SP.

      4. We will discover that the special modes we have learned with the PC
         turn out to be direct results of the general behavior of these modes.

   C. TRANSPARENCY OF PROGRAMMING CARD MATERIAL ON MODES - GO OVER COLUMNS

III. Addressing Modes for use with General Registers (R0 .. R13)
---  ---------- ----- --- --- ---- ------- --------- -----------

   A. We have already met REGISTER MODE

      1. Mode = 5; any register except PC.  (The result of using this mode
         with the PC is unpredictable; this is because most VAX processors do
         some amount of instruction lookahead.)

      2. Assembler mnemonic: Rn         Example: R5

      3. Uses:

         a. Registers are often used for local/temporary variables, because
            access to a data item in a register is much faster than access
            to an item in memory.

            i. There is no direct analogue to this in HLL programs.  However,
               most good compilers will use registers for the most active
               variables in a program, to improve program speed.

           ii. This is one reason why you need to specify /NOOPT with the /DEBUG
               qualifier on Pascal programs; otherwise, the compiler will 
               generally put some of your local variables in registers where 
               they will not be accessible to commands like EXAMINE and DEPOSIT.

         b. This mode is also used for loading a register in preparation for
            its use with one of the other modes.

   B. We have likewise already met REGISTER-DEFERRED MODE.

      1. Mode = 6; any register except PC.  (The result of using this mode
         with the PC is unpredictable; this is because most VAX processors do
         some amount of instruction lookahead.)

      2. Assembler mnemonic: (Rn)               Example: (R5)

      3. Uses: This mode can be used whenever the address of an operand must
         be calculated at run-time - e.g.

         a. Address of an array element when the subscript contains variables.

         b. Dereferencing a pointer variable.

      4. Note carefully the difference between this mode and register mode.
         The following example may help:

                type
                    nodeptr = ^node;
                    node = record
                        info: integer;
                        link: nodeptr
                    end;

                var
                    p,q: nodeptr;       (* ASSUME THE COMPILER OPTIMIZES BY
                                           PLACING P IN R2 AND Q IN R3 *)
                ....

                p := q;                 MOVL    R3, R2
         vs
                p^.info := q^.info;     MOVL    (R3), (R2)

   C. AUTOINCREMENT MODE

      1. Mode = 8; any register INCLUDING THE PC.  (We will discuss use with
         the PC separately later.)

      2. Assembler mnemonic: (Rn)+      Example: (R5)+

      3. Meaning: Same as Register-Deferred, except that AFTER the operand
         address is calculated, the register is incremented by the size
         of the operand.

         Example:       Suppose R0 contains 1000

                        Following       MOVL    (R0)+, R1

                        R1 will contain the contents of memory location 1000
                        R0 will contain 1004

                        Note: If the MOVL moved some other size operand, the
                              value in R0 would differ as follows

                        Operation               R0 final value

                        MOVB                    1001
                        MOVW                    1002
                        MOVL, MOVF              1004
                        MOVQ, MOVD, MOVG        1008
                        MOVO, MOVH              1010

      4. Uses

         a. Step systematically through any kind of table

            Example:    TABLE:  .BLKL   100

            Sum up all elements of table and put sum in R2:

                                MOVAL   TABLE, R0
                                MOVL    #100, R1        ; Counter
                                CLRL    R2
                        LOOP:   ADDL2   (R0)+, R2
                                SOBGTR  R1, LOOP

         b. Special uses with SP, PC to be covered later

   D. AUTODECREMENT MODE

      1. Mode = 7; any register except the PC.  (The result of using this mode
         with the PC is unpredictable; this is because most VAX processors do
         some amount of instruction lookahead.)

      2. Assembler mnemonic: -(Rn)      Example: -(R5)

      3. Meaning: Same as Register-Deferred, except that BEFORE the operand
         address is calculated, the register is decremented by the size
         of the operand.

         NOTE WELL: With autodecrement, the register is decremented BEFORE the
                    operand address is calculated; with autoincrement, it is
                    incremented AFTER the address is calculated.

         Example:       Suppose R0 contains 1000

                        Following       MOVL    -(R0), R1

                        R1 will contain the contents of memory location FFC
                        R0 will contain FFC

                        Note: If the MOVL moved some other size operand, the
                              address accessed and the value in R0 would 
                              differ as follows

                        Operation               Item accessed / R0 final value

                        MOVB                    FFF
                        MOVW                    FFE
                        MOVL, MOVF              FFC
                        MOVQ, MOVD, MOVG        FF8
                        MOVO, MOVH              FF0

      4. Uses

         a. Step systematically through any kind of table, going backwards

            Example:    TABLE:  .BLKL   100

            Find value of LAST non-zero element of table and put in R2:

                                MOVAL   TABLE+400, R0
                                MOVL    #100, R1        ; Counter
                        LOOP:   MOVL    -(R0), R2
                                BNEQ    FINI
                                SOBGTR  R1, LOOP
                        FINI:   

         b. Special use with SP to be covered later

   E. AUTOINCREMENT DEFERRED MODE

      1. Mode = 9; any register INCLUDING THE PC.  (We will discuss use with
         the PC separately later.)

      2. Assembler mnemonic: @(Rn)+     Example: @(R5)+

      3. Meaning: The register contents is taken as a memory address, and the 
         contents of that location in memory is taken as the address of the 
         operand.  The register is then incremented by 4 (the size of an 
         address).  Note that TWO LEVELS OF INDIRECT ADDRESSING are involved.

         Example:       Suppose R0 contains 1000, and memory location 1000
                        contains 2000

                        Following       MOVL    @(R0)+, R1

                        R1 will contain the contents of memory location 2000
                        R0 will contain 1004

                        Note: If the MOVL moved some other size operand, the
                              value in R0 would still be the same

      4. Uses

         a. Step systematically through the objects referenced by a table of 
            POINTERS

            Example:    The FORTRAN library includes a built in function called
                        MAX which takes any number of integers (up to 255) as 
                        arguments and returns the largest

                        e.g. MAX(2, 3) would return 3

                             MAX(1, 19, 12, 37, 16, 4) would return 37

                        On the VAX, procedures are called with the AP register
                        (R12) pointing to an argument list that has the
                        following format:

                        Number of arguments (0 .. 255)
                        First argument
                        Second argument
                        Third argument
                        ...

                        Each argument is a longword.  In the case of FORTRAN,
                        it is always the ADDRESS of the actual argument (a
                        pointer to it) - like a var parameter in Pascal.

                        The MAX function could be written like this.  The value
                        will be returned to the caller in R0, which is the
                        convention for function values on the VAX.

                        .ENTRY  MAX, ^M<R2>
                        MOVL    (AP)+, R1       ; Number of arguments
                        BEQL    ERROR           ; Error - no arguments
                        MOVL    @(AP)+, R0      ; Assume first argument biggest
                        DECL    R1              ; Count this one off
                        BEQL    FINI            ; If only one, it must be max
                LOOP:   MOVL    @(AP)+, R2
                        CMPL    R2, R0
                        BLEQ    SMALL
                        MOVL    R2, R0
                SMALL:  SOBGTR  R1, LOOP
                FINI:   RET                     ; R0 contains maximum
                ERROR:  Something has to be done here

                Note: The first MOVL uses plain autoincrement, not
                      autoincrement deferred, because the argument count in
                      the list is an actual value, not a pointer to a value.

         b. Special use with PC to be covered later

   F. BYTE/WORD/LONGWORD DISPLACEMENT MODES

      1. Mode = A/C/E; any register INCLUDING THE PC.  (We will discuss use with
         the PC separately later.)

      2. Assembler mnemonic: B^displ(Rn)        Examples: B^5(R5)
                             W^displ(Rn)                  W^200(R5)
                             L^displ(Rn)                  L^120000(R5)

         Note: The B^, W^, or L^ may be omitted, in which case the assembler
               will choose the correct mode based on the size of the
               displacement - e.g.

                5(R5)           is equivalent to B^5(R5)
                120000(R5)      is equivalent to L^120000(R5)

      3. Meaning: The displacement value and contents of the register specified
         are added to form the address of the operand.  The register contents
         is unchanged.

      4. Uses: Many!

         a. For accessing array elements:

            Example:

            Pascal:     var
                            grades: array[1..100] of real;

                        ...

                            grades[i] := 0.0;

            MACRO:      GRADES:         .BLKF   100
                        ...
                                        MULL3   #4,I,R0
                                        CLRF    GRADES-4(R0)

            - Note how much shorter this is than using register-deferred mode

         b. For accessing components of a record structure pointed to by a
            pointer variable.

            Example:

            Pascal:     type
                            student_ptr = ^student;
                            student = record
                                id: integer;
                                gpa: real;
                                link: student_ptr
                            end;
                        var
                            p: student_ptr;
                        ...

                            p^.gpa := 0.0;
                            p := p^.link;

            Macro:      P:      .BLKA   1
                        ...
                                MOVL    P, R0
                                CLRF    4(R0)
                                MOVL    8(R0), P

         c. For accessing arguments of a procedure when passed by VALUE:

            Example:

            C:          int f(int i)
                          {
                            return i + 2;
                          }

                        /* Example is in C because VAX Pascal always passes
                           parameters by reference - even when they are value
                           parameters. */

            MACRO:              .ENTRY  F, 0
                                ADDL3   #2,4(AP),R0
                                RET

                        - Offset of 4, because first word of argument list (to
                          which AP points) is count of number of arguments.

         d. Special uses with SP, PC to be covered later

   G. BYTE/WORD/LONGWORD DISPLACEMENT DEFERRED MODES

      1. Mode = B/D/F; any register INCLUDING THE PC.  (We will discuss use with
         the PC separately later.)

      2. Assembler mnemonic: @B^displ(Rn)       Examples: @B^5(R5)
                             @W^displ(Rn)                 @W^200(R5)
                             @L^displ(Rn)                 @L^120000(R5)

         Note: The B^, W^, or L^ may be omitted, in which case the assembler
               will choose the correct mode based on the size of the
               displacement - e.g.

                @5(R5)          is equivalent to @B^5(R5)
                @120000(R5)     is equivalent to @L^120000(R5)

      3. Meaning: The displacement value and contents of the register specified
         are added to form the address of a memory location.  The contents of
         this location is then taken as the address of the operand.  The 
         register contents is unchanged.

      4. Uses:

         a. For accessing objects through an array of pointers

            Example:

            Pascal:     type
                            nodeptr = ^node;
                            node = record
                                info: integer;
                                link: nodeptr
                            end;

                        var
                            nodes: array[1..100] of nodeptr;

                        ...

                            nodes[i]^.info := 0;


            MACRO:      NODES:          .BLKA   100
                        ...
                                        MULL3   #4,I,R0
                                        CLRL    @NODES-4(R0)

            - Note this only works when the field we want is at the beginning
              of the object pointed to by the pointer.

         b. For accessing the item pointed to by a pointer which is itself a
            component of a record structure pointed to by a pointer variable.

            Example:

            Pascal:     type nodeptr, node as above

                        var
                            p: nodeptr;
                        ...

                            p^.link^.info := 0;

            Macro:      P:      .BLKA   1
                        ...
                                MOVL    P, R0
                                CLRL    @4(R0)

            - Note this only works when the field we want is at the beginning
              of the object pointed to by the second pointer.

         c. For accessing arguments of a procedure when passed by REFERENCE:

            Example:

            Pascal:     function f(var i: integer): integer;
                          begin
                            f := i + 2
                          end;

                        (* Note: in VAX Pascal, parameters are passed by
                           reference even if non-var; but var parameters are
                           ALWAYS passed by reference. *)

            MACRO:              .ENTRY  F, 0
                                ADDL3   #2,@4(AP),R0
                                RET

                        - Offset of 4, because first word of argument list (to
                          which AP points) is count of number of arguments.

         d. Special use PC to be covered later

   H. LITERAL MODE

      1. We have already seen that the VAX has an immediate mode that allows
         an operand to be embedded within an instruction.  One problem is that
         immediate mode consumes a fair amount of space in an instruction.

         Example: An immediate mode longword operand requires 5 bytes: 1 for
                  the operand specifier (8F) and four for the value.

      2. Many times, immediate operands are small numbers - e.g. 2, 3, etc.
         For such operands, the VAX provides a shorter way to include them
         in an instruction.  This option is available for:

         a. Integer operands in the range 0 .. 63

         b. Floating point operands that can be represented with a 3 bit
            exponent and 3 bit mantissa - e.g.

            i. One of the following true exponents:  0 .. 7

           ii. One of the following mantissas: (binary)

               .1000 = 1/2              Note: The leading 1 is the hidden bit,
               .1001 = 9/16                   so only three bits are needed
               .1010 = 5/8                    to store the mantissa
               .1011 = 11/16
               .1100 = 3/4
               .1101 = 13/16
               .1110 = 7/8
               .1111 = 15/16

          iii. Complete set of possible floating values: TRANSPARENCY P. 68

      3. The addressing modes that provide this feature are called the
         LITERAL MODES.  In brief, when the high-order two bits of the
         operand specifier are 00, the remaining 6 bits are taken as a short
         integer or floating point value (depending on the type of instruction
         being executed.)

         a. This corresponds to "modes" 0, 1, 2, 3 - i.e. the leftmost two
            bits of the mode field are 00 and the rightmost two bits are
            part of the literal value.

         b. For these "modes" the 4 bits used for the register field form
            part of the literal value.

         Example: The literal value 24 (Hex) would be encoded as "mode" 2
                  "register" 4 - i.e. 24

                  The literal value 0.75 (decimal) would be encoded as

                        00 000 100

                  which looks like "mode" 0, "register" 4

      4. The assembler syntax is S^#value

         a. Examples:   MOVL    S^#^X24, R0

                        MOVF    S^#0.75, R0

         b. When the syntax #value is used, the assembler chooses between
            immediate and literal mode, preferring literal mode wherever
            possible - e.g.

                        MOVL    #3, R0  -- would be translated as if S^#3

                        MOVL    #100, R0 -- would be translated as if I^#100

   I. INDEX MODE

      1. The final mode we need to consider is mode 4.  It is called INDEX MODE.

         a. When an operand specifier calls for mode 4, the register it
            specifies is called the INDEX REGISTER.

         b. Unlike all other modes, this one never stands by itself - it is 
            always used in conjunction with some other mode, called the BASE
            MODE.

         c. The assembly language syntax is to enclose the index register
            in square brackets FOLLOWING the base mode - e.g. 

                (R0)[R5]        - would specify base mode register deferred
                                  with R0, plus index register R5

                TABLE[R5]       - would specify base mode relative, plus index
                                  register R5

         d. In the machine language code, the mode 4 / index register specifier
            comes FIRST - before the base mode operands specifier, and
            must be immediately followed by the base mode operand specifier.  
            This base mode operand specifier - plus any additional bytes it 
            needs - is used to calculate an address in the ordinary way.  Then 
            the contents of the index register is multiplied by the operand 
            size, and the result is added to the address calculated previously 
            to form the operand address.

            Example:    Suppose we have the following instruction

                        60 41 D4        CLRW    (R0)[R1]

                        Suppose further that R0 contains 1000 (hex) and R1
                        contains 3.

                        The address of the location to be cleared is calculated
                        as follows: the base address specifier specifies 1000.
                        To this is added the operand size (word - 2) times the
                        contents of the index register (3) to yield 1006 as
                        the address of the word to clear.

            NOTE WELL: The base mode specifier comes BEFORE the index
                       specifier in the assembly language code, but AFTER it
                       in the machine language code.

         e. Some special rules apply to this mode

            i. It must only be used with base address specifiers that specify
               a memory address - e.g. it cannot be used with register mode,
               literal mode, or immediate mode.  (The result of doing so is
               either a fault or an unpredicatable result.)

           ii. If the base mode modifies its register in some way (e.g.
               autoincrement, autodecrement, autoincrement deferred), then
               the usual modification to the base register is done.  

          iii. It is not usually meaningful to use the same register as both
               an index register and part of the base mode.  If this is done,
               and the base mode modifies its register, then the results are
               unpredictable.

      2. The primary use for this mode is for array subscripting, provided
         the array elements are of some standard hardware type (such as
         longword.)

         a. Example:

            Pascal:     var
                            grades: array[1..100] of real;

                        ...

                            grades[i] := 0;

            MACRO:              MOVL    I,R0
                                CLRF    GRADES-4[R0]

         b. Example:

            Pascal:     type
                            student = record
                                gpa: real
                                id: integer;
                                name: packed array[1..12] of char;
                            end;

                        var
                            stu_array: array[1..100] of student;

                        ...

                            student[i].gpa := 0.0;

            MACRO:      Cannot be done using index mode - size of an array
                        element (20) is not a standard hardware size, and
                        is not the size of the item being cleared anyway.
                        We must use displacement mode as in previous examples.

IV. Addressing modes for use with SP
--  ---------- ----- --- --- ---- --

   A. As we noted in our introduction to the architecture of the VAX, the
      architecture divides memory into four major regions:

                ------------------------- 
                |                       | 00000000
                |       P0 space        |
                |                       | 3FFFFFFF
                |-----------------------+---------
                |                       | 40000000
                |       P1 space        |
                |                       | 7FFFFFFF
                |-----------------------+---------
                |                       | 80000000
                |       System space    |
                |                       | BFFFFFFF
                |-----------------------+---------
                |                       | C0000000
                |       (Reserved)      |
                |                       | FFFFFFFF
                -------------------------

      1. The code and global data for each process resides in its P0 space.

      2. P1 space is used for a process's stack, which grows from high
         addresses toward low.  Processor register 14 (SP) always points
         to the current top location on the stack.

         a. To push a new item on the stack, it is necessary to SUBTRACT the
            item's size from SP.

         b. To pop an item from the stack, it is necessary to ADD the item's
            size to SP.

   B. We have noted that all of the addressing modes can be used with the
      stack pointer register.  Four have special uses:

      1. Access top item on stack - use register deferred on SP         (SP)

         Example: If the top item on the stack is an integer, then to copy
                  it into R2 (leaving it also on the stack) we would write:

                        MOVL    (SP), R2

      2. Pop an item from stack - use autoincrement with SP             (SP)+

         Example: If the top item on the stack is an integer, to pop
                  it off the stack and into R2, we would write:

                        MOVL    (SP)+, R2

      3. Push an item on stack - use autodecrement with SP              -(SP)

         a. Example: To push the real number X on the stack, we could write:

                        MOVF    X, -(SP)

         b. For reasons of efficiency, it is the convention to always push
            items on the stack whose size is one or more longwords.  Thus,
            to push a byte (say in R0) we would normally use:

                        CVTBL   R0, -(SP)
            or
                        MOVZBL  R0, -(SP)

            (though simply MOVB R0, -(SP) would be legal.)

            Of course, for items larger than a longword we can just push
            them as is - e.g. to push an H-float:

                        MOVH    R0, -(SP)

         c. Actually, because pushing is done so often we can often use
            the special PUSHL instruction instead - e.g. to push the real X:

                        PUSHL   X

            (But note that this special instruction is only available for
             longwords.)

      4. One important use of the stack is to create working space for the
         local variables of a procedure.  This can be done by subtracting
         a constant from SP and then referencing the variables by using
         displacement mode on SP:

         a. Example:

            Pascal:     procedure foo;

                        var
                           i, j: integer;

                        ...

                           i := j+1;

            MACRO:      .ENTRY  FOO, ^M<>
                        SUBL    #8, SP
                        ...
                        ADDL3   #1, 4(SP), (SP)

                        (Note: We used register deferred for i because the
                         displacement would be 0 - i.e.

                         (SP) is equivalent to 0(SP) but one byte shorter.

         b. In conjunction with our lectures on procedures, we will see that
            another register called the FRAME POINTER (R13 = FP) is always
            used to point to the base address of working space for a procedure.
            Thus, the above instruction might also be coded as:

                        ADDL3   #1, -8(FP), -4(FP)

      5. Finally, we note that it is possible to use the hardware stack to
         directly handle 0-address (stack machine) code.

         Example:

         Pascal:                A := B + C * D - E

         Stack machine code:    PUSH    B
                                PUSH    C
                                PUSH    D
                                MUL
                                ADD
                                PUSH    E
                                SUB
                                POP     A

         VAX equivalent:        PUSHL   B
                                PUSHL   C
                                PUSHL   D
                                MULL2   (SP)+, (SP)
                                ADDL2   (SP)+, (SP)
                                PUSHL   E
                                SUBL2   (SP)+, (SP)
                                MOVL    (SP)+, A

   C. We have discussed the use of a stack whose top is pointed to by the SP
      register. 

      1. This stack is automatically set up by the operating system when a 
         program is entered, and is used by hardware instructions like BSBB,
         BSBW, JSB, RSB, CALLS, CALLG, and RET.

      2. It is possible for a program to create another stack (anywhere in
         its memory) by using another register as a stack pointer.

         Example: We saw in CS122 that one way to reverse the order of
                  the characters in a string would be to push them onto a
                  stack and then pop them back off.  This could be done
                  in MACRO as follows:

                        THE_STRING:     .BLKB   100
                        MY_STACK:       .BLKB   100
                                        ...
                                        MOVAB   THE_STRING, R0
                                        MOVAB   MY_STACK, R1
                                        MOVL    #100, R2
                        LOOP1:          MOVB    (R0)+, -(R1)
                                        SOBGTR  R2, LOOP1
                                        MOVAB   THE_STRING, R0
                                        MOVL    #100, R2
                                        MOVB    (R1)+, (R0)+
                                        SOBGTR  R2, LOOP1

V. Addressing modes for use with PC
-  ---------- ----- --- --- ---- --

   A. Several of the addressing modes produce some interesting and important
      effects when used with the PC.  These effects come about as a direct,
      natural result of the definitions of the modes.  We are now able to
      see why these modes work the way they do.

   B. IMMEDIATE MODE            Mode = 8; register = PC (F)

      1. This is actually autoincrement on the PC.

      2. To see how it works, consider the following example:

         MACRO:                 MOVW    #^X1234, R0

         Machine language:      50 1234 8F B0

         - When the first operand specifier (8F) is fetched, the PC is
           advanced to point to just after it - i.e. to the value 1234.

         - This value, then, is the operand, since it is what register F (the 
           PC) now points to.

         - Register F is incremented by the size of a word 2.  This makes 
           register F (the PC) point to the next operand specifier (50),
           which is what we now want to continue instruction execution.

   C. ABSOLUTE MODE             Mode = 9; register = PC (F)

      1. This is actually autoincrement-deferred on the PC.

      2. To see how it works, consider the following example:

         MACRO:                 MOVW    @#^X1234, R0

         Machine language:      50 00001234 9F B0

         - When the first operand specifier (9F) is fetched, the PC is
           advanced to point to just after it - i.e. to the address 00001234.

         - This value, then, is the address of operand, since it is what 
           register F (the PC) now points to.

         - Register F is incremented by the size of an address (4).  This makes 
           register F (the PC) point to the next operand specifier (50),
           which is what we now want to continue instruction execution.

   D. BYTE, WORD, and LONG RELATIVE MODES    Mode = A, C, E; register = PC (F)

      1. These are actually byte, word, or long displacement mode on the PC.

      2. To see how it works, consider the following example.  Assume
         that the instruction begins at address 1000, and that TABLE is
         at 2000.

         MACRO:                 MOVL    TABLE, R0

         Machine language:      50 0FFFC CF D0

         - When the first operand specifier (CF) is fetched, the PC is
           advanced to point to just after it - i.e. to the displacement 0FFFC.

         - Since the mode is word displacement, this word value is fetched,
           and the PC is advanced to just after it - i.e. to 1004.

         - The displacement is added to register F (the PC) to yield the
           operand address 2000.

         - Register F (the PC) now points to the next operand specifier (50),
           which is what we now want to continue instruction execution.

   E. BYTE, WORD, and LONG RELATIVE DEFERRED MODES    

                        Mode = B, D, F; register = PC (F)

      1. These are actually byte, word, or long displacement-deferred mode on 
         the PC.

      2. These work just like relative, except that the calculated address is
         used as the address of the address of the operand, not as the address
         of the operand.

VI. Summary of VAX Addressing Modes
--  ------- -- --- ---------- -----

   A. TRANSPARENCY/HANDOUT showing all modes

   B. TRANSPARENCY/HANDOUT showing translation of various Pascal constructs

   C. Go over programming card TRANSPARENCY again.

VII. MIPS Addressing Modes
---  ---- ---------- -----

   A. We have seen that the VAX has a very rich and powerful set of
      addressing modes, which can be used with practically any instruction.
      (The one exception is the branch instructions, which always use
      relative addressing.)

   B. In contrast, MIPS has a very small set of addressing modes, and
      only certain modes can be used with any given instruction.

      1. Computational instructions (add etc) always use register mode
         for the destination and one of the sources.  The second source
         can use either register mode or immediate mode (e.g. add uses
         register mode for all three operands; addi uses immediate mode
         for the second source operand and register mode for the other
         two operands.)

      2. Memory reference instructions (load, store) use displacement mode.
         The instruction has the following format:

         op (6) | rs (5) | rt (5) | displacemenT (16)

         The address of the operand is calculated by adding the offset to
         rs.  rt is the register to load/store, as the case may be.

         ASK: Which VAX address mode does this most closely corrrespond to?

         ANS: word displacement - mode C

      3. Jump/branch instructions use relative mode - except jr uses
         register mode.

   C. Despite this simplicity, most addressing modes on the VAX can be
      synthesized by one or more MIPS instructions.

      1. We have already seen that the MIPS equivalent of a VAX instruction
         that does computation on values in memory is to load the operands
         into registers, perform the computation, and then store the
         result back to memory.

      2. We now consider how various VAX addressing modes can be synthesized
         for the MIPS load/store instructions.

         a. Register mode - use the add instruction with $0 as the second
            source.

         b. Register deferred mode

            ASK

            Use displacement mode with a displacement of 0

         c. Autodecrement mode

            ASK

            First use addi to add an appropriate negative value (-1, -2, -4,
            etc.) to the register, then same as register deferred

         d. Autoincrement mode

            ASK

            Same as register mode, then use addi to add an appropriate value
            (1, 2, 4, etc.) to the register.

         e. Autoincrement-deferred

            ASK

            Example:    VAX:    movl    @(r2)+, r3

                        MIPS:   lw      $1, 0($2)  ; $1 = address of operand
                                lw      $3, 0($1)
                                addi    $2, $2, 4
                                
         f. Displacement - trivial

         g. Displacement-deferred

            ASK

            Use same strategy as above

         h. Immediate - use addi with $0 as the first source.

         i. Absolute - load the address into a temporary register, then
            same as register deferred.  (Note VAX mnemonic - @#!)

         j. Relative - No MIPS equivalent.  (Other RISCS have a relative
            mode version of the load/store instructions with a different
            format)

Copyright ©1999 - Russell C. Bjork