Some Assembly Required
by Robert Peck
Last issue presented a program for bulletin board operators to help them screen the incoming messages. This installment will cover one of the techniques used in that article.
When programming directly in machine language, the system can perform many times faster than when programming in BASIC because it is doing ONLY what it has been asked. It does not have to do the tasks of translating or interpreting everything every time it gets to a set of program statements.
But because each item the machine is to do must be detailed, it may take more time to write the program in the first place. Since BASIC is often too slow, a programmer may be able to make best use of time by combining it with machine language. The parts that do not need to be especially fast, but easy to program in BASIC, can remain in BASIC. The parts that must be fast can be done in machine code.
Atari BASIC provides the USR function for this purpose, which is highlighted by the fact it supplies as many machine code groups as desired. The function, however, must be instructed where the routine is located.
Figure 1 is a sample use of the USR function, taken from the last article.
This function allows the user to "pass parameters" to the assembly language program. A parameter is a number in the range 0 to 65535. If there is a fractional part to any of the numbers being sent to the machine, they will all be rounded down to the next lower integer value.
The postal clerk examples discussed in the second part of this series will help in comparing the parameter passing to a mailman's duties.
(ADR(E$): Where the machine code is located.
ADR(A$): First Parameter
ADR(B$): Second Parameter
LEN(B$)): Third Parameter
X = USR(ADR(E$),ADR(A$),ADR(B$),LEN(B$))
Atari BASIC will take apart the USR instruction line and change all the functions into numbers. When it has all the numbers in the correct form, it is ready to pass control to the machine language processor. Notice how the central processing unit (CPU), which is the machine language processor, has been doing the work while Atari BASIC is active. This time, however, BASIC knows there is another language which is to be obeyed and it simply acts to interpret, separate and count the "stack" of numbers which are to be given to this other language. In this way, no matter how many numbers are given, BASIC can provide a warning to the other language.
In fact, since the stack area of the machine is somewhat limited, the other language had better get rid of the leftovers before it completes its job. Otherwise, the central processor can get lost and never return control to the BASIC language.
Let's go to the central processor BASIC parameter passing box (address) and wait for BASIC to start a machine code operation. BASIC now discovers the first parameter -- in this case ADR(E$) -- is the location where the control is to be passed. It converts all of the other expressions or functions into integer numbers, stacks them up in a special memory area and then passes control.
The "clerk" -- CPU -- gets the message, goes to the stack and pulls the first item. This item tells there are three more items on the stack which must be used by the machine language processor. Actually, all of them must be removed from the stack because underneath is the address of the next instruction to be performed after the machine language processor is finished with this special instruction group. This next instruction is the one which makes the BASIC language active again.
The first part of this article showed how the numbers on the stack could range from 0 to 65535. But because this is an 8-bit machine, each individual item on the stack can hold only a number from 0 to 255. Therefore, BASIC, although with three parameters on the stack, actually used two memory locations to hold each of them. There will be twice that many, or six bytes of data on the stack, passed from BASIC.
In the sample program from the March 1984 issue, the first instruction commented as: PLA; discard count of items on the stack. This was simply convenient and was NOT good programming practice. Make sure the stack is emptied of the data BASIC puts there -- or truly TRUST the user of the routine not to mess it up by using too few or too many parameters in the USR call.
Back to the topic.
In order to store these numbers on the stack, Atari BASIC has to split them into the two parts, as the assembly language processor. The high-byte (most significant part) is formed by the value:
HIPART = INT (NUM / 256)
Then the low-byte (least significant) is formed by the value:
LOPART = NUM - (HIPART * 256)
This is actually the remainder of the division of the original number by 256.
Atari BASIC places these numbers on the stack in the following sequence:
Stacked under all of these items is the LOW and then HIGH byte of the return address.
Therefore, the assembly language processor must pull off all of the other items so it can see the return address when the RETURN (RTS) instruction is given.
The instruction used for this data removal is: PLA, which means pull the next item off of the stack into the accumulator area. This is an area reserved for arithmetic instructions. But it also can be used for data handling, as it is done here. A PLA instruction not only moves the data, but also changes a pointer -- the stack pointer.
There isn't really a physical "stack" of items with which the processor is dealing. There is, instead, an area of memory (such as a whole line of pigeonholes) into which data such as single letters may be placed. There is a number -- an address associated with each of these memory locations in the stack area.
Imagine the clerk has a sliding pointer along the side of the series of pigeonholes. That pointer is aimed at one of these slots. This is the "stack pointer". If the clerk is told to get one of the items from the slot at which the pointer points, he also will be expected to move the pointer to the next slot in line. Therefore, the next time he is asked to get an item, he will know to retrieve the next one and not just the same one again.
This is what the PLA instruction means. GET (copy, not remove) the item just above the address to which the stack pointer points, and INCREMENT (add one) the stack pointer.
There is another instruction called PHA -- PUSH accumulator value onto the stack -- which does the opposite. It places a value into a memory area (replaces the old value there), then decrements (substracts one) the stack pointer.
In this way, the stack pointer always shows where the next available memory storage slot is located. In other words, if an item has been PULLED off the stack, the memory slot it used is now fair game to use again. If an item is PUSHED onto the stack, the item goes into the location pointed to "now", then the pointer is decremented so the next possible memory area will be used.
When reading a memory location, its value is not changed, but is only copied from the source to the destination. Therefore, all the data from the stack would not immediately have to be copied as was done with the sample program last time.
Instead, keep track of the pointer and simply pull (copy) the items from the stack as needed. This topic will be discussed in future articles. At this point, however, moving all of the data was the safest and quickest way to show how the data was being passed from BASIC.
Once the parameters have been removed, a Return From Subroutine (RTS) will revert back to BASIC. Of course, the instructions in between will be expected to do some useful work. To pass a value back to BASIC in a return, before the RTS, put that value into addresses $D4 (low byte) and $D5 (hi byte). These are addresses 212 and 213 in decimal. Atari BASIC will assign to X a value from 0 to 65535 by the formula:
X = 256 * PEEK(213) + PEEK(212)