Section 9.8
Changeable parameters implemented by pointers

In some languages such as Pascal, Ada and C++, you can specify that some parameters can be changed inside the subroutine and those changes will "stick" when control returns to the caller. Others, like C and Java, do not permit parameters to be changeable. For example, the following program is legal C but does not do what you expect:

void increment (int x) {                      // This is legal C
     x = x + 1;   // similar to the x++ operation
}
int main() {
     int z = 5;
     increment(z);
     printf ("%d", z);   //   shows 5, not 6
}
The reason is that the parameter x in the function increment is a simple int and C does not permit int parameters to be changeable, i.e. to reflect back in their caller any changes to those parameters. We say parameters in C are call by value.

Just to contrast for you, the following C++ program does permit its int parameter to be changeable. Note the additional & symbol after int in the parameter declaration:

void increment (int& x) {               // This is legal C++
     x = x + 1;   // similar to the x++ operation
}
int main() {
     int z = 5;
     increment(z);
     cout << z;   //   shows 6
}

cout << z; is the usual way of writing an output statement in C++, though printf can still be used.

C++ arranges for changeable parameters to push changes to them back to the caller. C forces you to do this explicitly through pointers. Though C++ uses pointers, too, it does so behind the scenes.

The next program, containing a short procedure, called augment, illustrates how C does changeable parameters, or rather how it lets you, the programmer, simulate changeable parameters through the use of pointers. augment() has two parameters but no return value because it changes the values of the variables linked to those parameters.

void augment (int *parm1, int parm2)
{
     *parm1 += parm2;
}

int main()
{
     int k=15, l=20, z=-4, w=6;
     augment (&k, l);   /* after this k is changed to be k+l */
     augment (&z, w);  /* same idea */
}

Here's the CSC-1 assembler code in 2-column format to save space

   0:   LOD   K               15:  PARM1:   NUM   0
   1:   STD   PARM1           16:  PARM2:   NUM   0
   2:   LOD   L               17:  K:       NUM   15
   3:   STD   PARM2           18:  L:       NUM   20
   4:   CAL   AUGMENT         19:  Z:       NUM   -4
   5:   LOD   PARM1           20:  W:       NUM   6
   6:   STD   K               21:  AUGMENT: LOD   PARM1
   7:   LOD   Z               22:           ADD   PARM2
   8:   STD   PARM1           23:           STD   PARM1
   9:   LOD   W               24:           RET
  10:   STD   PARM2
  11:   CAL   AUGMENT
  12:   LOD   PARM1
  13:   STD   Z
  14:   HLT

By the way, this code breaks one of the cardinal rules of assembler programming, which is that comments must exist to explain what is going on in the code.

The way parameters are handled in the assembler subroutine augment is called call by value/result or call by copy-back. It is not exactly equivalent to the C program shown that uses pointers. However, as long as you don't look at the values of the parameters and their referents until the subroutine returns, the two methods appear identical.

If changeable parameters are implemented by pointers, then any changes to the parameters are reflected back to the calling routine's variables immediately. The copy back strategy above is not needed, since the parameters are really just aliases for the calling routine's variables. Let's see what this would look like in assembler.

First the call would look different since we would get the address of the calling routine's variable and store it into the parameter. Using the example augment above,

     augment (&k, l);   /* after this k is changed to be k+l */

we would do the following:

     LDI   K
     STD   PARM1     ;  K is changeable, so get its address
     LOD   L
     STD   PARM2     ;  L is simply call by value, not changeable

Now in the augment subroutine itself, we would see the following:

void augment (int *parm1, int parm2) {
     int temp = *parm1 + parm2;
     *parm1 = temp;
}

The original code has been expanded and temporaries have been inserted to clarify the steps. To get the value of *parm1, we do the following:

     LOD   PARM1     ; has the address of the true parameter
     A2S
     LDS             ; A register now has current value of *parm1

     ADD   PARM2
     STD   TEMP

To stash the value back into *parm1, we need to use indirect store:

     LOD   PARM1     ; has the address of the true parameter
     A2S
     LOD   TEMP      ; what will get stored into *parm1
     STS             ; go ahead and store, using S's value as address

Putting all these lines together is how we would encode the original statement:

               ;  *parm1 += parm2;
     LOD   PARM1     ; has the address of the true parameter
     A2S
     LDS             ; A register now has current value of *parm1
     ADD   PARM2
     STD   TEMP
     LOD   PARM1     ; has the address of the true parameter
     A2S
     LOD   TEMP      ; what will get stored into *parm1
     STS             ; go ahead and store, using S's value as address

If you don't have an appreciation for the tremendous work that compilers and high level languages do for you, then you a hopeless dolt!