![]() |
Previous | Table Of Contents | Next | ![]() |
Indirect Addressing
We have already studied the first of the two methods for accessing the data memory of the RPN calculator: direct addressing, which has the limitation that it can only reach the first 20 of the 30 available data memory locations. You might guess that the other alternative, indirect addressing, was invented to solve this problem. But although indirect addressing does allow you to reach all 30 data memory locations, it was invented to solve a different problem.
The only RPN calculator statements that can employ indirect addressing are:
The i keystroke can be found here:
.
Clearly the letter i is used to signify an indirect address. But what's the
address? You (and the calculator) can only learn it indirectly.
The statement STO 2 tells the calculator
to store the contents of the X stack location to register (data memory address) 2.
The statement STO i tells the calculator to get a data memory address out of
register 0 and then to store the contents of the X stack location at this location
in data memory. So the statement STO i gives no clue as to the destination
address, it all depends upon the value currently in register 0. Register 0
is used in exactly the same way for the other statements such as RCL i.
Register 0 is the only data memory location that can hold an indirect address.
If you don't need to employ indirect addressing then you can treat register 0
the same as any other register.
Since we can put any value from 0 through 29 into register 0, we can use the STO i and RCL i statements to reach any data memory location. But obviously this is more work than the direct addressing alternative. So why bother?
The answer becomes apparent if we try to write a program that will populate all of data memory with a particular value, say 7. We know that the keystroke sequence 7, STO 0 will place the value 7 into data memory location 0. So one way to write this program is:
This obviously leaves a lot to be desired. We would prefer to have a single STO statement inside a loop that iterates (that is, repeats) 30 times.
I have only shown you infinite loops so you don't yet know how to make a loop repeat only 30 times. But ignore that for a moment and just imagine that we had the following attempt at this program:
Each time through this loop we need the STO statement to write to a different data memory address. This is obviously going to be impossible if the address is hard coded in the STO statement. We need a STO statement that allows the address to keep changing even though the statement doesn't. This is the problem that led to the invention of indirect addressing. The statement we place inside the loop is STO i which refers to an address specified elsewhere: specifically, by the contents of register #0.
Then we just need to cause register #0 to hold a different value during each iteration of the loop. Turns out the RPN calculator offers an instruction just for this purpose: DSZ which stands for "Decrement and Skip if Zero". The DSZ statement is our first example of a conditional statement which can make a decision based upon a condition (event) observed at run-time, which is the moment when that statement is reached while the program is executing.
The condition that the DSZ instruction watches for is whether register 0 holds the value 0. And it checks this condition AFTER decrementing register 0. If, after the decrement, register 0 does hold 0 then the DSZ statement skips the next statement. But if register 0 does not hold 0 then the DSZ statement does not skip the next statement. When you put a GTO statement immediately after a DSZ statement you create a conditional branch. This is what we see in our next example program which employs both indirect addressing and the DSZ statement. Note that since indirect addressing can only employ register 0, register 0 is sometimes called the i register.
Single-step this program (it requires no passed parameters) and watch it populate every data memory location. The flow chart for this program is shown below. Note that the diamond shape is traditionally used to mark each spot in a program where a condition is evaluated. And remember that the condition is NOT evaluated in the time frame that you write the program (this is called compile-time) but rather in the time frame when the program is later run (this is called run-time).
Let's look at another example program. Imagine that we want to write a program which will populate register 0 with the value 0 squared, register 1 with the value 1 squared, register 2 with the value 2 squared, etc. up to register 29 with the value 29 squared. We organize our program around a loop which iterates (is repeated) 30 times. We will again employ the DSZ instruction to provide the conditional branch which twenty-nine times jumps back to the beginning of the loop and then after that exits from the loop. Because DSZ decrements the contents of register 0, it simplifies the program to perform the work in reverse; that is, the first iteration of the loop should compute 29 squared and store this in register 29. We can then exit from the loop when register 0 has counted down to 0. The program which accomplishes this is shown below.
You run this program in the normal manner using GSB 0 (it has no passed parameters that you must first initialize). When the program halts you can quickly inspect the contents of all the registers by clicking on SST to convince the Registers window to open.
There is a bit of serendipity in the program. Consider the following code fragment:
This code fragment initializes the indirection register (register 0) with the value 1 and then enters a loop controlled by the DSZ statement. DSZ decrements register 0 and then tests if the new value is 0. Since register 0 was initialized with 1, the very first time this code fragment executes the DSZ instruction register 0 will become 0 and hence the GTO 1 statement will be skipped. This means that the loop has iterated once. If we had initialized register 0 with the value 2 then the loop would have iterated twice, meaning the body of the loop would have been executed twice (the GTO 1 branch would have been taken once).
So in our ThirtySquares.txt example program the body of the loop is executed 29 times, since we initialize register 0 with the value 29 before entering the loop. But our program needs to compute 30 squares, not 29. What saves us is the fact that 0 squared is still 0. So even though we never execute the x2 statement for the value 0, register 0 still ends up holding the correct square.
Now that we know about conditional branches we can offer a final version of the SecondOrderEq series of example programs. The SecondOrderEq7.txt program shown below will compute and plot exactly 100 (x,y) data points and then halt. The first data point uses whatever x axis value you placed into the X stack location before initiating the program and all subsequent x axis values are spaced apart by 1.
I would suggest you start this program via -50, GSB 0 since then the 100 (x,y) data points will be centered on the y axis. When comparing SecondOrderEq7.txt to the previous SecondOrderEq6.txt you will notice that I had to make a change and employ register #1 rather than register #0 to store the sequence of x axis values. This is because the DSZ instruction employs (that is, affects) register 0.
Indirect addressing is a big deal. All computers offer it. Even a simple microprocessor like the Intel 8051 that we will study next offers 4 different mechanisms (called addressing modes) for specifying a desired data memory location. More advanced microprocessors such as the Intel Pentium III that might be in the computer in front of you offer 9 different addressing modes. High-level languages such as C and C++ wrap the use of indirect addressing in a concept called pointers which are data memory locations that point to (that is, hold the addresses of) other data memory locations.
![]() |
Previous | Table Of Contents | Next | ![]() |