
by Tom Hudson
Welcome to Boot Camp, the beginner's assembly language column. With this issue, we will have completed our introduction to the world of 6502 assembly operation codes. Starting next issue, we'll find out exactly how to apply these instructions in BASIC subroutines, games, utilities and other programs.
Last issue's homework was for you to write a subroutine that would add the X and Y registers, placing the result in the accumulator. If the result of the add was greater than 255, you were to put the value $FF in the X register. If not, you were to set the X register to zero. Figure 1 shows one possible solution. Let's step through it and see how it works.
10 ;SUBROUTINE "ADDXY"
12 ;
14 ;ADDS X REGISTER TO Y REGISTER
16 ;PLACING RESULT IN ACCUMULATOR
18 ;IF RESULT > 255, X REG = $FF
20 ;IF RESULT <= 255, X REG = $00
22 ;
24 *= $0600
26 ADDXY CLD ;BINARY MATH
28 STX TEMP ;SAVE X REG.
30 TYA ;PUT Y IN ACC.
32 CLC ;CLEAR FOR ADD
34 ADC TEMP ;ADD X REGISTER
36 BCS GTR255 ;BRANCH IF > 255
38 LDX #$00 ;ZERO X REGISTER
40 RTS ;AND RETURN!
42 GTR255 LDX #$FF ;SET X REGISTER
44 RTS ;AND RETURN!
46 TEMP *=*+1
48 .END
How would we use this subroutine? Figure 2 shows an example of the code necessary to call the subroutine ADDXY.
LDX ADD1 ;GET ADD #1
LDY ADD2 ;GET ADD #2
JSR ADDXY ;ADD X & Y
CPX #$00 ;ADD OK?
BNE BADADD ;NO!
STA RESULT ;ADD OK!
JMP OK ;JUMP ELSEWHERE
BADADD JMP NOTOK ;HANDLE ERROR
As you can see, this code first loads the X and Y registers with the desired add values, then JSRs to the subroutine.
The first instruction after the JSR tests the X register to see if it's zero. If not, the add was too large for the accumulator, and we branch to the label BADADD. If the add was okay, we store the accumulator in the location labeled RESULT and jump to another part of the program, labeled OK.
Of course, the use of the X register as an overflow flag was not really necessary in this problem. We could have simply tested the carry flag after the JSR and taken the appropriate action then. Still, I thought this would be a good time to introduce you to the technique of using subroutine result indicators.
So there you have it. Just one of the many ways in which the homework assignment can be solved. I'm sure most of you came up with other ways to accomplish the objective, and as long as they work it doesn't matter which approach you take. Just remember to thoroughly test each subroutine you write, to be sure they'll return the proper results.
Up till now, all our stack usage has been handled by the 6502 itself, in the JSR and RTS instructions. Now we're going to find out how to use the stack for our own purposes.
The first two stack instructions we're going to investigate are the PHA (Push accumulator onto stack) and PLA (Pull accumulator from stack). The format of the PHA instruction is:
PHA (NO ADDRESSING)
The PHA instruction is used to place the accumulator on the "top" of the stack. It doesn't affect any status flags. Let's see what happens when a PHA instruction executes.
6502 stack <----
---------- |
$01FF | | |
|----------| |
| | | SP
|----------| | --
| | ---|00|
|----------| --
| |
Figure 3 shows how the stack looks when it's empty. The stack pointer (SP) contains $00. As you recall from the last two Boot Camp installments, the 6502 stack resides in the memory from $0100-01FF.
Let's assume the following two instructions are executed:
LDA #$40
PHA
The first instruction loads the accumulator with the value $40. The second instruction "pushes" this value onto the stack. The 6502 decrements the stack pointer (to $FF), then stores the accumulator's contents at the indicated memory location. Figure 4 shows how the stack looks after the PHA instruction.
6502 stack
----------
$01FF | 40 |<----
|----------| |
| | | SP
|----------| | --
| | ---|FF|
|----------| --
| |
If we like, we can push another value onto the stack. Let's push the value $6D onto the stack this time. Here's the code:
LDA #$6D
PHA
This time, the stack pointer will be decremented (to $FE), and the value $6D stored at the indicated location. Figure 5 shows how the stack looks now.
6502 stack
----------
$01FF | 40 |
|----------|
| 6D |<---- SP
|----------| | --
| | ---|FE|
|----------| --
| |
See how simple the PHA instruction is? No registers except the stack pointer are affected, and the numbers are sitting on the stack, ready for you to use them. How do we get them back? With the PLA instruction, of course!
Once you have numbers stored on the stack, they're incredibly easy to retrieve. We simply use the PLA instruction. Its format is:
PLA (NO ADDRESSING)
The PLA instruction takes the first number on the stack, places it in the accumulator, sets the SIGN and ZERO flags accordingly, and increments the stack pointer so that the next value is ready to be pulled from the stack. Let's see how this works with the numbers we placed on the stack earlier.
Figure 5 shows the stack as it appears now. We want to pull a value off the stack, so we write the following code:
PLA
The 6502 loads the accumulator from the indicated byte of the stack ($6D) and increments the stack pointer. At this point, the accumulator contains $6D, and the stack looks like Figure 6.
6502 stack
----------
$01FF | 40 |<----
|----------| |
| 6D | | SP
|----------| | --
| | ---|FF|
|----------| --
| |
Simple, right? We've just retrieved the last number placed on the stack. Let's do it again. We use the code:
PLA
When complete, the accumulator contains $40, and the stack looks like Figure 7.
6502 stack <----
---------- |
$01FF | 40 | |
|----------| |
| 6D | | SP
|----------| | --
| | ---|00|
|----------| --
| |
Now you see how easy stack usage is. All you need to do is push and pull the desired values, and the computer takes care of all necessary overhead. However, there are a few things you need to remember when using the stack.
The first thing you must remember about the stack is that it is a LIFO (Last-In, First-Out) structure. That is, the last number you place onto the stack will be the first number that you pull off. This sometimes takes getting used to, but you'll get the hang of it if you diagram your stack logic on paper first.
Second, the stack can only hold up to 256 numbers, and some space on the stack is used by the system. A good rule of thumb is to use the stack only when you need to, like in BASIC USR calls or when you're running out of memory (a PHA only takes one byte; an STA can take up to three bytes).
What can you use the stack for? Most people use it to store numbers temporarily or as a small table that automatically maintains pointers.
Here's an example of using the stack to save the accumulator's contents when a subroutine is executed. Remember that when a subroutine is executed, if it uses any registers, the values that were in those registers are lost.
Figure 8 shows how to save the accumulator so that you can be sure it is unchanged after a subroutine executes.
10 PHA ;SAVE ACCUMULATOR
20 JSR SUBTRN ;PERFORM SUBROUTINE
30 PLA ;RESTORE ACCUMULATOR
Unfortunately, the designers of the 6502 did not allow for the PUSHing of the X and Y registers, so we have to write a little extra code.
To push the X register, we use the code:
TXA ;MOVE X TO ACCUM.
PHA ;AND PUSH IT!
This transfers the X register to the accumulator, then pushes the value onto the stack.
Similarly, the Y value register can be pushed with the sequence:
TYA ;MOVE Y TO ACCUM.
PHA ;AND PUSH IT!
To pull the X or Y registers from the stack, use one of the following code sequences:
PLA ;PULL THE VALUE,
TAX ;AND PUT IN X!
PLA ;PULL THE VALUE,
TAY ;AND PUT IN Y!
These routines are simple enough, but you should remember that the accumulator will be lost in all of these operations unless you save it somewhere first.
Sometimes you'll want to save the processor status register before a subroutine or comparison operation so that you can test certain flags later. This can be done by using the PHP (Push processor status register onto stack) and PLP (Pull processor status register from stack) instructions. Their formats are:
PHP (NO ADDRESSING) PLP (NO ADDRESSING)
The PHP and PLP instructions work just like the PHA and PLA instructions,except that they push and pull the status flags instead of the accumulator.
The PHP instruction does not affect any flags, but the PLP instruction changes all the flags, since it is actually loading the flags from the stack.
We'll explore the use of the PHP in more detail later, when the need arises.
Someday, you may need to know where the stack pointer is currently pointing, or you may need to change the stack pointer to point to a particular location. This is usually a rare occurrence, but I needed to do this in my debug utility, HBUG, in issue 18.
The 6502 has two instructions that will allow us to examine and change the stack pointer. These are TSX (Transfer stack pointer to X) and TXS (Transfer X to stack pointer). The formats of these instructions are:
TSX (NO ADDRESSING) TXS (NO ADDRESSING)
The TSX instruction simply loads the X register with whatever happens to be in the stack pointer at the time. The sign and zero flags reflect the result of the load.
Figure 9 shows an example of the use of the TSX instruction.
10 *= $0600
12 LDA #$F0 ;PUT # IN ACCUM.
14 TSX ;GET STACK PTR
16 STX STACK1 ;SAVE STACK #1
18 PHA ;PUSH ACCUM.
20 TSX ;GET STACK PTR
22 STX STACK2 ;SAVE STACK #2
24 PLA ;ACCUM.
26 TSX ;GET STACK PTR
28 STX STACK3 ;SAVE STACK #3
30 BRK ;ALL DONE!
32 STACK1 *=*+1
34 STACK2 *=*+1
36 STACK3 *=*+1
38 .END
Let's walk through this code and see what happens.
Type this program into your computer and assemble it. Note the locations of STACK1, STACK2 and STACK3 during the assembly. When the program is assembled, execute it.
After execution, examine the memory locations at STACK1, STACK2 and STACK3. STACK1 contains the stack's location at the beginning of the program. STACK2 contains the stack's location after the PHA instruction. Since the PHA decrements the stack pointer, STACK2 should be one less than STACK1.
STACK3 contains the stack pointer's contents after the PLA instruction. A PLA instruction increments the stack pointer, so STACK3 will be one more than STACK2.
The TXS instruction does the opposite of TSX. That is, you can move the contents of the X register to the stack pointer. To do this, you simply load the X register with the desired value and execute a TXS instruction, like so:
LDX #$40 ;STACK AT $0140
TXS ;POINT THERE!
I strongly suggest that you leave this instruction alone for the time being. Incorrect setting of the stack pointer can cause a system lockup, so hold on until we get a chance to use it safely in a Boot Camp program.
Well, we've covered all the major 6502 instructions, and were ready to learn some system-specific material. Starting next issue, we'll go full speed ahead into the world of the Atari's innards.
Send all letters to: