6502 Introduction

        .cr     6502        To load the 6502 cross overlay
        .cr     65c02       To load the 65C02 cross overlay

This is where it all began with for me. I started assembly programming on an Apple ][ computer powered by a 6502 processor. My first SB-Assembler was originally written on and for a 6502 processor and that's why it was actually tailored for this processor.

The 6502 was designed by MOS Technologies in NMOS technology. Unfortunately that company doesn't exist anymore, otherwise we might even be running on 6502 descendants today, rather than on Pentiums ;-) The original 6502 had some bugs in it, but none serious enough to be bothered about.
Later WDC redesigned the 6502 in CMOS and called it the W65SC02. The most important bugs of the 6502 were eliminated with this new device, but most important a few extra instructions and addressing modes were added as well. Especially the ability to save and retrieve the index registers X and Y contents directly to and from the stack is the most often used novelty.
Rockwell added even more instructions to this new design in their own version of the 65C02. WDC also created a 65C02 version with the bit instructions added by Rockwell.

The classic Apple ][ The heart of the Apple ][ was a 6502

All 3 processor types are so alike that I can describe them here on one page. I think it is obvious that the 6502.sba and cr6502.py overlays are meant for the 6502.
The 65c02.sba overlay is the one you need for WDC's 65SC02. I didn't bother creating a special overlay for version 3, because it only lacks a few bit manipulation instructions.
r65c02.sba and cr65c02.py are the overlays you need for the full featured instruction set of the CMOS devices. These crosses can also be used for th WDC 65SC02, as long as you don't use the bit manipulation instructions because the processor won't understand them.
The opcode test files included in the download package show all the processor specific instructions and new addressing modes.

To make things even more complicated not all CMOS versions have the WAI and STP instructions. None of the Version 2 cross overlays can handle these instructions. The CMOS cross overlay cr65c02.py for Version 3 can assemble these instructions however.
If you need one of these instructions in Version 2 you can simply make a macro for them.

I think the greatest power of the 6502 is its simplicity and its flexible addressing modes. Especially the zero page addressing was a wonderful invention (or was it invented by Motorola?).

Programming Model

The programming model in the picture below shows the most important registers of the 6502 processor. I only include a little summary about the features of the 6502's programming model here. It is not my intention to make the original documentation obsolete, so please refer to the original documentation for further details. More information about the 6502 can be found on www.6502.org.

6502 programming model

Don't be alarmed by the relatively small number of registers compared to other processor types. The 6502 has a very powerful addressing mode called zero page addressing. This way all 256 bytes of page 0 in memory can be addressed with only 8 bits. These 256 addresses can be considered the "registers" of the 6502!

The Accumulator

The Accumulator is the most important register for 8 bit arithmetic operations. It can not be used as an index register though.

The Index registers X and Y

The 6502 has 2 index registers, called X and Y. They are both 8 bits wide and can be used as temporary registers, or as index pointers in a variety of addressing modes. The index registers can not be used for arithmetic operations.

The Program Status Register

The P register holds all the system flags. Some flags are set or reset under program control. Others reflect the status of the machine after mathematical instructions.

The P contains 8 system flags:

Bit 7NNegative sign Flag
Bit 6VOverflow Flag
Bit 51Always 1
Bit 4BBreak Flag, set by BRK instruction
Bit 3DDecimal mode Flag
Bit 2IIRQ Disable Flag
Bit 1ZZero Flag
Bit 0CCarry Flag

There is a small difference in behaviour between the 6502 and the 65C02 when it comes to the Decimal flag. The Decimal flag of the 6502 is not initialized during reset, leaving it in an unpredictable state. Therefore you'll always see a CLD instruction directly after reset on all 6502 programs.
This small bug is fixed on the 65C02 processors, but it wouldn't hurt to continue the habit of clearing the Decimal flag after reset.

The Stack Pointer

The stack pointer of the 6502 is only 8 bits long and therefore the stack is limited to 256 bytes. The stack itself is always located on page $01 in RAM memory.

The NMOS 6502 could only push or pull the accumulator (PHA, PLA) and the P register (PHP, PLP) on the stack. Therefore context switching speed during interrupts was quite low. The CMOS 65C02 processors can also push and pull both index registers to the stack (PHX, PLX, PHY and PLY).

During subroutine calls (JSR) both bytes of the current program counter are pushed on the stack. The value that is pushed is the address of the next instruction to be executed after return (RTS) minus 1 ! This is a bit odd, but no problem as long as you know it. First the high byte of PC is saved and the stack pointer S is decremented by 1. Then the low byte of PC is saved and the stack pointer S is decremented by 1 once more.
Interrupts will also push both address bytes to the stack, followed by the P register.
As you can see the stack grows down in memory on the 6502 family.
It goes without saying that the return from subroutine or interrupt process is exactly the reverse operation.

The Program Counter

The program counter PC is normally incremented after fetching each instruction or operand byte during program execution. The only way you can change this behaviour is with the jump, subroutine and return instructions. Also interrupts can change the program counter's value.


SB-Assembler Version 3 can show you the cycle times of each instruction when the TON list flag is switched on. The numbers presented are the number of clock cycles the processor needs to execute the instruction.
A single * behind a time value means that you'll have to add 1 cycle when a page boundary is crossed by the instruction. A double ** behind a time value means that you'll have to add 1 cycle if the condition is true and the destination is on the same memory page. And add 2 if the condition true and the destination is to another memory page.

Reserved Words

The SB-Assembler 6502 cross overlay family has only one reserved word, which is the letter A which stands for Accumulator. However, I advise you not to use any of the register names as labels. By the way: Labels should have meaningful names for better readability of your sources and I can't think of any meaningful name with only one letter.

In Version 3 of the 6502 cross overlays the label SWEET16 has a special meaning too. See the description of the Sweet-16 interpreter further down this page.

Special Features

Compound instructions

A compound instruction is an instruction that is translated to more than 1 real instruction. I've added 2 compound instructions to the 6502 family of cross overlays, namely: ADD and SUB.
When it comes to adding two bytes together the 6502 knows only the ADC instruction. This instruction always adds the carry to the end result. The compound statement ADD is formed by a concatenation of the CLC and ADC instructions. So now you can simply use the ADD instruction, like you would use the ADC instruction, to add two values together without bothering about the initial state of the Carry flag.
The same is true for the SUB instruction which is translated to SEC and SBC, and subtracts 2 values from each other, without bothering about the borrow status.

While programming Version 3 of the SB-Assembler I've noticed a little oops here. The ADD and SUB mnemonics are also used in the Sweet16 interpreter. Therefore you can't use the Sweet16 instructions because they will be translated to the wrong opcodes.
I'm not going to change that anymore in Version 2. If you want to use Sweet16 instructions please upgrade to Version 3, where this little oopsy is fixed or use a small macro to replace the Sweet-16 ADD and SUB instructions.

Forced zero page and absolute addressing modes

One of the strongest features of the 6502 family is its zero page addressing mode. With zero page addressing mode you specify a memory location that can be addressed with only one byte (instead of 2 for all other memory locations). This way the 6502 can be seen as a micro processor with 256 registers.

The SB-Assembler automatically selects zero page addressing mode when that mode is available and the high byte of the address is $00 (being the zero page). We only know for sure that the high byte of the address is $00 if there was no unresolved label in the expression identifying the address. If a forward referenced label is used in an address expression we automatically assume the worst case situation and opt for absolute addressing mode (2 bytes address field).
You may override this automatic selection of addressing mode by preceding the address field with a < or a > symbols.
The < symbol forces the assembler to use zero page addressing mode, even if the address expression contains a forward referenced label.
On the other hand the > symbol will force the assembler to use the absolute addressing mode, even if the address could be resolved to a zero page address.
However a Out of range error will be reported if you try to force to use the zero page addressing mode where the high byte of the address isn't zero.


0010-           LABEL     .EQ   $10            A zero page address
8000-A5 10                LDA   LABEL          Appears to be zero page
8002-A5 11                LDA   <FORWARD       Clearly a forward referenced label
8004-85 12                STA   $12            Is a zero page address
8006-8D 11 00             STA   >$11           Force absolute addressing mode
0011-           FORWARD   .EQ   $11            A zero page address

Implied Accumulator Instructions

There are some differences in the syntax for some implied accumulator instructions. On the original 6502 these instructions are ASL, LSR, ROL and ROR. The 65C02 derivatives add the instructions DEC and INC to this list.
When you intend to use the accumulator as operand, you may omit the operand. An operand is omitted if the line ends directly after the instruction, or when the next non space character behind the instruction is more than 8 spaces away (10 spaces for Version 3).
On the other hand you may also specifically indicate the Accumulator as operand by using the letter A.


                ASL                 Implied Accumulator instruction
                ASL    A            The same instruction with different syntax
                ASL    $12          This instruction operates on the zero page

JMP (IND) bug

The NMOS 6502 version had some bugs in it. One of those bugs actually affects the assembly language programming in a very rare occasion. The bug I'm talking about is called the JMP (IND) bug, and will only be a problem when using this instruction/addressing mode combination.
This instruction points to a location in memory, reads one byte from that location and one byte from the next location, these 2 bytes will form a 16-bit JMP destination address. So far so good. But if the first address pointed to by the (IND) operand is at the end of a memory page ($xxFF), the next destination address byte is not taken from location $00 of the next memory page as it should do, but from location $00 of the current memory page.

Sounds difficult? It is not really. Here's an example: Suppose the instruction says JMP ($10FF), and the memory location $10FF holds $34, and $1100 holds $12. Normally, if the bug didn't exist the destination address of the JMP instruction would be $1234. But instead of reading addresses $10FF and $1100 to get the destination address, the NMOS 6502 reads the addresses $10FF and $1000. It is not very likely that address $1000 contains the expected value $12, so the destination jump address is unpredictable.

The SB-Assembler for the NMOS 6502 checks the indirect address to see if it is endangered by the hardware bug. If it is a *** 6502 JMP (IND) bug error is reported to warn you. It is up to you then to move the indirect jump table out of the danger zone. Adding one extra byte in front of the table will do the trick.
The Version 3 cross overlay will not report an error message. It reports a *** Warning: 6502 JMP (IND) bug at $xxFF instead.

Please note that this only applies to the NMOS version of the 6502, so the 65C02 processors are not affected. Therefore those assemblers won't generate the above mentioned error message.

Sweet-16 interpreter

Early Apple ][ people might know what the Sweet-16 interpreter did, although it was one of the least understood features of the first apple models. It is an interpreter, pretending to be a 16-bit machine on an 8-bit processor. The Sweet-16 interpreter was written by the famous Apple pioneer Steve Wozniak. Only the first Apple ][ models, the ones with Integer basic, are standard equipped with the Sweet-16 interpreter. Later came the models with Applesoft basic and the Autostart monitor ROM and they didn't have room for the interpreter in ROM anymore.

I've added the pseudo Sweet-16 instructions to the 6502 cross overlay family, for those of you who would like to play around with this interpreter. The trick to use the interpreter is simple:
Call the starting address of the interpreter with JSR SWEET16. All instructions following this JSR are interpreted by the Sweet-16 interpreter and not by the processor itself. Using the SB-Assembler you can simply write code with pseudo instructions, which are translated to represent the proper Sweet-16 instruction codes. The Sweet-16 interpreter is terminated when it executes the instruction RTN. Your normal 6502 program will continue after this Sweet-16 RTN instruction, as if nothing ever happened.

I have never actually used the Sweet-16 interpreter, I wonder even if someone ever has. But if you want to use it, the SB-Assembler won't stop you.
The opcode test file in the download package shows all Sweet-16 instructions.

I'm not going to include the Sweet-16 interpreter code on my home page or in the assembler package. I doubt that the original copyrights are still valid, but I'd better be safe than sorry. You can find lots of information about the Sweet-16 interpreter on the internet using one of the numerous search engines out there.
One of the best sources of information about the Sweet-16 interpreter can be found at www.6502.org.

Differences between SB-Assembler Version 2 and Version 3. With Version 2 the Sweet-16 and the real 6502 instructions are translated from the same big list. This means that the assembler will translate Sweet-16 code in between 6502 code, and the other way around, without complaining. In real life this will result in a program which won't work. Remember that the Sweet-16 interpreter only runs when it is called with JSR SWEET16.
With Version 3 by default the assembler will only translate real 65(C)02 instructions and will not tolerate Sweet-16 instructions at all. Only when a JSR SWEET16 is found it will switch over to Sweet-16 mode. From then on only Sweet-16 instructions are translated, and real 6502 instructions won't be tolerated anymore. The assembler will switch back to 65(C)02 mode again after translating the Sweet-16 RTN instruction.
This way the assembler will switch seamlessly to and from the Sweet-16 mode, avoiding the ADD and SUB confusion seen in Version 2.
For the assembler it doesn't really matter where the label SWEET16 points to, only the name matters. In real life it should point to the starting address of the Sweet-16 interpreter.

Differences in syntax from the real Sweet-16 assembler (if there ever was one). The original Sweet-16 syntax was SET R15 VALUE. The SB-Assembler uses its own syntax here, which is SET 15,VALUE. Thus R15 is now just 15 and the space between the two operand is replaced by a comma.
You could, if you want, declare labels like R15 .EQ 15 in order to use register names rather than just numbers.

Overlay Initialization

Two or three things are set while initializing the 6502 overlay family every time the overlay is loaded by the .CR directive.

  • Little endian model is selected for 16-bit addresses and for the .DA and .DL directives. This means that words or long words are stored with their low byte first.
  • The maximum program counter value is set to $FFFF.
  • The Version 3 cross will also disable the Sweet-16 mode when the cross is initialized.

Differences Between Other Assemblers

There are some differences between the SB-Assembler and other assemblers for the 6502 family processor. These differences require you to adapt existing source files before they can be assembled by the SB-Assembler. This is not too difficult though, and is the (small) price you have to pay for having a very universal cross assembler.

  • Not all assemblers have the compound instructions ADD and SUB.
  • Not all assemblers will understand forced zero page and absolute addressing modes.
  • Very few other assemblers will understand the Sweet-16 instructions.
  • Almost no other assemblers will warn you for the 6502 JMP (IND) bug.
  • The obvious differences in notation of directives common to all SB-Assembler crosses.
  • Don't forget that the SB-Assembler does not allow spaces in or between operands. Only Version 3 will allow one space after each comma separating operands in the operand field.