Introducing the 6502
This article is a re-written version of an article I wrote for my Development Blog which was titles "A Tour of the 6502" 1. As I am the author of the article this is based on, I clearly have permission to use this material. The text has been cleaned up from the original version and I have made sure all the material is consistent with the information listed in the Commodore 64 Programming Reference Guide2 which I used for obtaining the information.
This is just a quick overview of the 6502, which is the processor that is used by the Atari 2600. If you are not familiar with assembly language, it is probably recommended that you find one of the many tutorials on the subject. If I have time, I will cover the material here in much more detail. The 6502 processor is fairly simple so if you are already familiar with programming (especially other assembly languages) then this should be enough info to let you grasp how to program this processor.
The 6502 is an 8-bit processor with a 16 bit address range. This means that it works with one byte at a time and can handle 65536 bytes of memory. Techniques like bank-switching allow for more memory but that will be covered later. It has 6 registers to hold the information it is working on. These are the program counter (PC), accumulator (A), index X (X), index Y (Y), the Flag register aka Processor Status Register (SR), and the stack pointer (SP).
The program counter is a 16-bit register and holds the current address that the processor is executing. The accumulator is a general-purpose register that is the only register that math operations can be applied to. The X and Y registers are index registers, which means they are used for counting. They can also be used for temporary storage. The program status register holds flags that hold information about what happened in previous operations. Finally, the stack pointer is an index holding which byte the top of the stack is at.
The flags that are tracked are NZCIDV. N stands for negative and is set if the highest bit of the last byte-related operation is set as signed bytes use this bit to indicate if a number is negative. The Z flag is set whenever an operation results in a value of zero. Finally, the C flag is used for math operations for dealing with multi-byte numbers.The I flag is the interrupt flag and is used to temporarily disable interrupts. D is to indicate if the chip is in Binary Coded Decimal mode or not. V is for oVerflow and is set when an operation results in a number larger than a byte.
The 6502 makes no distinction between program memory and data memory so it is possible, though probably not wise, to create self-modifying programs. Certain Commodore 64 copy-protection schemes actually took advantage of this to obfuscate how they worked. Commands take the form of an Operation Code (OP Code) which may be followed by one or two data bytes for holding a number or an address. The Op Code is simply a number which indicates which command should be executed. The nice thing about assembly language is that you do not need to know the numbers for the op codes you just need to use easy to remember 3 letter commands. These are actually fairly easy to learn and once you have learned them you will find it easy to remember them even decades later.
This is all easy enough, but to complicate things, many instructions have a number of modes in which they can be used. These are simply different ways of accessing memory. There are 13 different addressing modes. The following shows how the mode is coded in assembly language followed by a brief description of the mode (note that $ indicates a hexadecimal number, % a binary number and no prefix a decimal number):
- OPC A ; Accumulator - value in accumulator used for data
- OPC $1234 ; Absolute - uses hard coded address
- OPC $1234,X ; Absolute, X - hard coded address with value in X Register added to it
- OPC $1234,Y ; Absolute, Y - hard coded address with value in Y Register added to it
- OPC #$12 ; Immediate - uses provided byte value
- OPC ; Implied - no operand
- OPC ($1234) ; Absolute Indirect - the hard coded address is used as a pointer to the real address
- OPC ($12,X) ; Indirect, X - hard coded zero-page address with value in X Register added to it
- OPC ($12),Y ; Indirect, Y - hard coded zero page address and consecutive byte form pointer to the real base address which is then increased by the value in the Y register to form the final address.
- OPC $12 ; Relative - signed byte is added to the program counter to determine address. Used for branching instructions.
- OPC $12 ; Zero Page - hard coded zero page address
- OPC $12,X ; Zero Page, X - hard coded zero page address with value of X register added to it.
- OPC $12,Y ; Zero Page, Y - hard coded zero page address with value of Y register added to it.
Not every addressing mode can be used with every instruction. Though generally it is clear what modes work with what instructions. Still, there are many times where you will need to look up which modes a particular instruction can use. Here is a summary of the instructions that the 6502 supports grouped into types of operations the commands perform.
- LDA - (LoaD Accumulator)
- LDY - (LoaD X)
- LDY - (LoaD Y)
- PHA - (PusH Accumulator)
- PHP - (PusH Processor status [flags])
- PLA - (PulL Accumulator)
- PLP - (PulL Processor status [flags])
- STA - (STore Accumulator)
- STX - (STore X)
- STY - (STore Y)
- TAX - (Transfer Accumulator to X)
- TAY - (Transfer Accumulator to Y)
- TSX - (Transfer Stack pointer to X)
- TXA - (Transfer X to Accumulator)
- TXS - (Transfer X to Stack pointer)
- TYA - (Transfer Y to Accumulator)
Math and Boolean logic instructions
- ADC - (ADd with Carry)
- AND - (logical AND)
- ASL - (Arithmetic Shift Left)
- DEC - (DECrement)
- DEX - (DEcrement X)
- DEY - (DEcrement Y)
- EOR - (Exclusive OR with accumulator)
- INC - (INCrement)
- INX - (INcrement X)
- INY - (INcrement Y)
- LSR - (Logical Shift Right)
- ORA - (OR with Accumulator)
- ROL - (ROtate Left)
- ROR - (ROtate Right)
- SBC - (SuBtract with Carry)
- BIT - (BIT test)
- CMP - (CoMPare with accumulator)
- CPX - (ComPare with X)
- CPY - (ComPare with Y)
- BCC - (Branch on Carry Clear)
- BCS - (Branch on Carry Set)
- BEQ - (Branch on EQual [zero flag set])
- BMI - (Branch on MInus [negative flag set])
- BNE - (Branch on Not Equal [zero flag clear])
- BPL - (Branch on PLus [negative flag clear])
- BVC - (Branch on oVerflow Clear)
- BVS - (Branch on oVerflow Set)
- JMP - (JuMP)
- JSR - (Jump to SubRoutine)
- RTI - (ReTurn from Interrupt)
- RTS - (ReTurn from Subroutine)
- CLC - (CLear Carry flag)
- CLD - (CLear Decimal flag)
- CLI - (CLear Interrupt disable flag)
- CLV - (CLear oVerflow)
- SEC - (SEt Carry)
- SED - (SEt Decimal)
- SEI - (SEt Interrupt disable)
- BRK - (BReaK)
- NOP - (No Operation)
For the assembly language programmers the arrow links to a table of all the instructions, with their timing and OP codes: 6502 Instructions