Thursday, January 23, 2014

A Tour of the 6502

Before I start writing code, it would probably be a good idea to quickly go over the 6502 processor that the NES uses. This is just a quick overview. As 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 then this should be enough info to let you follow what I am doing.

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), X register (X), Y register (Y), the Flag register aka 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 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 NV-BDIZC. 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. V is for oVerflow and is set when an operation results in a number larger than a byte.  The - flag is reserved and I am not sure what the B flag does but it stands for break so I am assuming it is for hard breaks. D is to indicate if the chip is in Binary Coded Decimal mode or not but as the NES doesn't support BCD, the only thing ever done with this is clearing it. The I flag is the interrupt flag and is used to temporarily disable interrupts. 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 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.

This is all easy enough, but to complicate things, some 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. Here is a summary of the instructions that the 6502 supports grouped into types of operations the commands perform.

Memory Manipulation
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)

Comparison Instructions
BIT (BIT test)
CMP (CoMPare with accumulator)
CPX (ComPare with X)
CPY (ComPare with Y)

Branching instructions
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)
JSR (Jump to SubRoutine)
RTI (ReTurn from Interrupt)
RTS     (ReTurn from Subroutine)

Flag Management
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)

Debugging aids
NOP (No OPeration)

No comments: