( 32 )
Previous Table Of Contents

About The Simulator

My RPN Calculator is a Windows program that was written in C++ using Microsoft's WIN32 API. The program (14,500 C++ statements), these help pages (33,000 words), and the example calculator programs were written over a period of a few months in 2001 by yours truly, John Kopplin, in Boise, Idaho. This program is compatible with Windows 95, Windows 98, Windows ME, Windows NT 4, Windows 2000, and Windows XP. It should run acceptably on any PC no matter its age as long as it contains one of these operating systems.

I wrote this Windows program to teach programming to beginners. It is the first component in my curriculum which proceeds next to assembly language via another one of my programs that simulates an 8051 microprocessor. It is easy for beginners to feel overwhelmed by modern programming languages and computer jargon and the RPN calculator allows them to start writing programs without ever having heard of bits, bytes, assemblers, compilers, syntax errors, etc. After learning assembly language, my curriculum concludes by teaching the high-level C++ language.

My RPN Calculator most closely resembles the model HP-29C calculator that was sold by Hewlett-Packard in the late 1970's. The HP-29C calculator could not draw graphs, print programs, save programs on a disk drive, display all four levels of the stack, etc., etc. so a PC based simulation of a hand calculator actually has some significant advantages over the original.

Acknowledgements

I would like to thank David G. Hicks who runs "The Museum of HP Calculators" which you can find on the Internet at the URL:

http://www.hpmuseum.org

David's site has lots of info and pictures on the history of HP's calculators. I used one of his photos as the basic backdrop for my RPN calculator.


Introduction

( 1 )
Next (Table Of Contents)

RPN Calculator

by John Kopplin

Copyright © 2002-05

Welcome to RPNCalc, a Windows program that simulates a programmable Reverse Polish Notation calculator such as those sold in the 1970's by Hewlett-Packard. You may find this calculator useful for balancing your checkbook, computing the amount of lumber you will need to make a deck, etc., but I wrote it in order to teach programming to beginners.

I use the RPN calculator to demonstrate fundamental programming concepts such as program memory, data memory, looping, single-stepping, subroutines, etc. When you have finished learning about the RPN calculator you will be ready for the next component of my curriculum, which is a simulation of Intel's 8051 microprocessor that I use to teach assembly language programming. And the final section of my curriculum introduces you to Windows programs and the king of programming languages, C++.

The help page that you are now viewing is merely the first of an extensive set of help pages that together represent a self-study guide that should allow you to learn to program on your own, at your own pace. To use these help pages as a self-study guide simply read them in the order they are listed on the Table Of Contents page. There is a hyperlink at the top and bottom of each page labeled "Next" which will take you to the next page in the order I intended. You will also see a page number in the upper right hand corner of each page to help you keep track of your progress. Of course I have also provided a keyword index and lots of other hyperlinks to help you navigate directly to particular topics. You can leave this help page window open while you interact with the calculator (and you probably want to make it bigger than its default size).

You have probably seen a Help system like this one already. It is built upon Microsoft's standard HTML Help technology. In the left-hand pane you can observe 4 tabs labeled Contents, Index, Search, and Favorites. The Contents tab simulates a book's table of contents while the Index tab simulates a book's index. The Search tab offers the feature that books would kill for. The Favorites tab is where you can record bookmarks on your favorite pages. These bookmarks make it easy to return to these spots later. One good use for a bookmark is to record the spot where you break from your studies so you can resume there later.

The home page for all of my software is www.computersciencelab.com For questions, you can reach me at support@computersciencelab.com. Enjoy!

Next (Table Of Contents)


( 2 )
Previous Table Of Contents Next

Table Of Contents

Introduction

Table of Contents (this page)

History of Calculators

Operating the RPN Calculator

Operating the Calculator

Reverse Polish Notation

Statistics

Least Squares Curve Fitting

Programming the RPN Calculator

Programming the Calculator

Executing a Program

Single Stepping a Program

Passing Arguments to a Program

Data Memory

Loops

Plots

Sinusoids and Spirographs

Indirect Addressing

Factorials

Subroutines

Debugging

Program Tracing

Successive Approximation

Review

Looking Ahead To Higher Level Languages

The Calculator's Instruction Set

Basic Calculator Operations

Math Operations

Memory Operations

Branch And Call Operations

Plot Operations

The Example Programs

The Windows Simulator

Simulator Options

About the Simulator


( 3 )

History of Calculators

If we define a calculator as a device that aids mathematical computation then the oldest calculator is certainly the human hand. Another calculator that has served mankind for thousands of years is the abacus. The oldest surviving abacus was used in 300 B.C. by the Babylonians. The abacus is still in use today, principally in the far east. A skilled abacus operator can work on addition and subtraction problems at the speed of a person equipped with an electronic hand calculator (multiplication and division are slower). Just like a hand, an abacus doesn't actually perform computations, instead its value is that it aids the memory of the human performing the calculation. While a modern abacus consists of rings that slide over rods, the older one pictured below dates from the time when pebbles were used for counting (the word "calculus" comes from the Latin word for pebble).

Although Leonardo da Vinci made drawings of gear-driven computing machines, he apparently never built any.

The first gear-driven calculating machine to actually be built was probably the calculating clock, so named by its inventor, the German professor Wilhelm Schickard, in 1623. This device got little publicity because Schickard died soon afterward in the bubonic plague.

The first mechanical calculator to be mass produced and offered for sale was the Pascaline, invented in 1642 by Blaise Pascal who, at age 19, wanted to ease the workload of his father who was a tax collector. Pascal built 50 of this gear-driven, one-function calculator (it could only add) but couldn't sell many because of their exorbitant cost.

Shown below is a 6 digit model for those who couldn't afford the 8 digit model. This Pascaline is opened up so you can observe the gears and cylinders which rotate to display the numerical result. Up until the present age when car dashboards went digital, the odometer portion of a car's speedometer used the very same mechanism as the Pascaline to increment the next wheel after each full revolution of the prior wheel.

Mechanical calculators continued to improve. If you worked as a bookkeeper in the early 1900's you probably used a Comptometer like the one shown below. Note that this device does not have a power cord.

The Comptometer [photo courtesy vintagecalculators.com]

Electronic computers began to supplant mechanical computers starting with the famous ENIAC which was built during World War II to compute the trajectories of shells fired from large guns such as those on battleships. Don't hunt for ENIAC in the following picture, as all of what you see (and more) is ENIAC.

You've no doubt heard of the micro-electronics revolution where semiconductor technology allowed electronic devices as large as ENIAC to be shrunk down to a few millimeters. Computers that had been as large as rooms started to appear on desktops and eventually in shirt pockets.

The Japanese companies Sharp, Canon, and Sanyo all began selling hand-held, four-function electronic calculators in 1971.

The Sharp EL8 [photo courtesy vintagecalculators.com]

In January of 1972 Hewlett-Packard introduced the HP-35, the first scientific handheld calculator that included transcendental functions like natural logarithm, sine, and cosine. It's initial list price was $395 and by 1973 slide rule manufacturers were declaring bankruptcy. The NASA engineers who managed to put a man on the moon in 1969 were still employing slide rules for much of their work.

Hewlett-Packard had previously sold desktop-sized calculators when Bill Hewlett decided that it was now feasible to manufacture a shirt-pocket sized unit. After Bill ordered his company to design such a unit his engineers showed up in his office to measure his shirt pocket to make sure they would get it right. Even though analysts had predicted the unit wouldn't sell, HP sold a hundred million in just the first year. Hand calculators were the "home computers" of the 1970s.

While ground-breaking, the HP35 wasn't programmable. In 1974 HP introduced the HP-65 which was the first ever programmable handheld calculator. This Windows program resembles another HP programmable calculator, the HP-29C, introduced in 1977 at a list price of $195.


Operating

( 4 )

Operating The Calculator

You can identify a Reverse Polish Notation (RPN) calculator because it lacks an Equals (=) key. How are you supposed to compute anything without an Equals key?

We all know how to employ a conventional calculator to compute, say, 2 times 3. Conventional calculator's are called algebraic calculators and you can find an algebraic calculator at Start/Programs/Accessories/Calculator in all versions of the Microsoft Windows operating system. We simply need to click the following keys:

On the algebraic calculator we get our answer when we click on the Equals key. That is, the multiplication is not performed when we press the Multiply key. Or is it? Try the following keystroke sequence on your favorite algebraic calculator:

The answer is 24, of course, but did you notice that this time a multiplication (the multiplication of 2 by 3) occurred when you pressed the Multiply key for the second time? Apparently, the Multiply keystroke may or may not cause a multiplication depending upon whether the two necessary operands are already available.

So the first advantage of an RPN calculator is consistency: a multiplication ALWAYS occurs when you hit the Multiply key. This means you must always enter both operands before hitting the Multiply key. This is the purpose of the ENTER key. Here is the keystroke sequence necessary to compute 2 * 3 using an RPN calculator:

Note that the Multiply key is employed only after both operands have been entered and that the multiplication result is computed as soon as the Multiply key is clicked.

I am now going to switch over to the actual button images used in this Windows program. Each of the buttons on our RPN calculator can perform up to 3 different functions, depending upon whether they are clicked without a prefix, with the [image] prefix (called the f key), or with the [image] prefix (called the g key). So the keystrokes necessary to compute 2 * 3 are:

Note that none of these four keystrokes required a prefix key since the functions we wanted were the ones printed on the center rather than the top or bottom edges of the keys.

Perhaps you are now wondering where the operands go when you "enter" them. This is another area where the RPN calculator outshines algebraic calculators. Consider the job of raising the number two to the third power on an algebraic calculator. This requires the [image] key or the [image] key depending upon your calculator (you won't see this key on the Microsoft calculator until you select View/Scientific from the menu bar). So evidently algebraic calculators have two storage locations called X and Y. But why aren't they mentioned with regard to any of the other operations that the algebraic calculator can perform? In truth, these same storage locations are employed for multiplication, division, and all other two operand operations.

The RPN calculator has a "y to the x" key ( [image] [image], note that it requires the f prefix) but more importantly the RPN calculator explicitly uses the same X and Y storage locations for ALL of its operations.

On the RPN calculator the X and Y storage locations are the first two locations in a four deep stack of operands. Want to see this stack? Just click and drag the On/Off slide switch over to the On position and the RPN calculator's LED display will change from:

to:

.

Now you can not only see the X and Y locations of the stack but also the other two locations, which are labeled Z and T (T for "top"). You are free to leave the On/Off slide switch in the On position thus causing the RPN calculator's stack to always be visible (the only downside is that the numbers are slightly smaller). Any time the On/Off slide switch is in the Off position you are viewing just the number in the X stack location.

To gain some familiarity with the operation of the stack, execute the following keystroke sequence while observing the stack:

After this keystroke sequence the four deep stack of the RPN calculator will look like:

but if you were careless and added a 4th ENTER keystroke then your stack will look like:

You have managed to enter four operands. Every time you type out a number it is placed into the X location of the operand stack. The ENTER key copies the number in the Z stack position into the T stack position (causing the prior contents of the T stack position to be lost), and copies the number in the Y stack position into the Z stack position, and copies the number in the X stack position into the Y stack position. This whole process is known as lifting the stack.

There are two other keys that directly affect the stack. The [image] key (called the Exchange key) interchanges the contents of the X and Y stack locations without affecting the contents of the Z or T stack locations. Go ahead, try it! Then experiment with the [image] key (called the Rolldown key). This key is sort of the reverse of the Enter key except that no numbers are lost: instead T is copied to Z, Z is copied to Y, Y is copied to X, and X is copied to T.

I said that these keys directly affect the stack. But other keystrokes indirectly affect the stack. For example, the Multiply ( [image]) keystroke causes the stack to drop. The stack drops because the two operands in the X and Y stack locations are both removed from the stack and the multiplication result is placed back into the stack in the X location. This is a net loss of one number from the stack and consequently Z drops (is copied) to Y and T drops (is copied) to Z with the result that the contents of the Z and T stack locations become identical.

This process will make sense after you watch the stack while performing the following keystroke sequence:

Note that you hit the Multiply key twice in a row! You can hit the Multiply key at any time because the stack will always be populated with 4 numbers and hence there will always be an X and Y operand available for the multiplication. In our example you can continue clicking on the Multiply key and the stack will not change. Do you see why?

Everything you have learned about the Multiply key also applies to every other two-operand key such as Add, Subtract, and Divide. All two-operand keystrokes take their two operands from the X and Y stack locations, place their result back in the X stack location, and drop the stack. Similarly, all one-operand keystrokes such as Reciprocal (1/x) and log (common logarithm) take their one operand from the X location of the stack and place their result back in the X stack location (the rest of the stack doesn't move).

Here is a list of the one-operand keystrokes listed in the order they appear on the calculator's face:

  • CHS (change sign)
  • ln (natural logarithm)
  • log (common logarithm)
  • ex (inverse natural logarithm)
  • 10x (inverse common logarithm)
  • sin (trigonometric sine function)
  • cos (trigonometric cosine function)
  • tan (trigonometric tangent function)
  • sin-1 (inverse sine function)
  • cos-1 (inverse cosine function)
  • tan-1 (inverse tangent function)
  • INT (integer portion)
  • sqrt (square root)
  • FRAC (fractional portion)
  • x2 (square)
  • ABS (absolute value)
  • 1/x (reciprocal)

Here is a list of the two-operand keystrokes:

  • % (percent)
  • ->R (convert polar to rectangular)
  • ->P (convert rectangular to polar)
  • - (subtract)
  • + (add)
  • x (multiply)
  • ÷ (divide)
  • yx (Y raised to the X power)

The pi keystroke can be considered a zero-operand keystroke since it lifts the stack by populating the X stack entry with 3.14159...

Every one of the calculator's keys is further explained in the section of this document entitled The Instruction Set.

Generally, anytime you start entering a new number the stack will lift to accommodate the new number. You can see these automatic stack lifts in the previous example. Most keystrokes leave the calculator in a state where the stack will automatically lift upon the next number. The only exceptions to this rule are the ENTER key and the CLX (clear X) key which both leave the calculator in a state where it won't automatically lift when the next number is entered. Consequently, the next number after an ENTER or CLX keystroke will replace the contents of the X stack location without affecting the rest of the stack. Why are there these two exceptions? The ENTER key copies X to Y so there is no reason for the stack to automatically lift when you enter a subsequent number. You also wouldn't want the stack to lift after a CLX because that would just insert a zero into the stack. You rarely need to think about this -- the calculator just does the right thing.

Before leaving this section I should point out that you don't have to use the mouse to click on the keys seen on the calculator if your computer's keyboard has the equivalent key. That is, you can operate the RPN calculator using the following keys found on a standard Microsoft keyboard:


RPN

( 5 )

Reverse Polish Notation

In the previous section I argued that RPN calculators were more consistent than conventional (usually called algebraic) calculators, since you could count on activity occurring whenever you pressed a key, and because all operands for all functions were handled identically: always taken off a stack. These are nice features, but they do not really explain why RPN calculators were built.

RPN was simpler for both the human operator and for the hardware (the machine). I have to use the past tense because the adoption of parentheses keys on algebraic calculators earned them the "simpler to operate" title. Furthermore, the microelectronics revolution that allows millions of transistors to be placed on a small chip of silicon meant that it was no longer necessary to employ the simplest circuit design. Consequently, algebraic calculators way outsell RPN calculators today.

To understand the parentheses issue, you must consider a math problem involving precedence, which is the order that operations must be performed to obtain the correct result. As an example, consider the problem of computing:

2 * 3 + 4 * 5

Because multiplication has higher precedence than addition this problem should be performed as:

( 2 * 3 ) + ( 4 * 5 )

and hence the answer is 6 + 20 = 26. When confronted with this problem armed only with an antique algebraic calculator (that is, one that lacks parentheses keys), the operator is forced to employ the following keystroke sequence:

Observe that you need 12 keystrokes. On an RPN calculator we can solve this problem with fewer keystrokes by leaving the intermediate result of 2 * 3 on the stack while we compute the other intermediate result of 4 * 5. Study how this is accomplished with the following keystroke sequence:

Even though the above illustrations show that the operand stack initially held nothing but zeros, this same sequence of keystrokes will compute (2 * 3 ) + ( 4 * 5 ) regardless of the initial state of the stack. That is, we can put our new operands in the stack and operate upon them without any interference from the original stack contents.

Using the RPN calculator we need only 9 keystrokes to compute (2 * 3 ) + ( 4 * 5 ). Note that even with a modern algebraic calculator having parentheses keys, this problem still requires 12 keystrokes. But on more complicated examples the parenthesis keys do simplify the operator's job of expressing the problem to the calculator.

So why have I chosen an RPN calculator for use in my programming curriculum? Because of the prominence of the stack. Stacks are a vital component in all computers. They are the invention that allows an arbitrary section of a computer program to employ (call into) any other arbitrary section of the program. Every computer and calculator includes at least one stack and some computers (such as the Burroughs B5000 and the HP-300) were designed around an operand stack exactly like the one in our RPN calculator. Stacks are so ubiquitous that the RPN calculator actually has a second stack that we will encounter shortly.

The name stack was selected for this invention because it operates exactly the same as a stack of dishes: you can only add a new dish on the top and you can only take a dish from the top. The stack in the RPN calculator loads and empties from the bottom but the critical aspect is the last in, first out nature of the device.

Here's a cute thing you can do with the stack. Programmers (for reasons that will become clear when we next study the 8051 microprocessor) are always needing to compute the sequence of powers of 2; i.e., 2, 4, 8, 16, 32 ... An RPN calculator allows you to compute this infinite sequence using fewer keystrokes than any other calculator. In fact, a single keystroke is sufficient to produce each subsequent number in the sequence. To do this, first fully populate the stack with nothing but twos. You can do this via the keystrokes: 2, ENTER, 2, ENTER, 2, ENTER, 2. You are now ready to compute the sequence. Simply click on the Multiply button each time you want the next number in the sequence. Do you see why this works?

The name Reverse Polish Notation honors the Polish mathematician Jan Lukasiewicz who in the 1920's developed a formal logic system which allowed mathematical expressions to be specified unambiguously without the need for parentheses. There were two such approaches. In the first approach, known as Polish notation, the operator precedes both operands. Computer scientists today call this a prefix notation. In the second approach, known as reverse Polish notation, the operator follows both operands. Today, this is more commonly called postfix notation. The usual pencil and paper notation is called infix notation because the operator goes in between the operands.

It is possible to build a computer (or a computer programming language) that employs postfix notation:

or infix notation:

or prefix notation:

Of these 3 alternatives, postfix notation could be accomplished with the fewest storage locations and hence the fewest transistors. This one aspect was the deal breaker in electronic devices of the 1970s and hence Hewlett-Packard went with RPN notation for their line of hand calculators. But RPN isn't dead. Even today, most algebraic calculators convert an expression with parentheses into an equivalent RPN format before computing the result! And you might have heard of the PostScript language used with laser printers, it is another example of a postfix language.


( 6 )

Statistics

One of the nicest features of the RPN calculator (besides the fact that it is programmable) is that it can compute statistics such as mean and standard deviation and perform least squares curve fits of experimental data. These operations share a small number of keystrokes.

Let's start by learning about mean and standard deviation. Imagine that you are a teacher and you have just finished grading your students' test papers. You are very fortunate to have a small class size of only 12 students and the 12 test grades were:

87 73 95 88 90 78 69 82 83 97 79 88

The calculator uses certain of its registers (data memory locations) whenever you ask it to compute statistics. These are data memory locations 10 through 15 which are known as the Summation registers. Before starting a new statistical computation you want to clear these registers so that old data does not pollute the new results. This is the purpose of the ClearSum key which looks like [image] [image] (the funny symbol is Sigma, the capital S letter in the Greek alphabet, S for "sum"). You could get the same effect by using the ClearREG keystroke ( [image] [image]) which clears all 30 of the registers. We will further discuss the calculator's registers in the section entitled data memory.

You are now ready to enter the test scores. Type in the first test score and then hit Sum+ ( [image]). The X location of the operand stack does not show the test score but rather displays the number 1 to remind you that you have entered 1 data point. Next type in the second test score and again hit Sum+. The calculator's LED display now shows the number 2. Continue in this manner (note that you are NOT employing the ENTER key) until you have entered all 12 test scores and the display shows that you have entered 12 data points.

Since the calculator now has all the data points you can ask it for the mean and the standard deviation. To ask for the mean use the Mean keystroke ( [image] [image]) which causes the mean of the data points to be placed into the X stack location. For these 12 test scores the mean score was 84. To ask for the standard deviation use the StdDev keystroke ( [image] [image]) which causes the standard deviation of the data points to be placed into the X stack location. For these 12 test scores the standard deviation among the scores was 8.41 . The standard deviation describes how tightly grouped the test scores were. For most types of data, the vast majority of the data points will be within three standard deviations of either side of the mean.

If you make a mistake while entering a data point you can use the CLX key ("clear X") to clear the X stack location. But if you have already hit the Sum+ keystroke before you realize the error then CLX will not be sufficient. You can either start over (that is, type ClearSum and then re-enter all the data points) or else again enter the erroneous data point and then hit the Sum- ( [image] [image]) keystroke.

The Sum+, Sum-, Mean, and StdDev keystrokes are actually more powerful than I have let on. Every time you hit the Sum+ keystroke both the number in the X stack location and the number in the Y stack location are added into separate running totals. When you later hit the Mean key the X stack location will show the mean of all the X values you entered and the Y stack location will show the mean of all the Y values you entered. Thus you can really perform two averaging operations simultaneously. This will become useful when we start dealing with Cartesian plots whose data points are (x,y) pairs.

The calculator will give you an error if you try the following keystroke sequence: . This is an error because you have asked for the average of 0 data points, which can't be computed. Similarly, you can't compute the standard deviation of 0 data points. You can compute the average of 1 data point but did you know that you can't compute the standard deviation of 1 data point? Standard deviation is only defined when you have 2 or more data points.


Least Squares

( 7 )

Least Squares Curve Fitting

Now let's move on to least squares curve fits of experimental data. "Experimental data" refers to any type of data that is subject to imperfect measurement. For example, most experimental data is collected by sensors and all sensors suffer from inaccuracy, nonlinearity, noise, aging, miscalibration, etc. which cause their readings to be somewhat contaminated. If all the readings are on the same side of the true reading we say the sensor is biased. It is usually pretty obvious when a sensor shows bias and there is usually a calibration procedure that can re-center the sensor. But even without bias the sensor readings will still wander or jitter on both sides of the true reading. Averaging can remove unbiased error from experimental data since about as many flaky readings will be on both sides of the true reading. But some types of data cannot be averaged and we must try other techniques to see past the noise in these readings.

As an example of experimental data that cannot be averaged, let's consider the following velocity versus distance measurements made by telescope observation of 8 galaxies:

distance in velocity megaparsecs in km/sec 3.06 281 6.12 500 7.14 406 8.88 906 11.22 708 12.04 750 15.10 1104 18.27 1302

These measurements were made with the Hubble telescope. Back in 1924 Edwin Hubble first discovered that not all the twinkling lights we see in our nighttime sky are stars. Instead, many (in fact, billions) of these lights are other galaxies just like our own Milky Way galaxy. Hubble (for whom the Hubble telescope was named) then determined that these other galaxies were all moving away from each other, indicating that our universe is expanding, as if it had been blown apart. This is where the "big bang" theory of the origin of the universe comes from. Hubble further learned that the most distant galaxies were moving with the greatest speeds. Hubble hypothesized that speed was linearly proportional to distance. Today, this proportionality factor is known as the Hubble constant. The measurements that Hubble was able to collect in 1929 to gauge the Hubble constant were lousy but the table above shows measurements made in 2001 using the Hubble telescope. These 8 data points are plotted below.

Clearly these 8 data points don't fall on a straight line. But this could be because, even today, it is still very difficult to accurately measure these quantities. Hence the data is probably corrupted by measurement noise. We want to remove the noise and determine the straight line that we believe is buried in these measurements. We can't average the data points because they are not the same measurement (they are supposed to be spread out across a line). But we can fit a straight line to the measurements in a way that best matches the data. This process is called curve fitting and is very easy for humans to do. You just hold a transparent ruler up to the plot and adjust it until it centers on the data. One possible straight line fit is shown below:

Notice that 1 data point is significantly above the fitted line and 3 are significantly below. Yet our eye tells us that this straight line best fits the data. Our goal isn't to find a line that places the same number of data points on either side of the line. Instead we want the line that minimizes the error between the data points and the fitted line. This error is shown by the vertical bars in the plot below:

I claim that there is no other straight line that you can draw that results in a smaller amount of error bars (i.e., that results in less red ink in the previous plot). I feel confident of this claim because it turns out that there is a mathematical procedure that can find that particular straight line that minimizes the error bars seen in the previous plot. This mathematical procedure is known as a least squares curve fit and the blue line is the result of applying this algorithm to the Hubble data.

The "least" portion of the name indicates that we are minimizing the error in the curve fit. The "squares" portion of the name indicates that we are squaring the error bars in order that all the errors become unsigned. That is, the error is the difference between the data point and the fitted line. When the error bar is above the fitted line the error is positive and when the error bar is below the fitted line the error is negative. We want to treat positive and negative errors identically so as to center the fitted line between all the data points. Therefore we square the error terms to remove their signs.

The mathematical equations that describe the slope and intercept of the best fit straight line are:

These equations may look forbidding, but they are easy to compute since the RPN calculator does all the legwork. The Greek letter Sigma in these equations indicates "summation" over all the experimental data. This type of summation is performed by the RPN calculator every time you employ the Sum+ keystroke. The terms needed for eq. [1] are automatically collected in the following registers of the calculator:

I am delaying teaching you about the calculator's registers (also known as "data memory") until we get to the section on data memory. For now, just realize that the Sum+ keystroke employs certain calculator resources which you must clear via the ClearSum (or ClearREG) keystroke before starting a new statistical calculation.

After entering the experimental data using the Sum+ key ( [image]) you are ready to hit the LR key ( [image] [image], LR stands for linear regression) which will cause the calculator to compute eq. [1]. The slope of the best fit straight line will be placed into the X stack location and the intercept will be placed into the Y stack location (just as with the Mean and StdDev keystrokes, these new numbers overwrite the X and Y stack locations and hence the stack does NOT lift).

So let's try this process on the 8 astronomical observations in the prior table. This time we will be entering both x and y values prior to each Sum+ keystroke and we want to be careful to get the graph's y values into the Y stack location or else we will end up solving for the slope and intercept of a flipped (mirror imaged) graph. Start by clearing the summation registers and then use the following keystroke sequence to enter the first (x,y) data point:

281 ENTER 3.06 Sum+

Continue in this manner until all 8 data points have been accumulated. Then click the LR keystroke and observe that the X stack location holds 65.95 . This is the slope of the best fit straight line. The Y stack location holds the y axis intercept value of 70.00 . Based upon this data, the Hubble constant is computed as 65.95. That is, the Hubble constant is simply the slope of a plot of velocity (in km/sec) versus distance (in megaparsecs).

Theoretically, this straight line should go through the origin (i.e., the x = 0, y = 0 point where the axes intersect). The line we obtain from a least squares curve fit of the 8 galaxy measurements obviously doesn't go through the origin. With more data points (more galaxies) we could get an even better estimate. The generally accepted value for Hubble's constant is 72 +/- 8 from which scientists compute the age of the universe as 13 billion years! The +/- 8 term is basically a "truth in advertising" clause that states that scientists believe the Hubble constant must lie somewhere between 64 and 80. Incidentally, the oldest human fossils are only a few million years old so humans have been around for only .02 % of the universe's lifetime.

I should also mention that there are other types of least squares curve fits. For example, if you have a collection of experimental data points that you believe should fall on a parabola there is a way to compute the particular parabola that best fits your data points in a least squares sense. But the RPN calculator can only perform straight line fits and this is why the keystroke is labeled LR for "linear regression".


Programming

( 8 )

Programming The Calculator

Now that you know your way around the keyboard of the RPN calculator you're ready to start programming it. A program is simply a stored representation of the activities you want to take place at that later time when you decide to execute the program. Executing a program is also known as running the program. We will store our RPN calculator programs in plain-text ASCII files having a .TXT suffix and hence compatible with the Notepad word processor that Microsoft ships with every version of their Windows operating system.

Before we can execute a calculator program we must load it into the RPN calculator's program memory. We won't concern ourselves with the representation for the program used inside the calculator (this representation involves bits and bytes which we will study when we get to the next portion of our curriculum, an 8051 microprocessor). Instead we will just imagine that program memory can hold text (character strings) such as ENTER, 7, and *. Wherever possible we will use the text that is printed on the calculator for the representation of each keystroke. An example of where we can't do this is the [image] key since we can't create a down arrow on a standard computer keyboard. So we use the text Rolldown as the representation of this keystroke. Text such as this is known as mnemonics because these character strings are designed to allow the human operator to remember the activities of each statement.

Let's imagine we wanted a program that would produce the sequence of integers starting from 1. Obviously this program would need to run forever. We can make a program run forever by including an infinite loop. An example of an infinite loop is the birthday card that says, "For your surprise, turn card over" on both of its sides. Or the instructions on the shampoo bottle that say:

  • wet hair
  • lather
  • repeat

These instructions would strand a computer programmer -- who is trained to be literal -- in his shower.

An RPN calculator program that creates an endless stream of integers starting from 1 is shown below. I will show you how to create this program and we will then save it in a plain-text ASCII file called "IncrementForever.txt".

01 LBL 0
02 1
03 LBL 1
04 1
05 +
06 GTO 1

You will notice that I have numbered the 6 statements in this program. Older computer languages required you to number your statements but most modern languages do not require statement numbers. In the case of the RPN calculator the statement numbers are mandatory but the calculator provides them for you.

In this program we see the mnemonics LBL, +, and GTO. You can read about each mnemonic in the section of this document entitled the instruction set but my plan is to introduce them to you a few at a time as we work through these help pages.

The first statement is a specific instance of the generic LBL n statement. The RPN calculator allows you to create up to 10 labels, numbered from 0 to 9. That is, the letter n in the generic LBL n statement indicates a 1 digit variable. We have chosen to start our program with a label numbered 0. We will see that having a label at the start of our program simplifies the task of running the program.

The next statement enters the number 1 into the X location of the operand stack (or, more precisely, it will have this action when we run the program). The prior contents of the stack lift but it makes no difference to our program what these prior contents were as we will never employ them. It is this statement that causes the sequence of integers to begin with 1. If you want your sequence to begin with a different number then change this statement.

The next statement is another label statement. We can't again use label #0 so this label is given the number 1. We need this label so we can use it as the destination of an unconditional branch that creates an infinite loop (more on this shortly).

The next statement enters the number 1 into the X stack location, thus causing the stack to lift. We now have two ones in our operand stack.

The next statement causes an addition operation (that is, it will cause an addition when execution reaches this statement). Because the X and Y stack locations both hold 1 this addition will result in the value 2 being placed in the X stack location.

The final statement is an unconditional branch statement which is a specific form of the generic GTO n statement. You can use however many GTO statements you want but you are limited to 10 destinations, labeled 0 through 9 since this is how many LBL statements you are allowed.

So our program is all designed and we want to get it into the RPN calculator. To accomplish this, first move the Prgm/Run slide switch over to Prgm (remember that you must click and drag the slide switches). This puts the calculator into its Program mode and causes the simulator's window to change from this:

to this:

The new pane that you see is called the Program window and it shows the RPN calculator's program memory. Any program that you wish to execute must first be placed into program memory. Using the scroll bar you can discover that program memory is limited to 98 statements (it is impossible to place a statement into the 00 slot and there is no 99 slot). You can also see that initially program memory is empty. Well, it's actually not empty: it is initialized with 98 repetitions of the R/S keystroke, which is the statement that stops a running program. But you can think of this configuration as being empty.

Anytime you want to return program memory to this reset condition just get into Program Mode and hit the [image] [image] key (this is called the ClearPRGM key). You are in Program mode anytime the Prgm/Run slide switch is set to Prgm.

Anytime you are in Program mode the current insertion point is highlighted in blue. To insert a new statement below the highlighted statement you merely need to press the necessary keys on the calculator. For example, immediately after performing a ClearPRGM operation the 00 numbered statement will be highlighted. This means we are ready to enter the calculator instruction we want to place into the 01 slot of program memory. To do this simply click on [image] [image] [image] to create the LBL 0 statement. Note two things here:

  • You don't provide the statement number, the calculator does.
  • You don't type a space (there is no way to type a space) between LBL and 0 even though the mnemonic "LBL 0" includes a space.

Because this action inserts a new statement at program memory location 01, all subsequent statements are pushed down in program memory and the former statement at program memory location 98 is lost. This will not be obvious at the moment since program memory is populated with a bunch of R/S statements that all look identical. Furthermore the current insertion point moves down so now the 01 statement is highlighted.

You are now ready to add the 02 statement to the program. This merely requires you to click on the 1 key. This completes the 02 statement. Then type LBL 1 to complete the 03 statement. Continue in this manner until you have entered the entire program.

Before we learn how to run this program we should learn how to edit a program since you may have made a mistake while entering it. If there is something wrong with one of your program statements you need to delete the bad statement and then re-insert the correct statement. The DEL key ( [image] [image]) deletes whatever statement is at the current insertion point and moves all subsequent statements up in program memory and re-populates program memory location 98 with a new R/S statement. So before you can use the DEL key you must position the current insertion point on the bad statement.

There are two ways to move the current insertion point. You can move it by 1 statement at a time by clicking on the SST (single step) or BST (back step) keys. But if you have a large distance to move then you don't want to have to take baby steps to get there. The solution is the GTO .nn generic keystroke. The nn notation signifies that you must always provide a two digit statement number as part of this statement. As an example, to move the current insertion point to statement 05 you would use the keystroke sequence:

I should warn you that there is a subtle difference between the current insertion point and the statement that is highlighted. If you don't disturb things then the current insertion point will always remain highlighted. But via the mouse you can manage to highlight an arbitrary statement or just a portion of a statement or even nothing at all (you might choose to do this as part of a cut and paste operation). ALTHOUGH THESE ACTIVITIES CHANGE THE HIGHLIGHT THEY DO NOT CHANGE THE CURRENT INSERTION POINT. Only the SST, BST, and GTO .nn statements change the current insertion point (well, okay, adding a new statement or deleting a statement also moves the current insertion point). There is a special toolbar button ( [image]) called the Highlight Next Statement button that will restore the highlight to the current insertion point. Click on this toolbar button any time you fear that the highlight might have gotten disturbed. The [image] toolbar button doesn't move the current insertion point, it only re-highlights it (the menu choice that corresponds to this toolbar button is labeled "Repair highlight").

Once you are happy with your program you will want to save it in a plain text ASCII file having a .TXT filename suffix. This is accomplished by selecting File/Save from the menu bar (or clicking on the floppy disc icon visible on the toolbar). Save this 6 statement program in a file named "IncrementForever.txt". This is the first of the example programs that I provide with my RPN calculator. At any later time you can reload this program into the calculator via the File/Open choice in the menu (or the manila folder icon on the toolbar). You can load a program while the calculator is in Run mode or Program mode.

So let's summarize. The RPN calculator can, upon command, perform the sequence of operations that are stored in its program memory. Program memory can hold up to 98 operations, each of which is indicated by a textual mnemonic. While in Program mode your keystrokes do not cause computations to occur, instead they cause program memory to be populated with the corresponding mnemonic.

Remember that you can clear the calculator's program memory via the ClearPRGM keystroke anytime you are in Program mode. You can also clear the program via the blank piece of paper icon on the toolbar (this works in both Run and Program mode).


Executing

( 9 )

Executing a Program

You can only edit your program while you are in Program mode (i.e., while the Prgm/Run slide switch is set to Prgm) and you can only execute a program while you are in Run mode (i.e., while the Prgm/Run slide switch is set to Run). So if you have fully entered the 6 statement IncrementForever.txt program that we created in the prior section then move the Prgm/Run slide switch back to Run in preparation for executing this program. The Prgm/Run slide switch controls the mode of the calculator but does not cause the stored program to begin executing.

Let me emphasize that while the calculator is in its Run mode a click on the Multiply key will cause a multiplication (of the numbers in the X and Y stack locations). But while the calculator is in its Program mode a click on the Multiply key will cause a * mnemonic to be inserted into program memory just below the current insertion point (the highlighted instruction). This mnemonic will cause a multiplication (of the numbers in the X and Y stack locations) to occur when we reach this statement while executing the program.

All computers execute their programs by performing the instructions found in program memory in sequential order. That is, each instruction is performed one after the other. The only way to get a program to jump to a different section of program memory is to employ a branch or call statement.

The hardware mechanism that is used to walk through the statements in program memory is a program counter (PC), which is a conventional counter that increments as each instruction is performed. If the PC holds the value 05 then the statement at program memory location 05 is executed next. Assuming this was not a branch or call statement then the PC will be incremented to 06 and the statement at program memory location 06 will be executed next. The way that branch and call statements alter the flow of control is that they load the PC will a new, non-sequential value (we will study branch and call statements in a later section).

You have a few alternatives when you wish to influence the PC manually. We will first learn about the GTO .nn ("goto") generic instruction. The nn portion of this instruction indicates that you must always include exactly 2 decimal digits indicating the new value you wish for the PC. Two digits are required because program memory can hold up to 98 statements and hence the PC can take on any value between 00 (which can't hold an instruction) and 98. We have already seen the GTO .nn statement in Program mode but this keystroke is available in Run mode as well. In Program mode the GTO .nn statement modifies the current insertion point and in Run mode the GTO .nn statement modifies the program counter.

From this you can conclude that the calculator employs the program counter to manage the current insertion point. You can observe that this is so because every time you switch from Program mode to Run mode the next instruction that will be executed (assuming you don't intervene and modify the PC) will be the instruction at the current insertion point. And every time you switch from Run mode to Program mode the current insertion point will be the last instruction that was executed. To summarize, in both Program mode and Run mode there is always a single, specially designated instruction among the 98 instructions in program memory and the PC points to this instruction. In Program mode it is the instruction that will be deleted or the instruction just prior to the next instruction that gets inserted. In Run mode it is the next instruction to be executed.

In the last section we wrote and edited our first program, which we stored in the file IncrementForever.txt . We now wish to execute this program. We must be in Run mode to execute a program so be sure the Prgm/Run slide switch is set to Run. One way to get a program to begin executing is to click on the R/S (Run/Stop) key. The R/S key causes execution to initiate at the instruction pointed to by the PC. Consequently you will almost always want to initialize the PC before you click on R/S to start a program. We can start our IncrementForever.txt program via the keystroke sequence:

While a program is running the calculator's LED display is hard to read since the operand stack is constantly changing. Allow this program to run for a few seconds and then click any calculator key to halt the program. I always choose to use the R/S key to halt a program but any key can be used. In this situation the key that you select does not perform its normal function but simply halts the program. Once the program halts you will either see the number 1 in the X stack location or some other number, depending upon precisely where the IncrementForever.txt program was interrupted. If the X stack location holds the number 1 then the Y stack location will hold the number that we have counted up to. Otherwise the X stack location will hold the number that we have counted up to. After running this program for 2 seconds on a 400 MHz Pentium II computer the calculator will have counted up past 1000.

You can continue the program from wherever it left off simply by clicking the R/S key. Execution continues from where it left off because nobody has modified the PC. And hence the incrementing count will continue from where it left off. Alternatively, you can re-initiate the program from its beginning by using the same keystroke sequence as before:

In this case we reset the PC to 0 and hence the program starts from the beginning and hence the incrementing count is re-initiated from the value 1. Note that the keystroke sequence GTO.01, R/S would be just as good as GTO.00, R/S since there is no instruction stored at program memory location 00.


Single Stepping

( 10 )

Single Stepping a Program

So far we have only seen the consequences of the running program; that is, we have seen the changes in the operand stack. But this Windows simulator allows you to actually observe the execution of the program itself. To accomplish this halt the program and then click on the SST (single step) key. The SST key is employed for a type of debugging activity called single-stepping which is where you only allow your program to execute (perform) a single statement at a time. Which statement? The one pointed to by the PC of course. And because the SST key indicates that you wish to debug your program two new panes open up in the simulator's window. The Program window we have seen before. It is the same pane that is visible while we are in Program mode. The Registers window is new. It displays the contents of the 30 data memory locations available on this calculator. We will employ data memory in later programs but for now just ignore this pane. These new window panes are shown below:

Now that the Program window is visible let's continue our program by clicking on R/S. You can now observe the blue highlight constantly moving to identify the particular program memory instruction being executed. You will see that this program constantly cycles among the 4 consecutive statements in program memory locations 03 through 06. This is the infinite loop that we placed in our program.

Halt the program (by clicking any key) and then perform the following keystroke sequence:

Observe that the highlight moves to statement 00. Next click AND CONTINUE TO DEPRESS the SST key. Observe that while you hold the SST key depressed the highlight moves to statement 01 and the X location of the stack shows the same statement, which is the statement about to be executed. When you release the SST key this one statement is executed and the X location of the stack goes back to showing operands rather than statements. The highlight moves on to the 02 statement to show that the PC is identifying this as the next statement to be executed. Click on the SST key a few more times. Note that each program memory instruction is executed in sequential order. However the consequence of executing statement 06 is that the PC is reset to 03 (this is because statement 06 is a GTO statement).

Single stepping is just a slow way of running a program and hence is especially useful for debugging a faulty program. At any time you can click on the R/S key to re-initiate full speed execution of the program. Execution starts from wherever the PC happens to be. Click the R/S key a few times and observe how chance decides exactly where you interrupt the program.

Now let's do something devious. Halt the program on one of the statements within the infinite loop (that is, on statement 03, 04, 05, or 06). Now type the following keystroke sequence:

This keystroke sequence has the consequence of initializing the X stack location with the number -5000. This shows that we are allowed to do whatever we want to the operand stack while the program is halted. Then continue the program from wherever it left off by clicking on R/S. The program is not smart enough to know that we have perturbed its counting sequence. It simply continues doing what it is told: endlessly adding 1 to the number in the X stack location. So you will observe that the program (which we haven't changed in any way) is now counting up from -5000. Eventually the count will pass 0 and then continue into the positive integers. Single-step the program if you are confused by this last paragraph.

As another experiment, halt the program and change the X stack location to the fraction 0.25 . As long as you continue rather than restart the program you will now observe the sequence 0.25, 1.25, 2.25, ... Remember, the R/S key continues a program while the keystroke sequence GTO.00 followed by R/S restarts a program from the beginning.

I said you had a couple of options for how to initiate your programs but thus far I have only shown you the R/S keystroke (which is usually used in conjunction with GTO .nn). A more conventional way to initiate a program is to use the GSB n generic keystroke sequence. The n indicates that you must supply a single digit to identify the label where you want execution to start. Since our program starts with a LBL 0 statement then the following keystroke sequence will start our program running from the top:

Since this keystroke sequence starts the program from the top it will be pointless to initialize the X stack location with -5000: the first (okay, second) statement of the program forces the count to begin from 1 (try it). However if you want to employ this program to count up from -5000 then initialize the X stack location with -5000 and then type:

This works because our program also includes a LBL 1 statement. If we start execution at LBL 1 we bypass the portion of the program that forces the count to begin from 1 (look at the program and check what is between the LBL 0 and LBL 1 statements).

As another experiment try the keystroke sequence:

This keystroke sequence causes an error since there is no LBL 2 statement anywhere in program memory. The calculator displays an error message which you must clear before you can do anything else. Click on ANY calculator key to clear the error (the normal function of the key is ignored).

The RPN calculator has a small number of error messages. Another way to elicit an error message is to attempt to divide by 0 via the keystroke sequence:

There is yet another way to initiate a program and it involves the GTO n generic instruction. When executed as part of a program the GTO n statement causes the program counter (PC) to be re-initialized to a new value and hence program execution continues at a new location. When executed from the keyboard the GTO n statement re-initializes the PC but does not initiate execution of the program.

Note the difference between GTO .nn and GTO n. The former keystroke sequence includes a decimal point and exactly two digits while the latter has no decimal point and exactly one digit. Whereas GTO .nn sets the PC to any value between 0 and 98 the GTO n instruction sets the PC to point to a LBL statement. So we can prepare to run our program from the top by clicking:

Whereas the GSB keystroke modifies the PC and then starts execution of the program, the GTO keystroke merely modifies the PC. We must then click on R/S to start the program running.

Before ending this section I want to warn you of a very common beginner's error. While you are in Program mode the PC is employed to define the current insertion point. When you throw the Prgm/Run slide switch back to Run the PC retains whatever value it last had (probably at the end of the program you just entered). If you simply click on R/S (or SST) your program will probably start running at the R/S statements that exist in program memory beyond the end of your program. But the calculator thinks that anything in program memory is part of your program. A R/S statement in a program causes that program to halt. So every time you click on R/S the program will execute another one of the R/S statements in program memory beyond the intended end of your program. Basically, nothing will be happening and you will wonder why. So always initialize the PC via the GTO .nn or GTO n statements before you click on R/S or else use the GSB n statement which accomplishes both activities.


Arguments

( 11 )

Passing Arguments To A Program

Here's a paradox: for a program to be useful, it generally needs to be incomplete. That is, it needs operands which are not specified as part of the program. The flexibility offered by these unspecified operands motivate us to execute the program more than once.

Our first example program, IncrementForever.txt, had no unspecified operands, and hence produces the exact same results each time it is run. Once we have seen this program run, there is little reason to ever run it again. We generally want to avoid this outcome when we go to the trouble to write a program.

We can improve that program by leaving some aspect of it unspecified. Let's create an IncrementForever2.txt example program that leaves the starting value unspecified. I have already written the IncrementForever2.txt program and so you only need to load it into the RPN calculator. You accomplish this via the File/Open choice on the menu bar. You can load a program while the calculator is in either Run mode or Program mode (you have to be in Program mode to create or modify a program). The full contents of the IncrementForever2.txt file are shown below:

; file IncrementForever2.txt ; This RPN calculator program forever increments by 1 whatever number ; you place in the X stack location before initiating the program. LBL 0 ; start of program +1. + GTO 0

The first thing you notice about this program is that it includes both English sentences and calculator statements. The sentences are examples of comments. Comments are ignored when the program is executed. All programming languages support the use of comments in order to allow the programmer to explain the operation of his program. For the RPN calculator, anything on a line after a semicolon (;) is a comment and is ignored.

There is no way to create comment lines (or blank lines) via the calculator's keyboard. If you would like to add comments to the programs you construct by clicking (while in Program mode) the keys on the calculator then you will have to save the program into a .TXT file that you later modify with Notepad. Once you have memorized the mnemonics used to represent each of the keystrokes, you can construct your program from scratch in Notepad.

Once you have loaded my IncrementForever2.txt program (which you can do in either Run mode or Program mode) go into Program mode so you can inspect it. You will see that the comment lines do not become numbered statements, as shown below. All of my comments are at the top of the program but you are free to place them anywhere you want.

Now that the shock of the comment lines has worn off, you can observe that IncrementForever2.txt is an even simpler program than IncrementForever.txt . It's simpler because it does less: it skips the initialization that used to exist prior to the infinite loop. It is now the responsibility of the operator to provide this initialization before he runs the program. To run this program you must enter Run mode, initialize the X stack location with a desired starting value (maybe -5000), and then click on GSB 0. The program will start and continue counting up from -5000 until you halt it. "Fascinating!" I can just hear you saying.

Okay, so I need to show you better reasons to use what are called arguments or passed parameters. I will do so in this section via the SecondOrderEq.txt example program but I want to show you a few more tricks before we leave our IncrementForever series of programs.

Load the example program held in the IncrementForever3.txt file. This program accomplishes the exact same thing as IncrementForever2.txt, but looks nothing like it. It too will keep adding 1 to whatever value is placed into the X stack location before the program is run. Since this program lacks a LBL statement you will have to run it via the keystroke sequence GTO.00, R/S. But where is the infinite loop? The IncrementForever3.txt program purposefully allows the program counter (PC) to overflow or wrap-around from its maximum value of 98 back to 0. All microprocessors and computers display this rollover phenomenon but it is usually not used in this manner.

To see the rollover occurring you just need to execute the program while the Program window is open. I have already mentioned that the SST keystroke will open the Program window. Keep clicking on SST until you observe the PC (and hence the highlight) wrap-around back to the beginning.

There is another way to open the Program window. The full list of user specifiable program options can be displayed by selecting View/Options from the menu bar. One of these options allows you to request that the Program window always remain visible. You might like to activate this option. The only downside is that the calculator simulation runs about 33% slower due to the extra work necessary to continually redraw the highlight while the program runs.

With that diversion out of the way, let's get back to studying why and how arguments are passed to programs. Suppose a teacher has asked you to plot the following quadratic (that is, second order) equation:

y = x2 + 2 * x + 3

You figure you could do a good enough job plotting this function if you knew the 21 y values that correspond to the 21 integer x values ranging from -10 to +10. So you have to substitute 21 x values into the equation and solve for 21 y values. Normally, that would take a while. But with a programmable calculator it's a snap.

What we want to leave unspecified in this program is the particular x axis value. Each time we initiate the program we will supply a different x value by placing it into the X stack location. The x axis value for which we want the corresponding y axis value is the argument or parameter that we pass to the program. We then only need to initiate the program 21 times, each time using a different argument, and we will have the numbers needed for our plot.

Before initiating the program we place the x axis argument into the calculator's operand stack. We use the operand stack because it is both accessible to us before we execute the program and is accessible to the program once the program starts running. We will later learn that there is another hardware feature: the data memory, that shares this dual accessibility feature and hence can also be used for passing parameters to programs.

Note that I use lower-case letters for algebra symbols and upper-case letters to identify the stack locations. It is perfectly fine for our program to cause the x axis value to move around among the X, Y, Z, and T stack locations.

So it's time to write the program. Maybe we decide to compute the right-hand side of the equation:

y = x2 + 2 * x + 3

working from left to right. In this case we can begin our program (after the LBL 0 statement that we always place at the top) with an x2 keystroke to compute the second order term. This works but causes a problem. The first order term, 2 * x, also involves the passed parameter x which we no longer have available! The human running the program (usually called the operator) placed the desired x axis value into the X stack location before he initiated the program. The program then immediately squared the value in the X stack location in order to compute the x2 term. This means the X stack location no longer holds the x axis value. Worse, the x axis value is not available anywhere else and hence we are prevented from computing the 2 * x term.

Perhaps the cleanest solution to this problem is to have the program copy the original x axis value into data memory so that it can be recalled whenever it is again needed. But since I have not yet introduced data memory I am going to solve the problem another way. I am going to make a duplicate copy of the passed parameter in the operand stack. The human running the program places a single copy of the x axis value into the X stack location. The first thing the program does is copy the X stack location to the Y stack location so that the operand stack holds 2 copies of the x axis value. When the x2 keystroke consumes one of these copies, the other is still available for the 2 * x term.

The complete program is shown below. You can load it from the SecondOrderEq.txt file.

; file SecondOrderEq.txt

; This RPN calculator program computes the second order eq.:
;    y = f(x) = 1x^2 + 2x + 3

; To run the program enter an x value into the X stack location
; and then type GSB 0 (alternatively, type GTO .00 followed
; by R/S).  When the program stops the corresponding y value
; is visible in the X stack location.

LBL 0        ; program entry point, at this time X = x
ENTER        ; make a duplicate copy of x axis value,
             ; at this time X = x, Y = x
x^2          ; X = x^2, Y = x
x<>y         ; X = x, Y = x^2
+2.          ; X = 2, Y = x, Z = x^2
*            ; X = 2x, Y = x^2
+            ; X = x^2 + 2x
+3.          ; X = 3, Y = x^2 + 2x
+            ; X = x^2+2x+3
R/S          ; we're done

Note how I use a comment on every program statement to show the contents of the calculator's four-deep operand stack.

Go ahead and run this program for various x axis values. You should get the following results:

x y --------- -5 18 -4 11 -3 6 -2 3 -1 2 0 3 1 6 2 11 3 18 4 27 5 38

Single step the program and watch how the X, Y, Z, and T stack locations take on the values identified in the comments in the program. Remember that I use lower-case letters for algebra symbols and upper-case letters to identify the stack locations.


( 12 )

Data Memory

Data Memory consists of storage locations where you can place and later recall arbitrary values. In the case of the RPN calculator, the values that can be stored in data memory are numbers, but in other computers you can put text, pictures, sounds, and other objects into data memory.

The RPN calculator offers a data memory large enough to hold 30 numbers. These 30 storage locations are illustrated below.

A Register window will open to display the contents of these 30 data memory locations (called registers) anytime you click on the SST key. But this window will automatically close when you next enter Program mode. You can request that the Registers window always remain open via a program option found on the dialog box you get by selecting View/Options from the menu bar.

There are two methods for accessing the data memory of the RPN calculator: direct addressing and indirect addressing. Direct addressing is the simpler technique (hence the name). But unfortunately direct addressing cannot reach all 30 of the data memory locations. You copy the number in the X stack location to data memory location 0 thru 9 via the generic statement STO n where n is a single digit (i.e., 0 through 9). You recall (to the X stack location) a number from data memory location 0 thru 9 via the generic statement RCL n where n is a single digit. You place a number in data memory location 10 thru 19 via the generic statement STO .n where n is a single digit. You recall a number from data memory location 10 through 19 via the generic statement RCL .n where n is a single digit. You cannot reach data memory location 20 through 29 using direct addressing.

Let's first practice with these new instructions. Click on the SST key to open the Registers window (it doesn't matter what program is in program memory since we will ignore the consequence of whatever single statement the SST keystroke just executed). Depending upon what your calculator has been doing, you may already see numbers other than 0 in data memory. You can clear all of data memory via the keystroke ClearREG ( [image] [image], note that this keystroke requires the f prefix).

The keystroke sequence 5, STO 5 will place the number 5 into data memory location 5. The keystroke sequence 15, STO .5 will place the number 15 into data memory location 15.

The generic instruction STO .n probably looks like a bit of a hack since it is not immediately obvious that the decimal point substitutes for a leading 1. But the only alternative would have been to define the generic instruction as STO nn rather than STO n meaning that you always had to type 2 decimal digits. This would have had the benefit of allowing you to directly reach all 30 data memory locations but would also have had the drawback that you would have to type STO 00 to write to data memory location 0. Since you rarely need more than 10 data memory locations anyway, getting rid of one keystroke was a defensible design decision.

Now that we know about data memory let's rewrite our SecondOrderEq.txt example program. You'll remember that we needed to make a temporary copy of the x axis value passed into the program in order that we wouldn't lose this x axis value when we computed the x2 term. In the original SecondOrderEq.txt program we kept this temporary copy on the operand stack. In the new SecondOrderEq2.txt program we will place this temporary copy into data memory location 0 which is also known as register 0.

; file SecondOrderEq2.txt ; This RPN calculator program computes the second order eq.: ; y = f(x) = 1x^2 + 2x + 3 ; To run the program enter an x value into the X stack location ; and then type GSB 0 (alternatively, type GTO .00 followed ; by R/S). When the program stops the corresponding y value ; is visible in the X stack location. LBL 0 ; program entry point, at this time X = x STO 0 ; preserve initial x value in Reg[0] x^2 ; X = x^2 +2. ; X = 2, Y = x^2 ENTER ; X = 2, Y = 2, Z = x^2 RCL 0 ; X = x, Y = 2, Z = x^2 * ; X = 2x, Y = x^2 +3. ; X = 3, Y = 2x, Z = x^2 + ; X = 2x+3, Y = x^2 + ; X = x^2+2x+3 R/S ; we're done

Just for fun we also made another change: we delayed combining the three terms in x2 + 2 * x + 3 until the very end of the program. These three terms all reside in the operand stack until the back-to-back + keystrokes that end the program.

The new program is 1 statement longer than the original program which only proves how handy the operand stack is as a temporary repository of operands (which is exactly what it was intended for).

Thus far we have used the operand stack to pass run-time parameters into our programs. Since data memory is accessible to both the human operator and to the running program it too can be used as the mechanism for passing parameters to a program. In fact, if you need to pass more than four parameters then you will certainly have to employ data memory to do it.

Let's rewrite the SecondOrderEq.txt example program once more, this time requiring the human operator to place his intended x axis value into data memory location 0 before he initiates the program. The new program is shown below:

; file SecondOrderEq3.txt ; This RPN calculator program computes the second order eq.: ; y = f(x) = 1x^2 + 2x + 3 ; To run the program enter an x value into data memory location 0 ; (register 0) and then type GSB 0 (alternatively, type ; GTO .00 followed by R/S). When the program stops the ; corresponding y value is visible in the X stack location. LBL 0 ; program entry point, at this time Reg[0] = x RCL 0 ; so now X = x x^2 ; X = x^2 +2. ; X = 2, Y = x^2 ENTER ; X = 2, Y = 2, Z = x^2 RCL 0 ; X = x, Y = 2, Z = x^2 * ; X = 2x, Y = x^2 +3. ; X = 3, Y = 2x, Z = x^2 + ; X = 2x+3, Y = x^2 + ; X = x^2+2x+3 R/S ; we're done

Remember that to employ this program to solve the second order equation for the y value corresponding to x = 5 you must use the keystroke sequence 5, STO 0, GSB 0.

I am showing you all these alternatives so you will understand that there are many ways to accomplish a given programming task. In general it is better to design a more complicated program if this will reduce the complexity visible to the operator of that program. This makes sense because programs are written once but run many times. Using this yardstick we would reject this latest SecondOrderEq3.txt example program since the operator is burdened with more keystrokes to initiate the program.

I have mentioned that you can also access data memory using something called indirect addressing but I have not yet explained this. I am going to delay discussing this technique until the next section where we discuss loops. In a program loop, indirect addressing can do something direct addressing cannot: a single statement can interact with any number of data memory locations.


( 13 )

Loops

We have already seen infinite loops in the IncrementForever example programs. But those program loops weren't particularly worthy, as no useful work was being done. We motivated the SecondOrderEq example programs by imagining that you had an assignment to plot the second order equation:

y = f(x) = x2 + 2 * x + 3

for the 21 data points over the range of integers from x = -10 to +10. We then wrote a few flavors of the program that solved for a single data point each time you ran the program. But a program loop, which is a structure that causes a certain section of the program to repeat over and over, can simplify the operator's job of getting his 21 data points. If we ask the operator to use the SecondOrderEq2.txt program he will need to make at least 3 * 21 keystrokes to get his 21 results because he must enter each x axis value and then click GSB 0.

But because the x axis values are predictable we can write a new program which will automatically advance the x axis value and thus save the operator keystrokes. The operator would start this new program by typing -10, GSB 0 and would then receive the y data point corresponding to x = -10. By merely clicking R/S (Run/Stop) to re-initiate the program where it left off, the operator would receive the y data point corresponding to x = -9. Additional R/S keystrokes would produce additional data points.

All we need to do to accomplish this is to increment x by 1 before looping back to the beginning of the program. Although we could store x on the operand stack, I will show you a program that stores x in data memory as this arrangement should be simpler to understand. The new SecondOrderEq4.txt program is shown below:

; file SecondOrderEq4.txt ; This RPN calculator program computes the second order eq.: ; y = f(x) = 1x^2 + 2x + 3 ; To run the program enter an x value into the X stack location ; and then type GSB 0 (alternatively, type GTO .00 followed ; by R/S). When the program stops the corresponding y value ; is visible in the X stack location. You can then get additional ; y values simply by clicking on R/S. Each new y value ; corresponds to an x value 1 larger than the former x value. ; This program employs Reg[0] to hold the sequence of x values. LBL 0 ; program entry point, at this time X = x STO 0 ; preserve initial x value in Reg[0] LBL 1 ; we loop back to here for each new computation x^2 ; X = x^2 +2. ; X = 2, Y = x^2 ENTER ; X = 2, Y = 2, Z = x^2 RCL 0 ; X = x, Y = 2, Z = x^2 * ; X = 2x, Y = x^2 +3. ; X = 3, Y = 2x, Z = x^2 + ; X = 2x+3, Y = x^2 + ; X = x^2+2x+3 R/S ; show result to operator RCL 0 ; recall original x +1. ; increment x by 1 + STO 0 ; save new x GTO 1 ; loop back for another iteration

The following four new lines at the end of the program increment the x value by 1. Remember that we have chosen to keep the x axis value in Reg[0] (i.e., data memory location 0).

RCL 0 ; recall original x +1. ; increment x by 1 + STO 0 ; save new x

After advancing the x axis value this program branches back for another iteration (repetition of the code in the loop). This is accomplished by the GTO 1 statement which returns control to the LBL 1 statement. Every time we pass the LBL 1 statement we have a new x axis value and we are about to compute:

y = f(x) = x2 + 2 * x + 3

Every time the program encounters the R/S statement it stops in order to show the operator the most recent result (the new y axis value).

Programmers often use flow charts to illustrate the flow of control in their programs. A flow chart for the SecondOrderEq4.txt program would be:

Most computer programs, if viewed from 20,000 feet, look similar to this flow chart. About the only change that you sometimes see is that if the program has a way to exit from the loop then it will perform some clean-up duties before shutting down.

The SecondOrderEq4.txt program produces a new y value every time the operator clicks on the R/S key. Let's again modify this program so that the operator can continue to get additional data points without hitting any keys! The trick is to remove the R/S statement and replace it with a PAUSE statement. A PAUSE statement causes the calculator's program to halt for 1 second and then proceed. Hence each new y value will be visible in the X stack position for (at least) 1 second. If this gives you enough time to write it down then you don't have to touch the calculator at all. If you want a longer delay then insert 2 PAUSE statements. Or, if the phone rings and you want to temporarily halt the program simply click on the R/S (or any other) key. After you hang up the phone again click on R/S and the program will continue. This version can be found in the SecondOrderEq5.txt file and looks like:

; file SecondOrderEq5.txt ; This RPN calculator program computes the second order eq.: ; y = f(x) = 1x^2 + 2x + 3 ; To run the program enter an x value into the X stack location ; and then type GSB 0 (alternatively, type GTO .00 followed ; by R/S). After a moment of computation the corresponding y value ; will be visible in the X stack location. Additional y values ; corresponding to a sequence of x values spaced apart by 1 ; will continue to appear every second. ; This program employs Reg[0] to hold the sequence of x values. LBL 0 ; program entry point, at this time X = x STO 0 ; preserve initial x value in Reg[0] LBL 1 ; we loop back to here for each new computation x^2 ; X = x^2 +2. ; X = 2, Y = x^2 ENTER ; X = 2, Y = 2, Z = x^2 RCL 0 ; X = x, Y = 2, Z = x^2 * ; X = 2x, Y = x^2 +3. ; X = 3, Y = 2x, Z = x^2 + ; X = 2x+3, Y = x^2 + ; X = x^2+2x+3 PAUSE ; show result to operator RCL 0 ; recall original x +1. ; increment x by 1 + STO 0 ; save new x GTO 1 ; loop back for another iteration

Remember that calculator programs continue to run until you hit a key (any key). The only keys which will start a program running are R/S, GSB n, and GSB i (we haven't introduced this last one yet).


( 14 )

Plots

You would think that I would be tired of the SecondOrderEq series of example programs by now, but this old calculator still has a few surprises in store for us. You see, I have converted this 1970's calculator into a graphing calculator. Graphing calculators are still sold today, but they don't impress me because of their limited screen size, their lack of color, their inability to print the graph, their inability to store the graph (say in a .GIF or .BMP file) so that it can be imported into a word processor document, etc. But Windows programs do not have these limitations. So I decided to link my RPN calculator to another window that can host a plot of the data points provided by the calculator. You have already seen this cGraph window, as I employed it to demonstrate the least squares curve fits.

The linkage between the RPN calculator and the cGraph window consists of only 3 keystrokes: ClearG, SumG, and SetG. Any time you hit the ClearG key ( [image] [image]) all data points are removed from the cGraph window. Any time you hit the SumG key ( [image] [image]) the contents of the X and Y stack locations are employed as an (x,y) data point which is added to the curve. The SetG key ( [image] [image]) takes a single operand which specifies which of the overlaid curves in the cGraph window should be made active. After the ClearG keystroke the #1 data curve ( also called a "data line") is made active. As you continue to employ the SumG keystrokes your data points will be added to this #1 curve. If you want a second curve to be overlaid on the same plot then use the keystroke sequence 2, SetG. This activates curve #2. The SumG keystroke now accumulates data points onto curve #2. After adding some data points to curve #2 you can still add more data points to curve #1 by first employing the 1, SetG keystroke sequence.

The legal range for the operand of the SetG keystroke keeps changing. Immediately after a ClearG keystroke the only legal operand for the SetG keystroke is the value 1. Of course, you don't need to issue this SetG command since its consequence has already been accomplished by the ClearG keystroke. But once you have added at least 1 data point to curve #1 you can now legally employ an operand value of either 1 or 2 with the SetG key. And after you have added at least 1 data point to curve #2 you can legally employ an operand value of 1, 2, or 3 with the SetG key. Got it?

Let's practice by manually constructing a plot. Start by issuing the ClearG keystroke so we know that the graphics subsystem has been cleared. Then type:

2 ENTER 1 SumG

As soon as you typed the SumG keystroke the cGraph window became visible (if it hadn't already been visible). Anytime you want to control the visibility of the cGraph window you can employ the View/Graph option found on the menu bar. Our graph currently consists of only a single data curve (curve #1) with the single data point (x,y) = (1,2). Let's now add a second data point (x,y) = (2,3) to this same curve:

3 ENTER 2 SumG

Now that the data curve has at least two points the cGraph window can draw a line between them. By default, the cGraph window is in its auto-range mode which means that it continually chooses the range for the x and y axes so as to fit the data points it has been given. Any time you would like to control the range of the x and y axes you can RIGHT click on the cGraph window and select "Reformat Plot..." from the context menu (i.e., the right-click menu). The plot range will then be fixed until you again select "Auto Range" from the cGraph's context menu. I will not try to cover all of cGraph's capabilities here, as it has its own help pages which you can access via the Help/Help menu choice on its menu bar.

Let's now overlay a second data curve. To do this we first type:

2 SetG

to activate curve #2. We can then add two data points to curve #2:

5 ENTER 3 SumG 4 ENTER 4 SumG

At this point your cGraph window will look like:

By playing around with the options available on cGraph's context menu you can transform this rather plain looking graph into something like:

Now that we have met the cGraph window let's programmatically spit data points at it. My SecondOrderEq6.txt example program does exactly this.

; file SecondOrderEq6.txt ; This RPN calculator program computes and plots ; the second order eq.: ; y = f(x) = 1x^2 + 2x + 3 ; for an infinite sequence of x values starting with ; whatever x value was placed into the X stack location ; before the program was initiated. ; To run the program initialize the X stack location with ; an x axis value and then type GSB 0 (alternatively, ; type GTO .00 followed by R/S). Each new data point will ; have an x axis value 1 larger than the prior data point. ; This program employs Reg[0] to hold the sequence of x values. LBL 0 ; program entry point, at this time X = x STO 0 ; preserve initial x value in Reg[0] ClearG ; clear any prior graph LBL 1 ; we loop back to here for each new computation x^2 ; X = x^2 +2. ; X = 2, Y = x^2 ENTER ; X = 2, Y = 2, Z = x^2 RCL 0 ; X = x, Y = 2, Z = x^2 * ; X = 2x, Y = x^2 +3. ; X = 3, Y = 2x, Z = x^2 + ; X = 2x+3, Y = x^2 + ; X = x^2+2x+3 = y RCL 0 ; prepare for SumG by arranging X = x, Y = y SumG ; add data point to graph (this statement also ; opens the cGraph window) +1. ; increment x by 1 + STO 0 ; save new x GTO 1 ; loop back for another iteration

Notice that SecondOrderEq6.txt has a SumG statement in the spot where SecondOrderEq5.txt had a PAUSE statement and SecondOrderEq4.txt had a R/S statement.

After loading this program I would suggest you start it via the keystroke sequence: -500 GSB 0; that is:

The program will then run forever, sketching out an ever increasing parabola. If you don't like watching all the jitter caused by cGraph's continual auto-ranging to the latest data, simply select the plot axes yourself using cGraph's "Reformat Plot..." context menu choice and then re-run the program. For example, for the horizontal axis select 5 tick marks with a minimum tick of -500 and a maximum tick of 500. For the vertical axis select 6 tick marks with a minimum tick of 0 and a maximum tick of 200000. Then re-run the program from the start with an initial x axis value of -500. Remember that this program runs forever. We will have yet one more version of this long-running series of SecondOrderEq example programs and that one will know how to stop after generating some reasonable number of data points.


Spirograph

( 15 )

Sinusoids and Spirographs

I have now taught you enough about programming the RPN calculator that you should be ready to write your first program from scratch. Programs that generate plots are obviously fun to watch so let's create one of those. Instead of a second order equation (which results in a parabolic shape) your assignment is to write a program that draws the sine wave given by the following equation:

y = sin(x)

You study the sin() function in a trigonometry class where you learn that this mathematical function describes the sinusoidal oscillation that we can observe in the movement of a pendulum, the reed on a clarinet, etc. We want to plot this equation for the infinite sequence of x axis values 0, 1, 2, etc. Hence the structure of the new DrawSineWave.txt program will be largely identical to the SecondOrderEq6.txt program we just studied except the new program won't require any passed parameters. You can either generate the new program using Notepad or else type it in using the calculator's keypad (remember to clear the old program first). Once you have a new x axis value in the X stack location you simply employ the sin mnemonic (which corresponds to the [image] [image] keystroke) to get the corresponding y axis value. Then arrange the X and Y stack locations to hold the x and y axis values before issuing the SumG command.

You can do this. Give it your best shot. My solution required only 13 statements and over half of those were simply carried over from SecondOrderEq6.txt (even professional programmers usually accomplish a new assignment by using a lot of cut and paste from prior programs). Don't just skip past this exercise or you won't learn to program. Think for a while and try some wild guesses if the solution isn't immediately obvious. Debug your attempt by single-stepping your program. Your observations on what the program is and what it is not doing will give you ideas on what to try next. You can't hurt your computer or this simulator. Only when you are exhausted do I give you permission to look at my solution which you can find in the "Solutions" folder.

Another function that you study in trigonometry is the cos() function which results in the cosine waveform which is quite similar to the sine waveform that you have now seen thanks to the DrawSineWave.txt program. Together, the sin() and cos() functions allow you to convert Cartesian coordinates (i.e., x/y coordinates) to polar coordinates (rho, theta) where rho is the radial distance from the origin and theta is the angle measured counterclockwise from the positive x axis. Every point can be described either by an (x,y) pair or a (rho,theta) pair as shown in the drawing below:

It is easier to describe curves that have circular symmetry using polar coordinates rather than Cartesian coordinates. For example, a circle of radius A centered on the origin can be described in polar form by the parametric equation shown below where i is the parameter that takes on the infinite sequence of integer values 0, 1, 2, ...

rho = A theta = i

You don't often see equations simpler than that! In Cartesian (also called "rectangular") coordinates this same circle is described by:

x = A * cos(i) y = A * sin(i)

Since the cGraph window can only accept points described by Cartesian coordinates, we have no choice but to employ the more complicated set of equations. You can find an RPN calculator program that draws a circle of radius A in the DrawCircle.txt file. This program requires 1 passed parameter, the desired radius A. To get a circle of radius 10, you would initiate this program via the keystroke sequence 10, GSB 0.

I kind of like watching the plots appear in the cGraph window as the cGraph software continually re-selects the best ranges for the x and y axes. But if you don't like this Auto Range feature then you can shut it off via the "Reformat Plot..." menu choice found on the cGraph window's menu bar.

When the circle is finished it may look more like an oval than a circle. This is because the cGraph window allows you to drag its width and height to any value and hence the plot's aspect ratio is not guaranteed to be unity. A unity aspect ratio means that 1 unit on the y axis covers the same number of pixels vertically as 1 unit on the x axis covers horizontally. Use your mouse to resize the cGraph window until you see a nice circle.

Once we can draw a circle it takes only a small change to get a spiral. A spiral is a circle whose radius is always decreasing. We can accomplish this using the following parametric equations:

x = .999i * A * cos(i)

y = .999i * A * sin(i)

When the i parameter has the value 0 the radius of the spiral will be .9990 * A = 1 * A = A. When i =1 the radius will be .9991 * A = .999 * A. When i =2 the radius will be .9992 * A = .999 * .999 * A = .998 * A. As i continues to increase the radius will continue to decrease thus distorting the circle into a spiral.

Start with my DrawCircle.txt example program and modify it to create a DrawSpiral.txt program that employs these last two equations. If you get stuck you can find my solution in the "Solutions" folder.

Can you predict the shape that will be generated by the following parametric equation?

x = sin ( 3 * i ) * cos( i ) y = sin ( 3 * i ) * sin( i )

This looks like our standard circle equation except the radius term is now sin(3i). This means that the radius will be changing in a sinusoidal fashion as we draw out the circle. Run my DrawFlower.txt (oops, I gave the answer away) example program to see what this small change causes.

Okay, let's move on to something even more flowery: spirographs (which mathematicians call "epicycloids"). Besides the spirograph toy, another place you have seen this complicated shape is when watching a wheel mounted bicycle reflector at night (assuming you are looking at a right angle to the path of the bicycle).

A Spirograph is formed by rolling a first circle (called the moving circle) inside or outside of another circle (called the stationary circle). The pen is attached to the moving circle at some arbitrary distance from that circle's center. If the radius of the fixed circle is R, and the radius of the moving circle is r, and the offset of the pen from the center of the moving circle is p, then the resulting curve drawn by the pen is described by:

x = ( R+r )*cos( i ) - p*cos( ( ( R+r ) / r ) * i ) y = ( R+r )*sin( i ) - p*sin( ( ( R+r ) / r ) * i )

Now that's what I call an equation! As before, i is the angle which starts at 0 degrees and continues incrementing forever. Let's not attempt to analyze or derive this equation, let's just enjoy the pictures it produces. Load up my DrawSpirograph.txt example program and run it for the following test cases:

R =  75, r = -30, p =  60
R =  75, r = -32, p =  60
R =  60, r = -16, p = -15
R =  90, r =   1, p = 105
R =  90, r =   2, p = 105
R =  89, r =   1, p = 105
R = 100, r =  48, p =  60

The keystroke sequence that will initiate the first test case listed above is:

75 ENTER 30 CHS ENTER 60 GSB 0

Here are some of the pretty pictures I observed using the particular test cases I have already listed. Remember that the cGraph window gives you complete control of the plot format so you can change the default pen color and plot range.

I list below a URL from the University of California at San Diego where you may still be able to find a web site that provides a Java applet that visually constructs the spirograph plots so you can see why they turn out like they do. As with all Java applets, it takes a while for the necessary code to be downloaded onto your computer, so be patient.

http://math.ucsd.edu/~dlittle/java/SpiroGraph.html


( 16 )

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:

  • STO i
  • RCL i
  • STO+ i
  • STO- i
  • STO* i
  • STO/ i
  • GTO i
  • GSB i

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:

7 STO 0 STO 1 STO 2 STO 3 ...

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:

LBL 0 7 STO ? GTO 0

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.

; file FillMemory.txt ; This RPN calculator program combines looping and indirect addressing ; to populate every data memory location (register) with the ; value 7. Note that since register 0 is employed to accomplish ; indirect addressing, we can't initialize it to 7 until ; the very end. LBL 0 ; the start of the program 29 ; initialize register 0 which must be STO 0 ; used for indirect addressing LBL 1 ; define a label so we can later branch here 7 ; the value we use to initialize all of data memory STO i ; store X in register indicated by register 0 DSZ ; decrement contents of register 0 and... GTO 1 ; ...go to LBL 1 if register 0 is not zero 7 STO 0 ; use direct addressing to initialize register 0 RTN

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.

; file ThirtySquares.txt ; This RPN calculator program combines looping and indirect addressing ; to store the squares of the numbers 0-29 in registers 0-29. LBL 0 ; the start of the program 29 ; we do the work in reverse so we can employ DSZ STO 0 ; prepare contents of indirection register LBL 1 ; define a label so we can later branch here RCL 0 x^2 STO i ; store x^2 in register indicated by register 0 DSZ ; decrement contents of indirection reg and... GTO 1 ; ...go to label 1 if register 0 is not zero RTN

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:

1 ; initial value for register 0 STO 0 LBL 1 ; top of the loop <the body of the loop goes here> DSZ GTO 1 ; branch back for another iteration RTN

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.

; file SecondOrderEq7.txt ; This RPN calculator program computes and plots ; 100 (x,y) data points for the second order eq.: ; y = f(x) = 1x^2 + 2x + 3 ; The first data point uses whatever x axis value ; was placed into the X stack location before the ; program was initiated via GSB 0 and all subsequent ; x axis values are spaced apart by 1. ; This program employs Reg[1] to hold the sequence ; of x values and Reg[0] to control how many times ; the loop iterates. LBL 0 ; program entry point, at this time X = x STO 1 ; preserve initial x value in Reg[1] ClearG ; clear any prior graph 100 ; initialize Reg[0] STO 0 RCL 1 ; return x to X LBL 1 ; we loop back to here for each new computation x^2 ; X = x^2 +2. ; X = 2, Y = x^2 ENTER ; X = 2, Y = 2, Z = x^2 RCL 1 ; X = x, Y = 2, Z = x^2 * ; X = 2x, Y = x^2 +3. ; X = 3, Y = 2x, Z = x^2 + ; X = 2x+3, Y = x^2 + ; X = x^2+2x+3 = y RCL 1 ; prepare for SumG by arranging X = x, Y = y SumG ; add data point to graph (this statement also ; opens the cGraph window) +1. ; increment x by 1 + STO 1 ; save new x DSZ ; decrement Reg[0] and test if zero GTO 1 ; loop back for another iteration

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.


( 17 )

Factorials

Let's now write a program which can compute the factorial of any positive integer n. The value of n factorial (usually written as n!) is defined as:

n! = n * (n-1) * (n-2) * ... * 3 * 2 * 1

Many calculators have factorial keys but our RPN calculator does not. That is not a problem since we can just write a program to compute a factorial. Clearly this program is going to need to allow the operator to specify the desired factorial at run-time, and hence it will need to accept a passed parameter from the operator. So we will write a program that computes the factorial of whatever positive integer is placed into the X stack location before the program is initiated.

We will base our program upon a loop which we iterate through n times. We will employ the DSZ statement to control this loop and hence we must employ register 0 to hold an integer which counts from n to 0. As we discussed in the last section, even though there are n+1 integers between n and 0, the body of the loop will only be executed n times, which is just what we want.

Before entering the loop we will initialize a particular data memory location with the value 1. Then during the first iteration of the loop we will multiply the contents of this data memory location by n, thereby computing the value 1 * n. During the next iteration of the loop we will multiply the contents of this data memory location by n-1, thereby computing the value n * (n-1). During the next iteration of the loop we will multiply the contents of this data memory location by n-2. During the final iteration of the loop we will be multiplying by 1, thereby computing the value n * (n-1) * (n-2) * ... * 2 * 1.

You can find the factorial program in the Factorial.txt file and it is reprinted below:

; file Factorial.txt ; This RPN calculator program computes the factorial of whatever ; positive integer n it finds in the X stack position. ; Implementation: We use register 0 to count down from n to 0 ; (employing the DSZ instruction) and we build the answer ; in register 1. LBL 0 ; the start of the program INT ; toss any fractional portion x<0 ; check for an illegal passed parameter GTO 2 ; unfortunately we can't signal this error very well x=0 ; check if n = 0 GTO 2 ; special case code computes 0! = 1 STO 0 ; copy n to register 0 1 STO 1 ; initialize register 1 with 1 LBL 1 ; start of program loop RCL 0 STO* 1 ; multiply contents of register 1 with contents of X stack location DSZ GTO 1 RCL 1 ; answer is in register 1 RTN LBL 2 1 ; 0! = 1 RTN

At the beginning of this program are some statements that are designed to protect the program from bad data. The factorial operation is only defined for positive integers. What should the program do if it finds a non-integer or negative value in the X stack location? Most computer programs are able to generate messages to the operator to alert him to errors such as these, but RPN calculator programs can only generate numbers. Therefore this program employs the INT statement to toss any fractional portion of the value found in the X stack location. And if this value is negative the program branches to a special label (LBL 2) which displays the "result" as 1. Hopefully the operator will catch on when he sees the result of all negative factorials being reported as 1. This code is an example of defensive programming and is an excellent idea. All of us have seen programs crash when they encountered something their designer had not protected against.

To decide if the passed parameter is negative the program employs the x<0 instruction. This is an example of one of the eight conditional statements available on the calculator. The other conditional statements are x>0, x=0, x!=0, x<=y, x>y, x!=y, and x=y. All these conditional statements work similar to each other in that if the condition is false then the program skips the next instruction in program memory. So if the statement x<0 is false, then the GTO 2 statement will be skipped. But if x is either less than or equal to zero then the program will branch to LBL 2 where it attempts to alert the operator to the problem.

After the defensive code that checks the legality of the n parameter passed to the program in the X stack location, the program next checks if n = 0. If so the program jumps to LBL 2 to display the result as 1 (which is the correct answer).

Next in the program appear 2 initialization steps. We initialize register 0 with the passed parameter n. We must employ register 0 for this purpose since the DSZ statement is linked with register 0. And we initialize register 1 with the value 1. During each iteration of the loop we will multiply the value in register 1 by a new factor such as (n-1) or (n-2) etc. So we must initialize register 1 to unity before we enter the loop.

With these preliminaries out of the way we can finally enter the program loop. This is the code between the LBL 1 statement and the GTO 1 statement that immediately follows (and hence is linked to) the DSZ statement. The only new statement in this section is an example of the generic STO* n instruction which replaces the contents of register n with the prior value in register n multiplied by the contents of the X stack location. This is the statement that, during each iteration of the loop, tacks on another factor in the ever expanding string of factors n * (n-1) * (n-2) etc.

Note that this program has two RTN statements, which means it can halt in two different places. This is not unusual in programs, and is illustrated in the following flow chart for the program:

Remember that this program requires a passed parameter and hence to employ this program to compute, say 5!, you must initiate it via a keystroke sequence such as 5, GSB 0. Single step through the program and watch how 5! gets built in register 1. During the program initialization you will see register 1 getting populated with the value 1. Then during the first iteration of the program loop the value in register 1 will be multiplied by 5 (this value having been taken from register 0). During the next iteration of the loop the value in register 1 will be multiplied by 4 (this value having been taken from register 0). Eventually register 1 will hold 1 * 5 * 4 * 3 * 2 * 1 = 120 = 5!

You can use the program to discover that 69! = 1.71e+98 which is the number 1.71 multiplied by 1098. This means this number has 99 columns in it and hence is huge. But if you next calculate the factorial of 70 or any number larger than 70 the answer will always come back as 9.999999999e+99. In these cases the program is actually failing. It turns out that 9.999999999e+99 is the largest number that the calculator can handle. All calculators, and computers for that matter, have a maximum and minimum number that they can deal with.


( 18 )

Subroutines

A subroutine is a specially packaged section of your program that holds code that you wish employ at many different times while your program is running. That is, if after writing a program you notice that you are doing something over and over again then repackage that repeated code into a subroutine. The code will then exist at only one location in your program which makes your program smaller. The rest of the program can call (enter) the subroutine whenever you want that code executed.

The use of subroutines improves your program. Back when you had the code repeated at different places in your program you might have made a typing error and not really had the exact same code in all those places. But after you have repackaged the code as a subroutine you are guaranteed that the code that executes at those different times is identical since it now only exists in one location. The other reason that using subroutines improves your programs becomes apparent the day you discover you need to make a change in that section. If the code is repeated then you must find all those locations and make the change in each one of them. But if the code is packaged as a subroutine then there is only one place you must make the change. So the use of subroutines improves the quality and reliability of your code.

So how do you package code as a subroutine? It's simple: precede the code with an unused LBL n statement and end it with a RTN statement.

We have already seen the GTO n statement which allows you to jump around in your program. The statement that is used to enter a subroutine is the GSB n statement. Yes, we have seen the GSB n statement too but up until now we have only executed it from the calculator's keyboard and never placed it inside a program. The difference between the GTO n and GSB n statements is that the latter has an automatic mechanism to return to the original location in the program. This is the critical feature of a subroutine: it can be called from anywhere in your program and will return back to the original location once it completes (that is, once the RTN statement is encountered).

So how is this magic accomplished? Well, let's try to invent the mechanism ourselves. We have already seen how register 0 is used as the indirect register, to hold addresses that are employed with the STO i, RCL i, etc. instructions. Let's imagine that in a similar spirit we claim register 1 as the return register. Anytime we enter a subroutine via a GSB n statement we will populate register 1 with the program step (PC value or program memory address) we need to return to once the subroutine completes. This would work as illustrated below:

25 * ; arbitrary statement before the subroutine 26 GSB 7 ; call into subroutine #7 27 + ; arbitrary statement after the subroutine ... 54 LBL 7 ; start of subroutine #7 55 x^2 ; body of the subroutine 56 RTN ; end of subroutine #7

We are executing the program at statement 26 when we discover a GSB n statement. Hence we need to branch to the LBL 7 statement but we also need to remember how to get back to statement 27. To accomplish this we populate register 1 with the value 27. We then begin executing at statement 54 (just as if a GTO statement had brought us there). When we get to the RTN instruction at statement 56 we need to return to wherever the subroutine had been called from. So we look in register 1 and discover that we need to return to statement 27, where we continue executing the program.

This design works, but only in the case of non-nested subroutines. A nested subroutine is the situation where the code in one subroutine calls another subroutine. We want this to be legal, but it means that register 1 will already be populated at a time when we need to preserve a return address. While we are executing within the nested subroutine we need to preserve two return addresses which we obviously can't do using only register 1.

Okay, you say. So use both register 1 and register 2 and subsequent registers to hold return addresses, the number of registers depending upon how deep you want to be able to nest subroutines. Sorry, this design offers sufficient storage space but no means for the RTN address to know which of these registers holds the program memory address it should return to. You see, every one of the nested subroutines ends with a RTN statement and these RTN statements are all identical.

The solution to this predicament is the return address stack. See, I told you stacks were important. All computers use a return address stack to allow subroutines to nest (that is, to allow subroutines to call other subroutines). This structure is called a stack because, just like the RPN calculator's operand stack, it displays a last in, first out nature. Consider the following code that employs a nested subroutine:

25 * ; arbitrary statement before the subroutine 26 GSB 7 ; call into subroutine #7 27 + ; arbitrary statement after the subroutine ... 54 LBL 7 ; start of subroutine #7 55 GSB 8 ; body of the subroutine #7 which consists ; solely of a call to subroutine #8 56 RTN ; end of subroutine #7 ... 81 LBL 8 ; start of subroutine #8 82 x^2 ; body of the subroutine #8 83 RTN ; end of subroutine #8

When we are operating at statement 25 the return address stack is empty. Statement 26 not only transfers execution to statement 54 but also pushes (adds) the program memory address 27 onto the return address stack. The top of the return address stack always holds the program memory address where we must return when we next hit a RTN statement. At the moment the return address stack has only a single entry. Statement 26 causes execution to enter subroutine #7 which immediately calls another subroutine. So after we execute statement 55 we enter subroutine #8 and the return address stack gets two entries: program memory addresses 27 (the statement after the GSB 7 statement) and program memory address 56 (the statement after the GSB 8 statement).

Subroutine #8 squares a number and then returns. Execution of a RTN statement causes the calculator to pop (remove) a program memory address from the top of the stack and then continue executing at that location. So the RTN instruction at statement 83 pops the program memory address 56 off the top of the stack and goes there. Execution continues at statement 56 which is another RTN statement. This RTN statement pops the program memory address 27 off the top of the stack and goes there. The return address stack is then empty. This is all illustrated in the following table:

statement being return address executed stack 25 -- -- -- 26 (GSB) -- -- -- 54 27 -- -- 55 (GSB) 27 -- -- 81 27 56 -- 82 27 56 -- 83 (RTN) 27 56 -- 56 (RTN) 27 -- -- 27 -- -- --

In this example the return address stack grew to a maximum depth of 2. Most computers give the stack sufficient memory that it can grow large enough to allow deeply nested subroutines. But the RPN calculator has a fixed return address stack depth of 3, which means that a subroutine can call a subroutine which can call a subroutine but then no additional subroutines can be called until you exit from (at least) that last subroutine.

So the stack cleverly solves the problem of getting a bunch of identical RTN statements to do different things. Each RTN statement pops the return address stack, thus providing unique program memory locations where execution should continue.

The only place where you can see the RPN calculator's return address stack is in a trace file. A trace file is a plain-text (.TXT) file which logs every instruction executed by a running program. Generating a trace file is the second most common debugging technique after single-stepping. Since the generation of this .TXT file does slow the calculator somewhat the operator must specifically request the trace file when he wants it. This is accomplished via the Tracing option found on the dialog box that displays when you select View/Options from the menu bar. This option is further discussed in the section on simulator options.

We will generate a trace file when we run our next example program in order to illustrate how subroutines work. So we need to find some code that cries out for restructuring as a subroutine. The code I have selected is found in a program that computes the mathematical operation known as combination.

Mathematical combination is a simple concept. Imagine that you have a collection of 4 distinct items, such as the letters A, B, C, and D. How many ways can you select 2 of these 4 letters? Well, you can select AB, AC, AD, BC, BD, or CD. Hence there are 6 ways of selecting 2 out of 4 items. Note that we consider AB to be the same combination as BA (there is a mathematical operation called permutation where these are considered to be different). A mathematician would write:

4C2 = 6

to say that there are 6 ways of taking 2 of 4 items. The number of possible combinations, each containing n objects, that can be formed from a collection of m distinct objects is given by:

m! mCn = ------------- eq. [1] (m-n)! * n!

where m and n are positive integers and 0 <= n <= m. The value mCn is also called the binomial coefficient (you may have seen Pascal's triangle which describes the symmetry seen in a table of binomial coefficients).

We see that we need to compute 3 factorials in order to solve for mCn. Since we need to keep employing the code that computes the factorial we will repackage it as a subroutine. Conceptually, this only involves sticking a LBL n statement at the top and a RTN statement at the bottom but we will discover some complications.

Notice that this program requires two passed parameters: m and n. The obvious thing to do is to require the operator to populate both the X and Y stack locations before initiating the program via a GSB 0 keystroke. So to compute mCn you will need to use the keystroke sequence m, ENTER, n, GSB 0.

We will look at this program in two pieces. Shown below is all of the program except for the subroutine that computes the factorial. This subroutine has been given the label 5 and hence you will observe three GSB 5 statements. These are the three calls to the factorial subroutine that compute the three factorials needed for eq. [1] above. Notice also that the first thing the program does is preserve the n passed parameter in register 2 and the m passed parameter in register 3.

; file BadCombination.txt

LBL 0       ; at start of program X = n, Y = m
STO 2       ; Reg[2] = n
x<>y        ; exchange X and Y
STO 3       ; Reg[3] = m
GSB 5       ; compute m!
RCL 2       ; recall n, pushes m! to Y stack position
GSB 5       ; compute n!, upon return Y still holds m!
/           ; X = m!/n!
RCL 3       ; X = m, Y = m!/n!
RCL 2       ; X = n, Y = m, Z = m!/n!
-           ; X = m-n, Y = m!/n!
GSB 5       ; compute (m-n)!, upon return Y still holds m!/n!
/           ; m!/[(m-n)!n!]
RTN         ; we're done

Now let's look at the subroutine. You should be able to easily see that this is just the same code from our previous Factorial.txt example program repackaged as a subroutine.

LBL 5 ; this subroutine replaces the value in the ; X stack location with its factorial ; Implementation: We use Reg[0] to count down from n to 0 ; (employing the DSZ instruction) and we build the answer ; in Reg[1]. ; X = n STO 0 ; copy n to Reg[0] 1 ; X = 1, Y = n STO 1 ; initialize Reg[1] LBL 6 ; start of program loop RCL 0 STO* 1 DSZ GTO 6 RCL 1 RTN ; X = n!

This is how I originally wrote the factorial subroutine and I was sure surprised when my combination program gave the wrong answers (such as saying that 4C2 = .5). You can still observe my original attempt in the file BadCombination.txt.

When life serves you lemons, make lemonade. So let's take this opportunity to learn about debugging.


( 19 )

Debugging

In the last section we wrote a program (in the BadCombination.txt file) that DIDN'T manage to correctly compute the mathematical combination mCn which is given by:

m! mCn = ------------- eq. [1] (m-n)! * n!

Since this equation requires three factorials we decided to repackage the code from the Factorial.txt example program as a subroutine. But the program is giving wrong answers.

We need to debug the program and we will do this by single-stepping the program until we can observe the problem. When you are searching for a bug you want to first locate a simple test case that displays the bug. Our test case will be 4C2 which should give the answer 6 but is currently giving the answer .5 . You then execute the test case a single program statement at a time.

Go ahead and load BadCombination.txt into the calculator and then, in Run mode, issue the following keystrokes: GTO .00, 4, ENTER, 2, SST. At this point all the program windows will be displayed and you will have already executed the first instruction in the program. The "02 STO 2" statement will be highlighted indicating that it is the next statement to be executed. Click on SST four more times and you will find yourself within the LBL 5 subroutine which is where we compute all the factorials. We called this subroutine to compute 4! and after 24 more SST keystrokes you will find yourself at the RTN statement at the end of this subroutine with the value 24 in the X stack location. Since 4! = 24 everything has gone fine thus far. Did you notice that during those 24 SST keystrokes you repeated a loop inside the LBL 5 subroutine 4 times? These 4 loop iterations were required to compute 4! .

The next SST brings you back to the statement immediately following the GSB 5 statement that entered the factorial subroutine. The calculator knew where to return to following the RTN statement thanks to a program memory address that was found on the return address stack. We will be able to see this stack in the trace file we will generate shortly. Two more SST keystrokes take you back into the factorial subroutine to compute 2! . Fourteen more SST keystrokes again bring you to the RTN statement and since the X stack location holds 2 which is 2! we are again happy with our subroutine. Click SST one more time to return to the code that called the factorial subroutine (this is called the calling code). You are about to execute statement 08 which is a divide operation. The Y and X stack locations are supposed to hold m! and n! respectively which we already saw being computed as 24 and 2 respectively.

But wait a minute! The Y stack location holds 1 instead of 24. This means the divide operation in statement 08 will not give the correct answer. So at this point we can see that our program has gone off track. We can see there is a problem but we don't know how it happened. The customary thing to do is to restart the program and again watch the events that led up to this situation.

To restart the program we only need to repeat the keystroke sequence: GTO .00, 4, ENTER, 2, SST. Click SST 28 more times and you will be at the RTN statement with the X stack location holding the value 24. Click SST once more and you will be back at the RCL 2 statement in line 06. Click SST once more and you will have executed the RCL 2 statement that populates the X stack location with n = 2. At this time everything is perfect: m! = 24 is in the Y stack location and n = 2 is in the X stack location and we are about to call the LBL 5 subroutine which is supposed to replace the contents of the X stack location with its factorial. If this really would happen then upon the return from the subroutine the Y stack location would still hold m! = 24 and the X stack location would then hold n! = 2 and we would be ready for the divide operation in statement 08.

But this won't happen and the fault lies in the factorial subroutine. Click on SST and you will enter the factorial subroutine. Slowly keep clicking SST while you watch the value 24 in the stack. You will observe that the operation of the factorial subroutine causes the value 24 to keep climbing up through the stack until it disappears from the top of the stack. Bummer! So we conclude that although the factorial subroutine computes the factorial of the X stack value correctly, it also perturbs other numbers left in the stack by the calling code.

High-level languages such as C, C++, and Java have a mechanism that prevents subroutines from interfering with the operation of the calling code. But in the RPN calculator (and in the 8051 microprocessor we will study next) there is no such protection. Both the subroutine and the calling code want to employ the stack and they are currently getting in each other's way.

There are two ways we could solve this problem. We could place the burden upon the calling code by insisting that it leave nothing important lying around in the stack. That is, before it calls into the factorial subroutine it will have to copy all its intermediate results into data memory locations for safekeeping. This approach requires us to add a bunch of code at every place where the factorial subroutine is called. The second approach is to improve the factorial subroutine so that it does as little damage as possible to the stack. This would make the subroutine a "good neighbor" for whatever code calls it. Since the subroutine code exists only once in the program while the calling code exists 3 times, it's obvious that it would be easier to make the changes in the subroutine.

A subroutine that is created using a high-level language begins with statements that preserve the state of the computer and ends with statements that restore the state of the computer. In between is the body of the subroutine which is now free to do whatever it wants. When the subroutine returns the calling code won't be able to tell that anyone else has been messing with the computer since everything is back to normal. We could accomplish this strategy for our Factorial subroutine by beginning it with statements that copy the four numbers in the stack into four data memory locations and then ending it with statements that repopulate the stack with these same numbers.

But I am going to solve the problem using fewer statements. I studied the subroutine and saw that it was the calculator's auto-stack lift feature that was causing the value of 24 to float away through the top of the stack. Generally, the auto-lift feature is just what you want but in this case it works against us. Fortunately, I knew that the auto-lift feature is turned off for the instruction immediately following a CLX instruction (the purpose of this "clear X" instruction is to allow you to remove a mistaken entry from the X stack location). After adding two CLX statements and a Rolldown statement I had reworked the factorial subroutine such that the Y and Z stack locations upon the exit from the subroutine still held whatever values they held when the subroutine was called. With a few additional statements I could have preserved the T location as well.

The new, improved factorial subroutine is shown below. Note the new comment that states, "This subroutine preserves the original contents of the Y and Z stack positions." This type of comment alerts the caller as to what it can expect.

LBL 5 ; this subroutine replaces the value in the ; X stack location with its factorial ; Implementation: We use Reg[0] to count down from n to 0 ; (employing the DSZ instruction) and we build the answer ; in Reg[1]. This subroutine preserves the original contents ; of the Y and Z stack positions. ; X = n, Y = Yorig, Z = Zorig STO 0 ; copy n to Reg[0] 1 ; X = 1, Y = n, Z = Yorig, T = Zorig STO 1 ; initialize Reg[1] Rolldown ; X = n, Y = Yorig, Z = Zorig LBL 6 ; start of program loop CLX ; shuts off auto stack lift RCL 0 ; X = i, Y = Yorig, Z = Zorig STO* 1 DSZ GTO 6 CLX RCL 1 RTN ; X = n!, Y = Yorig, Z = Zorig

The new code can be found in the Combination.txt file. You can use it to verify that 4C2 = 6 and 43C3 = 12341. You might like to single-step the new code on the test case 4C2 to observe that the value of 24 no longer floats off the top of the stack.

My original implementation (in BadCombination.txt) displays a classic computer programming problem known as contention where two sections of code think they can both employ a certain resource (in this case, the operand stack) but they end up interfering with each other. And this example program has illustrated how detail oriented computer programming can be. The computer will do exactly what you tell it. The problem is usually that the human programmer doesn't comprehend the full ramifications of what his program tells the computer to do. Computer programmers need to be able to remember lots of little details and this is probably why computer programmers are usually in short supply and hence earn good salaries.


Tracing

( 20 )

Program Tracing

I used single-stepping to identify the problem in my BadCombination.txt example program. The other common method of debugging a program is to generate a trace file. Let's do this now. First we must request the trace file by selecting View/Options from the menu bar and then adding a checkmark to the Tracing option. On the resulting dialog we are told that the trace file will be named TRACELOG.TXT and will be found in whatever directory (folder) holds RPNCALC.EXE, which is the Windows executable for the RPN calculator. After requesting the trace file close the options dialog and then load the BadCombination.txt program. Then run the program as you normally do: 4, ENTER, 2, GSB 0. The program runs and then halts displaying the answer of .5 which is wrong.

At this point you can use Windows Explorer (i.e., Start/Programs/Windows Explorer) to show you the contents of whatever directory you chose to hold RPNCALC.EXE back when you installed this software (this location defaults to the C:\Computer Science Lab\RPNCalc folder). In this directory you will see the new file TRACELOG.TXT. By double-clicking on this .TXT file you will command Windows to activate Notepad to show you the contents of this file. Shown below are the contents of this trace file augmented with a new left-hand column that numbers every line:

Program from file 'D:\johnk\projects\RpnCalc\BadCombination.txt'
run on Tue Apr 30 11:25:13 2002

    Instruction     X        Y      Reg[0]  Subr Return Addr
1   01 LBL 0      +2.00    +4.00    +1.00      --  --  --
2   02 STO 2      +2.00    +4.00    +1.00      --  --  --
3   03 x<>y       +4.00    +2.00    +1.00      --  --  --
4   04 STO 3      +4.00    +2.00    +1.00      --  --  --
5   05 GSB 5      +4.00    +2.00    +1.00      06  --  --
6   15 LBL 5      +4.00    +2.00    +1.00      06  --  --
7   16 STO 0      +4.00    +2.00    +4.00      06  --  --
8   17 1          +1.00    +4.00    +4.00      06  --  --
9   18 STO 1      +1.00    +4.00    +4.00      06  --  --
10  19 LBL 6      +1.00    +4.00    +4.00      06  --  --
11  20 RCL 0      +4.00    +1.00    +4.00      06  --  --
12  21 STO* 1     +4.00    +1.00    +4.00      06  --  --
13  22 DSZ        +4.00    +1.00    +3.00      06  --  --
14  23 GTO 6      +4.00    +1.00    +3.00      06  --  --
15  19 LBL 6      +4.00    +1.00    +3.00      06  --  --
16  20 RCL 0      +3.00    +4.00    +3.00      06  --  --
17  21 STO* 1     +3.00    +4.00    +3.00      06  --  --
18  22 DSZ        +3.00    +4.00    +2.00      06  --  --
19  23 GTO 6      +3.00    +4.00    +2.00      06  --  --
20  19 LBL 6      +3.00    +4.00    +2.00      06  --  --
21  20 RCL 0      +2.00    +3.00    +2.00      06  --  --
22  21 STO* 1     +2.00    +3.00    +2.00      06  --  --
23  22 DSZ        +2.00    +3.00    +1.00      06  --  --
24  23 GTO 6      +2.00    +3.00    +1.00      06  --  --
25  19 LBL 6      +2.00    +3.00    +1.00      06  --  --
26  20 RCL 0      +1.00    +2.00    +1.00      06  --  --
27  21 STO* 1     +1.00    +2.00    +1.00      06  --  --
28  22 DSZ        +1.00    +2.00    +0.00      06  --  --
29  24 RCL 1      +24.00   +1.00    +0.00      06  --  --
30  25 RTN        +24.00   +1.00    +0.00      --  --  --
31  06 RCL 2      +2.00    +24.00   +0.00      --  --  --
32  07 GSB 5      +2.00    +24.00   +0.00      08  --  --
33  15 LBL 5      +2.00    +24.00   +0.00      08  --  --
34  16 STO 0      +2.00    +24.00   +2.00      08  --  --
35  17 1          +1.00    +2.00    +2.00      08  --  --
36  18 STO 1      +1.00    +2.00    +2.00      08  --  --
37  19 LBL 6      +1.00    +2.00    +2.00      08  --  --
38  20 RCL 0      +2.00    +1.00    +2.00      08  --  --
39  21 STO* 1     +2.00    +1.00    +2.00      08  --  --
40  22 DSZ        +2.00    +1.00    +1.00      08  --  --
41  23 GTO 6      +2.00    +1.00    +1.00      08  --  --
42  19 LBL 6      +2.00    +1.00    +1.00      08  --  --
43  20 RCL 0      +1.00    +2.00    +1.00      08  --  --
44  21 STO* 1     +1.00    +2.00    +1.00      08  --  --
45  22 DSZ        +1.00    +2.00    +0.00      08  --  --
46  24 RCL 1      +2.00    +1.00    +0.00      08  --  --
47  25 RTN        +2.00    +1.00    +0.00      --  --  --
48  08 /          +0.50    +2.00    +0.00      --  --  --
49  09 RCL 3      +4.00    +0.50    +0.00      --  --  --
50  10 RCL 2      +2.00    +4.00    +0.00      --  --  --
51  11 -          +2.00    +0.50    +0.00      --  --  --
52  12 GSB 5      +2.00    +0.50    +0.00      13  --  --
53  15 LBL 5      +2.00    +0.50    +0.00      13  --  --
54  16 STO 0      +2.00    +0.50    +2.00      13  --  --
55  17 1          +1.00    +2.00    +2.00      13  --  --
56  18 STO 1      +1.00    +2.00    +2.00      13  --  --
57  19 LBL 6      +1.00    +2.00    +2.00      13  --  --
58  20 RCL 0      +2.00    +1.00    +2.00      13  --  --
59  21 STO* 1     +2.00    +1.00    +2.00      13  --  --
60  22 DSZ        +2.00    +1.00    +1.00      13  --  --
61  23 GTO 6      +2.00    +1.00    +1.00      13  --  --
62  19 LBL 6      +2.00    +1.00    +1.00      13  --  --
63  20 RCL 0      +1.00    +2.00    +1.00      13  --  --
64  21 STO* 1     +1.00    +2.00    +1.00      13  --  --
65  22 DSZ        +1.00    +2.00    +0.00      13  --  --
66  24 RCL 1      +2.00    +1.00    +0.00      13  --  --
67  25 RTN        +2.00    +1.00    +0.00      --  --  --
68  13 /          +0.50    +2.00    +0.00      --  --  --
69  14 RTN        +0.50    +2.00    +0.00      --  --  --

A lot of this trace file should make perfect sense. For example, the first instruction that was executed was the LBL 0 instruction located in program memory location 01. This was because we initiated the program via GSB 0. The calculator then continued executing instructions in order until it came to the GSB 5 statement. Hence line 6 of the trace file shows that execution jumped from program memory location 05 to program memory location 15. Furthermore, the far right-hand column shows that the GSB 5 statement caused the 3 deep subroutine return address stack to be populated with the program memory address 06, which is the statement following the GSB statement. This is where execution should continue when we reach the RTN statement in the subroutine.

So we are now inside the factorial subroutine. The sequence of statements that then get executed is illustrated below. We can very clearly see that we iterate 4 times through the loop while computing 4!

6 15 LBL 5 (start of factorial subroutine) 7 16 8 17 9 18 10 19 LBL 6 (first iteration of loop inside 11 20 the factorial subroutine) 12 21 13 22 14 23 15 19 LBL 6 (second iteration of loop inside 16 20 the factorial subroutine) 17 21 18 22 19 23 20 19 LBL 6 (third iteration of loop inside 21 20 the factorial subroutine 22 21 23 22 24 23 25 19 LBL 6 (fourth iteration of loop inside 26 20 the factorial subroutine 27 21 28 22 29 24 30 25 RTN (we leave the factorial subroutine)

I want to zoom in on the following lines taken from the trace file:

Instruction X Y Reg[0] Subr Return Addr 29 24 RCL 1 +24.00 +1.00 +0.00 06 -- -- 30 25 RTN +24.00 +1.00 +0.00 -- -- -- 31 06 RCL 2 +2.00 +24.00 +0.00 -- -- --

Notice how the subroutine return address stack has been populated the entire time we are inside the factorial subroutine but then when we hit the RTN statement the program memory address 06 is removed from the stack. This is why line 31 of the trace file shows that execution continues at program memory address 06. Note also that the Y stack location holds 24 = 4! so everything is okay at the moment.

Lines 32 through 47 of the trace file show the next call into the factorial subroutine. This time we only iterate twice through the loop since we are only computing 2! = 2. And line 47 shows what makes the program in the BadCombination.txt file bad: the Y stack location no longer holds 4! = 24.

Ignoring this fault, line 52 of the trace file shows that the program makes a third call into the factorial subroutine to compute (4-2)! . Lines 53 through 67 show that we only iterate twice through the loop since (4-2)! = 2!

Now take a look at the far right-hand column of the trace file. This column is labeled "Subr Return Addr" and shows the contents of the 3 deep stack. There are only 3 moments during the running of this program that this stack holds anything: these are the 3 moments when we are inside the factorial subroutine (i.e., LBL 5). Even though it is the same subroutine which is called, the return address is different. This is because the subroutine is called from 3 different places. Hence the RTN statement at the end of the factorial subroutine causes us to return to statements 06, 08, and 13. Again, the RTN statement is the same each time, but the contents of the return address stack are different each time execution reaches the RTN statement.

Once we discovered the flaw in the BadCombination.txt example program, I then provided you with a better program, Combination.txt, that does not suffer from the contention problem that exists in BadCombination.txt. But while Combination.txt correctly computes 4C2 = 6, it does not correctly compute 73C4. The problem is that the algorithm used in Combination.txt is a straight forward implementation of eq. [1], which I repeat below:

m! mCn = ------------- eq. [1] (m-n)! * n!

This equation involves three factorials and we have already pointed out that because the RPN calculator cannot deal with any number larger than 9.999999999e+99 it cannot correctly compute any factorial larger than 69!. To use eq. [1] to compute 73C4 requires that we compute 73!, 4!, and 69! and hence we get the wrong answer. Yet the right answer for 73C4, which is 1088430, is not such a large number. The final result of eq. [1] is a reasonable sized number because we take an immense numerator (73!) and divide it by an almost as immense denominator (69! * 4!).

If you are suspecting that there should be a way to write an RPN calculator program that can handle 73C4 you are correct. In fact, such a program can be found in the Combination2.txt file. This program is rather involved because it doesn't do the obvious thing implied by eq. [1]. The most hardy of you will be able to figure out the operation of the code in Combination2.txt from the comments that I have provided in that file. The rest of you can skip this example program since it doesn't introduce anything new and is getting so complicated that it would be better written in a higher-level language such as C, C++, or Java. But all of you should hear me say that a smarter algorithm always makes for a better program.

I'll close this section with some implementation details of the tracing capability of the RPN calculator. If a calculator program that is being traced runs indefinitely then the trace file could grow without bound. Eventually your hard disk drive would be full and the Windows operating system would croak. To prevent this I don't allow the trace file to grow larger than 1 megabyte. Everytime the TRACELOG.TXT file reaches 1 MByte I copy its contents over to TRACELOG_OLDER.TXT and empty TRACELOG.TXT. Hence, when you eventually inspect the contents of TRACELOG.TXT you will be looking at no more than the most recent 1 MByte of program activity. It's possible that older tracing activity can be found in the TRACELOG_OLDER.TXT file. With this design, tracing cannot consume more than 2 MBytes of your hard disk which is an inconsequential amount. You will notice that the trace file is always named TRACELOG.TXT . I chose to not name the trace file after the name of the calculator program because then your hard drive could get littered with multiple trace files as you traced different calculator programs.


( 21 )

Successive Approximation

Successive approximation algorithms are those algorithms that continue to better approximate some desired result all the longer you allow them to run. You typically iterate these algorithms meaning that you repeat the same processing step over and over again. How do you get such an algorithm started? With an initial guess (and you thought guessing wasn't allowed in math!). It doesn't matter where you start these algorithms since they converge on (head to) the correct answer. If your initial guess is particularly bad then more iterations will be required to achieve a given accuracy, but other than that it doesn't matter where you start.

We are going to write a program which implements a successive approximation algorithm for computing the square root of a number. Most calculators (including our RPN calculator) have a keystroke dedicated for computing square roots and the algorithm that we will study could very well be used internally by the calculator for that keystroke. If we are searching for the square root of n then we select an initial guess A and plug n and A into the following equation:

B = ( A + ( n/A ) ) / 2

This equation solves for B, which is the new, improved approximation for the square root of n. Until we are happy with this approximation, we continue repeating the process by copying the new B value into A and then re-executing the same equation to get a new B value. A few iterations of this algorithm for finding the square root of n=9 starting from an initial guess of A=1 are shown below:

A B --------- --------- 1.00000 5.00000 5.00000 3.40000 3.40000 3.02353 3.02353 3.00009

So after only 4 iterations we are within a very small percentage of the true answer that sqrt(n) = sqrt(9) = 3. To prove that the initial guess really doesn't matter I will repeat these first 4 iterations, this time using A = 10 for the initial guess:

A B --------- --------- 10.00000 4.95000 4.95000 3.38409 3.38409 3.02180 3.02180 3.00008

Since we knew that the square root of 9 was 3 it was pretty easy to tell when we had run the algorithm enough to produce an acceptable result. But we want to be able to write a program that can solve for any square root and this means we can't employ knowledge of the answer in the program.

Our program needs a way to decide that it has run long enough and can quit. We might decide to always run the algorithm for a fixed number of iterations, such as 10. But then the quality of the final result would depend upon how close the initial guess was to the actual square root. There is a better way to know when to stop iterating.

When our initial guess was 1 the algorithm produced a new guess of 5, which means the guess changed by 4 out of 1 or 400%. When the initial guess was 5 the algorithm produced a new guess of 3.4, which means the guess changed by 1.6 out of 5 or 32%. When the initial guess was 3.4 the algorithm produced a new guess of 3.02353 which means the guess changed by .37647 out of 3.4 or 11%. When the initial guess was 3.02353 the algorithm produced a new guess of 3.00009 which means the guess changed by .02344 out of 3.02353 or .7%. So as we performed the algorithm for the first 4 iterations the percent change in the answer went from 400% to 32% to 11% to .7%.

Does this give you the idea that the program could continue looping until the new guess is basically not much different than the old guess, which must indicate that both guesses are pretty close to the true answer? Note that we are comparing two consecutive guesses to each other instead of comparing the latest guess to the true answer (which we don't know).

If you stop iterating the algorithm when two consecutive approximations are within, say, .001 of each other then the program will produce excellent results for the sqrt(n) as long as the true sqrt(n) is much larger than .001 . But this program will halt too soon when asked to compute, say, sqrt(.00000001). So we want to continue to iterate until the change in the approximation is a small PERCENTAGE rather than a small number.

Another cute aspect of this method of computing a square root is that it only requires that the hardware support the 4 basic math operations of +, -, *, and /. And, as we will learn in the next section of our curriculum where we study an Intel 8051 microprocessor, these are the only math functions built into a microprocessor. Hence it is a software algorithm such as the successive approximation algorithm shown here that must provide other results such as square roots, sine functions, logarithms, etc.

My implementation of this successive approximation algorithm for computing a square root can be found in the SquareRoot.txt file. Try it out! To compute the square root of 8 you would use the keystroke sequence 8, GSB 0. Remember that you can single step this (or any other) program to watch it do its thing.


( 22 )

Review

Let's review the most important programming concepts that were introduced via the RPN calculator:

  1. We introduced the concept of a program which is just a sequence of statements.
  2. We introduced statements which are textual representations of computer operations. Statements have syntax rules and are built upon the machine's instruction set.
  3. We introduced the instruction set which is the list of primitives (most basic operations) of a programmable device. For the RPN calculator "STO" is a member of the instruction set and "STO 5" is a legal statement built upon the STO primitive.
  4. We introduced the stack. A stack works the same as the spring-loaded stack of trays you find in a school cafeteria: students can only remove the top tray and the kitchen staff can only add newly washed trays to the top of the stack. The RPN calculator has a 4 deep operand stack. All operands which are to be used for computations must first make it into the stack before they can be operated upon. New numbers can only enter the stack thru the X location. All one-operand functions use the number in the X stack location for their operand. All two-operand functions employ the numbers in the X and Y locations.
  5. We introduced the distinction between data memory and program memory. The RPN calculator has exactly 98 program memory locations and exactly 30 data memory locations. Program memory holds operation codes which represent the operations the calculator can perform. Data memory holds (duh) data which are the operands that operations act upon. You cannot operate upon things pulled from program memory and you cannot place statements in data memory.
  6. We introduced one-operand functions and two-operand functions. An example of a one-operand function is 10^x and an example of a two-operand function is y^x.
  7. We introduced postfix notation via a reverse Polish notation calculator. The advantage of postfix notation is that the operation can be performed as soon as you specify the operation. That is, all necessary operands will already be available.
  8. We introduced branching which has the two types unconditional branching and conditional branching. The GTO n statement results in an unconditional branch to the label n. The x>0 statement paired with a GTO n statement forms a conditional branch. Conditional and unconditional branch statements can cause a program to loop which means it repeatedly executes the same section of the program. It is looping that allows a 10 statement program to produce an unlimited number of results. Conditional branches are used to exit from the loop when a certain result has been achieved. When a loop has a defective exit it becomes an infinite loop which is one of the first program bugs most programmers discover.
  9. We introduced subroutines which become function calls in higher-level languages. A subroutine is used to hold a fragment of code that is useful at multiple times within a program. Hence by writing these statements only one time and jumping to them when necessary you save typing and achieve a bug free program faster.
  10. We introduced debugging via single stepping and program tracing. Single stepping is where you command the computer to execute only the next instruction. Tracing is where the program creates a record of its activities so the designer can observe the flow of his program (that is, the path made through all the branches). Single stepping is obviously very slow while tracing allows the program to run at close to full speed (a little performance is lost due to the need to create the log file).
  11. We introduced indirection. When employing direct access to data memory you specify the address of a data memory location that holds the data you want to operate upon. When employing indirect access to data memory you specify the address of a data memory location that holds the address of a different data memory location that holds the data you want to operate upon. Indirection obviously takes more effort and hence runs slower, but it buys flexibility. A common situation where indirect addressing is useful concerns the difference between compile time and run time. Compile time refers to the time when a program is created. Run time refers to the time (possibly years later) when the program is executed. The use of direct addressing requires that the data memory location be known at compile time, since this address is hard coded (that is, fixed) in the statements of the program. But sometimes the location (address) of the needed data does not become known until run time. For example, the address may depend upon the results of earlier calculations the program made at run time. In this case indirect addressing allows the data memory address to be left unspecified at compile time.
  12. We introduced addressing modes. The RPN calculator offers the programmer the choice of only 2 addressing modes: direct and indirect addressing. The 8051 microprocessor we will study next offers 4 choices of addressing modes. The Intel Pentium III microprocessor used in many PCs offers 9 addressing modes. You choose an addressing mode for every operation when you program in a low level language such as assembly. The compiler selects an addressing mode to accomplish each of the operations you request when you program in a high level language such as C, C++, or Java.


Looking Ahead

( 23 )

Looking Ahead to Higher Level Languages

We have seen that the RPN calculator allows us to create unconditional branches via the GTO n statement. We can also construct conditional branches by pairing GTO n statements with conditional statements such as DSZ or x=0. By the standards of modern high-level languages such as C, C++, or Java this is a pretty crude notation. It is not immediately obvious that the following program statements:

x=0 GTO 3 LBL 6 ... LBL 3

correspond to the following flow chart logic:

The next language that we will study (in the 8051 microprocessor portion of this curriculum) is assembly language. Even though assembly language is still considered a low-level language the conditional branch statements are much more readable. An 8051 assembly language statement to branch (they call it a jump) when a value equals zero looks like:

JZ CloseDoor OpenDoor: ... CloseDoor:

The mnemonic JZ stands for "jump if zero". Note that we get to employ arbitrary text for the labels rather than the single digit numbers allowed by the RPN calculator's LBL n statement. Most programmers find it a lot easier to visualize the following flow chart logic when they see this type of assembly language code.

But things get even better in a high-level language. A conditional branch in the C, C++, or Java languages looks like:

if ( x == 0 ) goto CloseDoor else goto OpenDoor

The power of the high-level language comes from the fact that we can put ANY expression that evaluates to true or false inside the parentheses seen in this if statement.

Higher level languages are designed to read more like English. But they also introduce scads of new syntax rules that specify exactly what statements the computer can understand. Beginning programmers often get frustrated by the need to master all the syntax rules before they can write programs. My curriculum is designed to avoid that frustration by starting with simple languages that you can quickly learn and use for practical purposes. We will incrementally climb from low-level to high-level languages in order that we can digest the syntax rules in bite size pieces, all the while continuing to write fun programs. For example, my 8051 microprocessor curriculum allows you to write short programs (usually about 20 statements) that scroll your name across an electronic signboard or guide a mouse through a maze to find a piece of cheese.

So at this time I want to begin to expose you to how the concepts that we have seen with the RPN calculator will mature in more powerful programming environments. I have already covered conditional branches and next I want to discuss syntax errors.

When you construct an RPN calculator program by clicking keys on the calculator's keypad it is impossible to cause a syntax error. This doesn't mean your program is guaranteed to work, as it is easy to cause a logic error. But it does mean that the individual statements will always be accepted into the calculator's program memory.

But you can cause a syntax error when you construct an RPN calculator program using Notepad and then load it into the simulator. I haven't mentioned it yet, but during the process that begins when you select File/Open from the menu bar, your program is checked for syntax errors. If any illegal statements are discovered then you will be alerted and those statements will not be entered into program memory slots. Let's take a look at this process.

The example program BadSyntax.txt holds the following statements:

LBL 12 ; illegal, operand too large + ; legal since blanks are ignored STO ; illegal, missing operand RCL x<0 ; illegal, bad operand Enter ; illegal, wrong capitalization RTN ; legal

When this .TXT file is loaded into the calculator's program memory you will receive four dialog box messages alerting you to the four syntax errors seen in program memory below. Note that the defective statements are not placed into program memory slots, they are simply ignored.

00 -- LBL 12 ; illegal, operand too large 01 + ; legal since blanks are ignored -- STO ; illegal, missing operand -- RCL x<0 ; illegal, bad operand -- Enter ; illegal, wrong capitalization 02 RTN ; legal

The only type of thing that the RPN calculator allows to be placed into its data memory is numbers. More specifically, all numbers are floating point numbers meaning they can be fractional and can have exponents. The fact that data memory can only hold one type of object makes programming the RPN calculator a lot simpler. High-level languages allow the programmer to populate data memory with floating point numbers, with integer numbers (which cannot be fractional nor have exponents), with unsigned numbers (which cannot be negative), with alphabet characters, with foreign language symbols, with digitized sound samples, with colored pixels representing pictures, etc. A lot of the complexity inherent in a high-level language follows from the fact that the programmer must explicitly declare the types of objects he wants to place into data memory. He does this via variable declaration statements which, unfortunately, introduce a lot of syntax rules. Shown below are the C, C++, or Java statements which declare the programmer's intention to reserve data memory for an integer variable he will refer to as "x", a floating point variable he will refer to as "sum", and a string of up to 30 characters which he will refer to as "message":

int   x;
float sum;
char  message[30+1];

Incidentally, one of the main reasons for all the program crashes that every Windows user sees all too often is that the program he is running tries to employ some section of memory in a manner incompatible with what that section of memory is actually holding.

As a final topic, let's return to my statement that in order to be useful a program usually needs to be incomplete. That is, it will usually require a passed parameter to be specified at run-time. Our RPN calculator programs accept a passed parameter either through the operand stack or data memory. The flexibility offered by the passed parameter is what gives the program a long useful lifetime since there will always be yet another test case for it to solve. This is because the passed parameters do not have to be fixed at compile-time (the time frame we write the program) but can remain unspecified until run-time.

In higher-level languages we place most of our code into subroutines which are now called functions or methods. These functions receive passed parameters from the calling code (whatever code calls into the function). For example, we might write a function (subroutine) that can compute and return a square root every time it is called. We decide to call this function "sqrt" and it obviously requires one passed parameter, the number (argument) for which we want the square root. In a high-level language the calling code makes a call to the sqrt function, passing in the argument 8, and receives the result back into a variable named "result" via a statement such as the following:

result = sqrt( 8 );

In this example the passed parameter is hard coded at the value 8. But more likely the argument would come from another variable like the variable we named "result". In high-level languages the programmer can select whatever names he wants for his variables, which are the values located in data memory. In our RPN calculator programs we had to access data memory by specifying a data memory address, such as the 3 in the statement RCL 3. But in a high-level language you simply state that you want to use an integer named "result" and the compiler goes and finds and reserves a data memory location to be used for this purpose.


( 24 )

Instruction Set

The instruction set of a computer is the list of operations that it can perform. The list of operations that can be performed by the RPN Calculator can be grouped into the following categories:

  • Basic calculator operations
  • Math operations
  • Memory operations
  • Branch and Call operations
  • Plot operations


Basic Operations

( 25 )

Basic Calculator Operations

This list documents the basic "4 function" calculator operations present in any calculator and the special stack based operations that give the RPN calculator its unique flavor. Each of these operations is identified by both a textual instruction statement (a mnemonic) and a keystroke sequence. The former is used when you prepare your program via a word processor such as Notepad and the latter is used when you prepare your program by clicking on the calculator's keys. In a generic instruction statement such as "FIX n" the letter n is a placeholder for a single digit; i.e., a member of the set {0,1,...9}.

FIX n | Sets the calculator into its fixed point display mode and requests that n digits be displayed to the right of the decimal point. In fixed point display mode, numbers are displayed without an exponent. Note that the calculator will automatically shift into floating point (SCI) mode if the current number cannot be properly displayed in the requested FIX mode.
SCI n | Sets the calculator into its floating point display mode and requests that n digits be displayed to the right of the decimal point. In floating point display mode, numbers are displayed with an exponent. This exponent always has exactly 2 digits.
ENG n | Sets the calculator into its engineering notation display mode and requests that n+1 significant digits be displayed. In engineering notation display mode, numbers are displayed with an exponent and this exponent is always a multiple of 3. These multiples of 3 correspond to the metric system prefixes such as kilo, mega, giga, etc.
GTO .nn | Goto program step nn. This means the program counter (PC) is re-initialized to the value given by nn, a two digit value which can range from 00 to 98 (the value 99 has the same consequence as the value 01). The program counter always points to the next statement to be executed. Note that it is impossible to include this statement in a program since it is used to edit your program (so use the GTO n statement instead).
SST | In Run mode this keystroke causes the next program statement to be executed. Executing a program by one statement at a time is a standard debugging technique known as single-stepping. This keystroke also causes the Program window and Registers window to open to aid your debugging of the program. Note that the next instruction to be executed is displayed in the calculator's LED display while you depress the SST key and then executed when you release the SST key. In Program mode this keystroke moves the insertion point forward by one program statement. If you have very far to go it is faster to employ the GTO .nn command.
BST | In Run mode this keystroke decrements the program counter (PC) that identifies the program statement which will be executed next, but does NOT execute any statements. If you need to make a large adjustment to the PC it is faster to employ the GTO .nn command. In Program mode this keystroke moves the insertion point backward by one program statement. If you have very far to go it is faster to employ the GTO .nn command.
x<>y | Exchanges the contents of the X and Y stack locations. As an example, if the stack held 1,2,3,4 (contents of X,Y,Z,T) then after the x<>y keystroke the stack would hold 2,1,3,4
Rolldown | Rotates the entire stack downward such that the prior contents of the Y stack location are moved to the X location, Z is moved to Y, T is moved to Z, and X is moved to T. As an example, if the stack held 1,2,3,4 (contents of X,Y,Z,T) then after the Rolldown keystroke the stack would hold 2,3,4,1.
% | Computes the value that is X percent of Y. That is, the keystroke sequence 200, ENTER, 50, % results in 100 since 100 is 50% of 200.
DEL | In Program mode this keystroke deletes the instruction at the insertion point (this is the instruction highlighted in blue). All subsequent instructions move up 1 location and the 98th program statement is populated with a R/S instruction. In Run mode this keystroke is ignored.
ENTER | Lifts the stack such that the prior contents of the Z stack location are copied into the T stack location, Y is copied to Z, and X is copied to Y. As an example, if the stack held 1,2,3,4 (contents of X,Y,Z,T) then after the ENTER keystroke the stack would hold 1,1,2,3
ClearPrefix | Cancels an unwanted but yet incomplete keystroke sequence. For example, it allows you to back out of a store operation if you have already hit the STO key but not yet specified the register number.
ClearPRGM | In Program mode this keystroke clears all of program memory which means every program memory statement is populated with R/S. Furthermore the program counter (PC) is reset to 0. In Run mode this keystroke is ignored.
ClearREG | Clears all the registers (i.e., zeros all of the data memory locations).
ClearSum | Clears the summation registers, which are registers 10, 11, 12, 13, 14, and 15.
CHS | Change sign. If you are currently entering a mantissa, this keystroke negates (flips the sign of) the mantissa. If you are currently entering the exponent, this keystroke negates the exponent. For example, to enter the number -2.3x10-5 use the keystroke sequence: 2 . 3 CHS EEX 5 CHS
EEX | Enter exponent. After completely entering the mantissa hit this key to begin entering the exponent. Note that exponents are limited to a max of 2 digits. As an example, to enter Avogadro's number to a precision of 5 significant digits (i.e., 6.0225x1023), use the keystroke sequence: 6 . 0 2 2 5 EEX 2 3
CLX | Clears (zeroes) the X stack location. As an example, if the stack held 1,2,3,4 (contents of X,Y,Z,T) then after the CLX keystroke the stack would hold 0,2,3,4. However since this keystrokes disables the normal auto stack-lift feature, the next number you enter will overwrite the 0.
GRD | Sets the calculator into gradians mode where the argument for all trig functions is assumed to be expressed in gradians (there are 100 gradians in a circle). Note that the current trig mode is always shown in the calculator's display.
RAD | Sets the calculator into radians mode where the argument for all trig functions is assumed to be expressed in radians (there are 2*pi radians in a circle). Note that the current trig mode is always shown in the calculator's display.
DEG | Sets the calculator into degrees mode where the argument for all trig functions is assumed to be expressed in degrees (there are 360 degrees in a circle). Note that the current trig mode is always shown in the calculator's display.
- | Subtracts the contents of the X stack location from the contents of the Y stack location, places the result in the X stack location, and drops the rest of the stack. As an example, if the stack held 3,4,5,6 (contents of X,Y,Z,T) then after the - keystroke the stack would hold 1,5,6,6.
+ | Adds the contents of the X stack location to the contents of the Y stack location, places the result in the X stack location, and drops the rest of the stack. As an example, if the stack held 3,4,5,6 (contents of X,Y,Z,T) then after the + keystroke the stack would hold 7,5,6,6.
* | Multiplies the contents of the X stack location with the contents of the Y stack location, places the result in the X stack location, and drops the rest of the stack. As an example, if the stack held 3,4,5,6 (contents of X,Y,Z,T) then after the * keystroke the stack would hold 12,5,6,6.
/ | Divides the contents of the Y stack location by the contents of the X stack location, places the result in the X stack location, and drops the rest of the stack. As an example, if the stack held 3,4,5,6 (contents of X,Y,Z,T) then after the / keystroke the stack would hold 1.33,5,6,6.
LASTx | Restores to the X stack location whatever value was in the X stack location prior to the last math operation. The stack lifts to accommodate the new entry.
pi | Initializes the X stack location with the value of pi (i.e., 3.14159). The stack lifts to accommodate the new entry.
PAUSE | Causes a 1 second delay, which is useful to give the operator an opportunity to view the display before the program continues.
R/S | In Program mode this keystroke places the R/S statement at the current insertion point (subsequent statements are pushed down in program memory to make room for the new statement). Either a R/S or a RTN statement can be employed at the very end of your programs to halt execution and return control to the keyboard. In Run mode if a program is currently running then this keystroke halts the program (of course, any other keystroke halts the program as well). In Run mode if a program is NOT currently running then this keystroke initiates execution of the program starting with the statement identified by the program counter (this statement is highlighted in blue).


( 26 )

Math Operations

The list of general math operations supported by the RPN Calculator is shown below. Each of these operations is identified by both a textual instruction statement and a keystroke sequence. The former is used when you prepare your program via a word processor such as Notepad and the latter is used when you prepare your program by clicking on the calculator's keys.

Note that any math operation that requires two operands uses the contents of the X and Y stack locations for these two operands. Any math operation that requires only one operand uses the contents of the X stack location for this operand. The result of any math operation is always placed into the X location of the stack after the operands have been removed from the stack.

Mean | Returns the mean of whatever data points you have summed using the Sum+ key since the last ClearSum (or ClearREG) keystroke. The mean of the X values is placed into the X stack location and the mean of the Y values is placed into the Y stack location. The stack does NOT lift hence the Z and T stack locations are not disturbed. For more info see the section entitled Statistics.
StdDev | Returns the standard deviation of whatever data points you have summed using the Sum+ key since the last ClearSum (or ClearREG) keystroke. For more info see the section entitled Statistics.
LR | Returns in X the slope and in Y the y axis intercept of that straight line which best fits in a least squares sense the data points you have summed using the Sum+ key since the last ClearSum (or ClearREG) keystroke. For more info see the section entitled Least Squares Curve Fitting.
Sum+ | Adds the X and Y stack entries to two separate statistical summations that are implemented with registers 10 through 15 as described in the sections entitled Statistics and Least Squares Curve Fitting.
Sum- | Subtracts the X and Y stack entries from the two separate statistical summations. This keystroke allows you to remove a bad data point and thus avoid having to start over.
ln | Natural logarithm. As an example, the keystroke sequence: 1 0 ln will result in the value 2.30259
e^x | Exponentiation or inverse natural logarithm. As an example, the keystroke sequence: 1 ex will result in the value 2.71828
log | Common logarithm. As an example, the keystroke sequence: 1 0 0 log will result in the value 2.0
10^x | Antilog or inverse common logarithm. As an example, the keystroke sequence: 2 10x will result in the value 100.
->R | Convert from polar to rectangular representation. As an example, if the calculator is currently in degrees mode then the keystroke sequence: 63.43 ENTER 2.236 ->R will result in X = 1 and Y = 2 since (magnitude, angle) = (2.236, 63.43 deg) is the polar representation of the Cartesian point (x,y) = (1,2).
->P | Convert from rectangular to polar representation. As an example, if the calculator is currently in degrees mode then the keystroke sequence: 2 ENTER 1 ->P will result in X = 2.236 and Y = 63.43 degrees which is the polar representation of the Cartesian point (x,y) = (1,2).
sin | Sine function of trigonometry. As an example, if the calculator is currently in degrees mode then the keystroke sequence: 3 0 sin will result in the value .5 since sin(30 deg) = .5
sin^-1 | Arcsine or inverse sine function. As an example, if the calculator is currently in degrees mode then the keystroke sequence: . 5 sin-1 will result in the value 30 since sin(30 deg) = .5
cos | Cosine function of trigonometry. As an example, if the calculator is currently in degrees mode then the keystroke sequence: 6 0 cos will result in the value .5 since cos(60 deg) = .5
cos^-1 | Arccosine or inverse cosine function. As an example, if the calculator is currently in degrees mode then the keystroke sequence: . 5 cos-1 will result in the value 60 since cos(60 deg) = .5
tan | Tangent function of trigonometry. As an example, if the calculator is currently in degrees mode then the keystroke sequence: 4 5 tan will result in the value 1.0 since tan(45 deg) = 1.0
tan^-1 | Arctangent or inverse tangent function. As an example, if the calculator is currently in degrees mode then the keystroke sequence: 1 . 0 tan-1 will result in the value 45 since tan(45 deg) = 1.0
INT | Integer portion. This operation tosses the fractional portion of the current contents of the X stack location. As an example, the keystroke sequence: 2 . 3 INT will result in the value 2.0
FRAC | Fractional portion. This operation tosses the integer portion of the current contents of the X stack location. As an example, the keystroke sequence: 2 . 3 FRAC will result in the value 0.3
sqrt | Square root. As an example, the keystroke sequence: 9 sqrt will result in the value 3.
x^2 | Square. As an example, the keystroke sequence: 9 x2 will result in the value 81.
y^x | Raise y to the x power. As an example, the keystroke sequence: 2 ENTER 3 yx will result in the value 8.
ABS | Absolute value. As an example, the keystroke sequence: 2 CHS ABS will result in the value +2.
1/x | Reciprocal. As an example, the keystroke sequence: 2 1/x will result in the value .5


( 27 )

Memory Operations

The list of memory operations supported by the RPN Calculator is shown below. Each of these operations is identified by both a textual instruction statement and a keystroke sequence. The former is used when you prepare your program via a word processor such as Notepad and the latter is used when you prepare your program by clicking on the calculator's keys. In a generic instruction statement such as STO n, RCL n, STO .n, RCL .n, etc., the letter n is a placeholder for a single digit; i.e., a member of the set {0,1,...9}.

STO n | Store the contents of the X stack location to register (data memory location) n where n can be any value from 0 thru 9.
RCL n | Recall the contents of register (data memory location) n where n can be any value from 0 thru 9 and place these contents into the X stack location, thus causing the stack to lift.
STO .n | Store the contents of the X stack location to register 10+n where n can be any value from 0 thru 9.
RCL .n | Recall the contents of register 10+n where n can be any value from 0 thru 9 and place these contents into the X stack location, thus causing the stack to lift.
STO i | Store to an indirectly addressed register. Specifically, store the contents of the X stack location to that register indicated by the value currently in register #0. Hence the value in register #0 is used as an address. Since an address must be an unsigned integer, only the integer portion of the absolute value of the number in register #0 is employed for the address.
RCL i | Recall from an indirectly addressed register. Specifically, the contents of the register indicated by the value currently in register #0 are transferred to the X stack location, thus causing the stack to lift.
STO- n | Subtract the contents of the X stack location from the contents of register n where n can be any value from 0 thru 9.
STO+ n | Add to register n.
STO* n | Multiply with register n.
STO/ n | Divide the contents of the X stack location into the contents of register n.
STO- .n | Subtract the contents of the X stack location from the contents of register 10+n where n can be any value from 0 thru 9.
STO+ .n | Add to register 10+n.
STO* .n | Multiply with register 10+n.
STO/ .n | Divide into register 10+n.
STO- i | Subtract the contents of the X stack location from the contents of that register indicated by the value currently in register #0.
STO+ i | Add to the indirectly addressed register.
STO* i | Multiply with the indirectly addressed register.
STO/ i | Divide into the indirectly addressed register.
ClearREG | Clears all the registers (i.e., zeros all of the data memory locations).
ClearSum | Clears the summation registers, which are registers 10, 11, 12, 13, 14, and 15.


Branch Operations

( 28 )

Branch And Call Operations

A computer runs its program by executing the statements of that program in sequence. The statements are normally executed in the order they appear in the program. This is accomplished via a program counter (PC) which selects the next statement to be executed. After most statements the program counter is simply incremented and hence the subsequent statement in the program will be the next statement to be executed.

However this flow of control can be altered by branch and call statements. A branch statement permanently jumps to a new section of the program. By "permanent" I mean no preparations are made for a later return to the original location. To accomplish a jump we simply re-initialize the program counter to a new value instead of merely incrementing it.

A call statement temporarily jumps to a new section of the program. This change in locality is temporary because the original program location is remembered and ultimately returned to. Call statements are further discussed in the section entitled subroutines.

Branch statements are either conditional or unconditional. A conditional branch is a jump that is taken only if a certain condition is satisfied at the moment the conditional branch instruction is executed. A conditional branch can be constructed by pairing a conditional statement such as x<0 with a GTO n statement.

An unconditional branch statement does not depend upon any conditions at the time the statement is encountered. Hence an unconditional branch is always taken. An example of an unconditional branch is the GTO n statement.

The list of branch and call operations supported by the RPN Calculator is shown below. Each of these operations is identified by both a textual instruction statement and a keystroke sequence. The former is used when you prepare your program via a word processor such as Notepad and the latter is used when you prepare your program by clicking on the calculator's keys. In a generic instruction statement such as GSB n the letter n is a placeholder for a single digit; i.e., a member of the set {0,1,...9}.

GSB n | Gosub meaning enter and execute the subroutine having the label n. Execution will return to the original location when the RTN statement is encountered in this subroutine. An error message will be displayed if the specified label does not exist. Note that subroutines can only be nested 3 levels deep (this is further discussed in the section entitled subroutines).
GSB i | When the value held in register #0 is positive this statement causes the calculator to enter an indirectly addressed subroutine. That is, the calculator will enter the subroutine indicated by the (integer portion of the) value currently in register #0. An error message will be displayed if this subroutine does not exist. When the value held in register #0 is negative this statement causes the program counter (PC) to be decremented by the integer portion of the absolute value of the number currently in register #0. Execution then continues from that location in the program. This is exactly what the GTO i statement does when the value in register 0 is negative (see the IncrementForever4.txt program for an example).
RTN | Return from the subroutine. Execution resumes at the statement following the GSB statement that entered the subroutine. It is the subroutine return address stack that remembers the location where execution should resume at the completion of the subroutine. This is further discussed in the section entitled subroutines. Either a R/S or a RTN statement can be employed at the very end of your program to halt execution and return control to the keyboard.
GTO n | Goto label n. Unconditionally branch to the label identified by the number n. An error message is displayed if this label does not exist. If executed from the keyboard, this statement modifies the program counter (PC) but does not execute any instructions. If part of a program, this statement causes execution to continue at the new location.
GTO i | When the value held in register #0 is positive this statement causes the calculator to unconditionally branch to the indirectly addressed label. That is, the calculator will jump to the label indicated by the (integer portion of the) value currently in register #0. An error message will be displayed if this label does not exist. When the value held in register #0 is negative this statement causes the program counter (PC) to be decremented by the integer portion of the absolute value of the number currently in register #0. If this statement is part of a program then execution continues at the new location. If this statement is entered from the keyboard then the PC is reset to this value but the program does not begin executing. See the IncrementForever4.txt program for an example.
GTO .nn | Goto program step nn. This means the program counter (PC) is re-initialized to the value given by nn, a two digit value which can range from 00 to 98 (the value 99 has the same consequence as the value 01). The program counter always points to the next statement to be executed. Note that it is impossible to include this statement in a program since it is used to edit your program (so use the GTO n statement instead).
LBL n | Creates the label having the number n which can range from 0 thru 9. Label statements must be used for the initial statement in subroutines and are also useful as destinations for branch statements.
DSZ | "Decrement and Skip if Zero". If executed from the keyboard this statement decrements the value held in register #0. In a program, this statement decrements the value held in register #0 and then skips over the next instruction if register #0 now holds the value zero. It is very common to use an unconditional branch as the "skipped over" instruction thus creating a conditional branch.
ISZ | "Increment and Skip if Zero". If executed from the keyboard this statement increments the value held in register #0. In a program, this statement increments the value held in register #0 and then skips over the next instruction if register #0 now holds the value zero.
x<=y | If this condition is false (that is, the contents of the X stack location are not less than or equal to the contents of the Y stack location) then the next program statement is skipped. It is very common to use an unconditional branch as the "skipped over" instruction thus creating a conditional branch.
x>y | If this condition is false then the next program statement is skipped.
x!=y | If this condition is false then the next program statement is skipped.
x=y | If this condition is false then the next program statement is skipped.
x<0 | If this condition is false (that is, the contents of the X stack location are not less than zero) then the next program statement is skipped.
x>0 | If this condition is false then the next program statement is skipped.
x!=0 | If this condition is false then the next program statement is skipped.
x=0 | If this condition is false then the next program statement is skipped.


( 29 )

Plot Operations

The list of all graph-related operations supported by the RPN Calculator is shown below. Each of these operations is identified by both a textual instruction statement and a keystroke sequence. The former is used when you prepare your program via a word processor such as Notepad and the latter is used when you prepare your program by clicking on the calculator's keys.

ClearG | Clears the graphics window and prepares for a new plot. Also has the same consequence as the "0, SetG" keystroke sequence thus guaranteeing that subsequent SumG keystrokes will add data points to the first data set.
SumG | Grabs the 2 values currently in the X and Y stack locations and adds these to the current data set drawn in the cGraph window. Also causes the cGraph window to become visible if it was not previously visible.
SetG | Sets the active data set to the value currently in the X stack location. Subsequent SumG keystrokes add new data points to this data set. The legal operands for this keystroke are discussed in the section entitled Plots.


Example Programs

( 30 )

The Example Programs

To help you learn to program the RPN calculator I have prepared a number of example programs. Each program is very short and has comment statements that explain its purpose and how it accomplishes that goal. I would definitely recommend that you study every one of these programs. I have carefully designed them to introduce new topics in a logical order and at a pace that hopefully won't rip your head from your shoulders. The example programs are integrated into these help pages. That is, read these help pages in order from the beginning and you will be told the appropriate time to explore each of the example programs.

IncrementForever.txt

This program does nothing but endlessly count by ones. I use it to introduce the R/S, GTO .nn, LBL n, GTO n, GSB n, SST, BST, DEL, and ClearPRGM statements.

IncrementForever2.txt

This program builds on the previous IncrementForever.txt program by adding the feature of passing the initial value into the program at run time instead of having it hard-coded in the program. This program also introduces comments.

IncrementForever3.txt

This is a silly program that simply demonstrates that the program counter (PC) will wrap-around from 98 back to 0.

SecondOrderEq.txt

This program solves a particular second order (i.e., quadratic) equation for a particular value of x which is passed to the program on the operand stack.

SecondOrderEq2.txt

This program introduces the calculator's data memory and employs it (rather than the operand stack) to store a temporary copy of a number. This program introduces the STO n statement.

SecondOrderEq3.txt

This program employs data memory rather than the operand stack to pass in the program's argument at run-time.

SecondOrderEq4.txt

This program doesn't just solve the second order equation for a single y value, but rather for a never-ending sequence of y values corresponding to the sequence of x values starting with the particular value passed to the program.

SecondOrderEq5.txt

This program is identical to SecondOrderEq4.txt except the R/S statement is replaced with a PAUSE and hence the program will continue working overnight without operator intervention.

SecondOrderEq6.txt

This program solves the same quadratic equation as the prior programs but instead of displaying the computed y axis values one at a time to the operator this program draws an actual graph of the results. This program introduces the ClearG, SumG, and SetG keystrokes. The window that shows the plot is a general-purpose piece of software that I call cGraph. If you have a need for viewing or printing 2D (i.e., Cartesian) plots then you can use this software independently of the RPN calculator via my GRAPHIT.EXE Windows program that I distribute with my RPN calculator simulator.

DrawSineWave.txt

This is the first program that I ask you to create from scratch. It should draw a plot of a sine wave in the same manner as the SecondOrderEq6.txt example program. You can find my solution in the "Solutions" folder but no peeking! My solution introduces the STO+ n statement. While this program is running the cGraph window is being constantly erased and redrawn and this causes a stroboscopic flashing that you may find annoying. After the program has run for a few minutes you can even begin to observe the type of visual aliasing effect that causes wagon wheels to appear to rotate backwards in movies. Once you halt the calculator program the cGraph window will look fine. You can eliminate the flashing by selecting a fixed plot range for the cGraph window before initiating the calculator program. To learn how to do this read the Help pages for the cGraph window (that is, select Help/Help from the cGraph window's menu bar).

DrawCircle.txt

This program employs the sin() and cos() functions to draw a circle. However the plot produced in the cGraph window will look like an oval rather than a circle until you drag the cGraph window border to force the x and y axes have an aspect ratio of 1.

DrawSpiral.txt

This is another program that I ask you to create from scratch. Actually, it requires only a few changes from the earlier DrawCircle.txt program. As always, you can find my solution in the "Solutions" folder.

DrawFlower.txt

This program draws a 3 leaf flower. By changing only a single number you can produce 4 leaf flowers, 5 leaf flowers, etc.

DrawSpirograph.txt

This program draws the type of artwork you may have produced as a kid using the Spirograph toy. You can produce an almost unlimited number of different designs just by varying the 3 parameters you pass to this program.

FillMemory.txt

This program initializes all of data memory with a constant value. I use it to introduce indirect addressing which is the trick that allows a single program statement to keep touching a different address during every iteration of a loop. This program introduces the DSZ and STO i statements.

ThirtySquares.txt

This program again demonstrates indirect addressing and the DSZ statement.

SecondOrderEq7.txt

Now that we know about the DSZ statement we can write a final version of our SecondOrderEq sequence of programs. Whereas SecondOrderEq6.txt just kept producing data points forever, this new SecondOrderEq7.txt program computes 100 data points and then stops.

Factorial.txt

This program computes the factorial of whatever number is in the X stack location when the program is initiated via GSB 0. It introduces the conditional statements such as x<0 and it introduces the STO* n statement.

BadCombination.txt

This program INCORRECTLY computes the mathematical operation known as "combination" which is the number of distinct ways that you can take n of m items (ignoring ordering). Because the equation for combination involves three factorials, this program motivates us to write our first subroutine. Subroutines are a way to package code such that it can be employed again and again at different moments in your program. This program introduces the GSB n and x<>y statements.

Combination.txt

This program CORRECTLY computes the mathematical operation known as "combination" which is the number of distinct ways that you can take n of m items (ignoring ordering). We fix the contention problem in the factorial subroutine that was messing up the stack for the calling code. This program introduces the Rolldown and CLX statements and discusses the calculator's auto-stack lift feature.

Combination2.txt

The RPN calculator cannot deal with any number larger than 9.999999999 x 10^99 (usually written 9.999999999e99). This fact causes the prior Combination.txt program to fail when asked to compute a combination such as 73C4 which is supposed to equal 1088430. But there is a way to re-write the algorithm to allow it to compute larger combinations without hitting the 9.999999999e99 number limit. The technique is described in the comments in the Combination2.txt example program but the technique is a bit too tedious for me to want to describe it here. I include it nonetheless for the bravest among you. In other words, "this is left as an exercise for the motivated reader".

SquareRoot.txt

This program uses a successive approximation algorithm to solve for the square root of an arbitrary positive number. Successive approximation algorithms are cool because they demonstrate that by merely repeating simple operations you can converge on a complex result.

BadSyntax.txt

This program isn't an example of anything other than what happens when you write your program using Notepad and then import it into the RPN calculator. Any statement that the calculator can't understand is flagged with the special notation "--" instead of being assigned to a program memory slot.

IncrementForever4.txt

I don't discuss this example program anywhere but here. It exists just to document how the RPN calculator handles the GTO i and GSB i statements if it discovers a negative value in register #0. The GTO i statement is supposed to find one of the ten values 0 through 9 in register #0 and then branch to the corresponding LBL n statement. If the corresponding LBL n statement does not exist in program memory than an error is generated. If the value in register #0 is fractional then the fractional component is ignored. If the value in register #0 is positive and greater than or equal to 10, an error is generated since there can't possibly be such a LBL n statement. If the value in register #0 is negative, the calculator branches backwards by the number of statements given by the absolute value of the integer portion of this negative value. What a mouthful! I don't give this technique much prominence since it doesn't carry over to higher-level languages which all branch to labels rather than by number of statements. But I use it in this example program to cause the calculator to count forever.

QuadraticEq.txt

Here's another program that I don't discuss anywhere but here. It employs the quadratic equation to solve for the roots of any second order equation. Any second order equation will always have two roots. The complication is that these two roots might be unique numbers, or they might be the same number (this is called a root with multiplicity of two), or they might be complex numbers involving i which is the symbol for the square root of -1. The RPN calculator has no facility for displaying a complex number in the usual a + bi notation and thus it is difficult for this program to present a satisfactory user interface. But it is a reasonable assignment for a hand calculator so I programmed up an approach and offer it to the motivated reader who can consult the comments in the QuadraticEq.txt file.

GeneralSecondOrderEq.txt

Anyone up for a final exam? Let's imagine that your teacher -- the one that gave you the assignment that motivated us to write the SecondOrderEq series of example programs -- has discovered you are using a computer program that can solve the equation:

y = x2 + 2 * x + 3

This teacher decides that in honor of your resourcefulness you should get a bunch more second order equations to plot. You realize that you could keep changing the coefficients that are hard-coded into the SecondOrderEq7.txt program. But instead you decide to write the mother of all second order equation programs, one that allows these coefficients to be specified at run-time. That is, to obtain a plot of any second order equation:

y = a * x2 + b * x + c

you merely need to use the keystroke sequence:

a ENTER b ENTER c GSB 0

Clearly this program will take 3 passed parameters: the a,b, and c coefficients. In the older SecondOrderEq7.txt program we had only a single passed parameter which was the initial x axis value where the plot should start. The plot then consisted of 100 data points corresponding to the next 100 integer x axis values. In the new program we will hard-code the x axis range as being the 101 integer x axis values from -50 to +50. Try writing this program yourself before checking my solution which you can find in the "Solutions" folder.


( 31 )

Simulator Options

This section describes the user-configurable options seen on the Options dialog that appears when you select View/Options from the program's menu bar. I will walk you through the purpose of each option seen on this dialog.

The first option allows you to request that the Program Memory Window remain open (visible) all the time. In the default configuration of this simulator, this window will be visible any time you are in Program Mode or any time after you click the SST key and continuing until you next leave Program Mode. This means that, by default, the Program Memory Window will be visible while you are editing or debugging your program but not while you are running it.

The next option allows you to request that the Registers Window remain open (visible) all the time. In the default configuration of this simulator, this window will be visible any time after you click the SST key and will continue to be visible until you next leave Program Mode. This means that, by default, the Registers Window will be visible while you are debugging your program but not while you are running it.

The next option is a checkbox that you can use to request a trace file. A trace file is a log file (a plain text ASCII file you can view using Notepad) that documents each and every instruction that gets executed as your program runs. This is a standard debugging technique like single-stepping. The log file is not created by default because it slows down the simulator.

The final option is another checkbox that allows you to shut up the simulator if you don't like the cute sound effects.