Skip to footer navigation.

« Oatmeal

Posts tagged 6502

Follow this tag's bespoke rss feed or return to the list of all tags.

What is an addressing mode?

In a recent post I referenced addressing modes. But what the heck are they!?

Setting the stage

The instruction register holds the program instruction that is currently being run.

A fixed number of bits within the instruction register represent the operation, e.g. “op. code” — examples of these instructions include things like add, subtract, load, and store. We can imagine the instruction register like this:

ASCII diagram of an instruction register. Links to a txt of the same diagram.

There’s a fixed number of bits allocated to the op. code (the 6 left-most boxes), and then a fixed number of bits that hold the operand/s being operated on (remaining 10 boxes). An operand could be a value, a CPU register, or a memory address. This set of fixed bits is referred to as the address field.”

The number of bits allocated to the address field determines the amount of memory that can be addressed. The number of bits allocated to the op. codes determines how expressive the op. codes can be (or at least how many of them there can be).

Addressing modes provide different ways to use the addressable memory.

In my diagram, 2 bits of the operation code are used to determine the addressing mode. The addressing mode tells the processor how the bits in the address field should be interpreted.

For example…

  LDA #80
  LDA $80

These similar looking instructions are pretty different.

# tells us that the number following is a literal value.

$ tells us that the number following references a memory address.

So, LDA #80 loads the literal decimal value 80 into the A register and LDA $80 loads the value located at memory address $80 into the A register.

#80 is known as immediate mode because we are directly, or immediately, loading a value, while $80 is known as absolute, or zero page, mode.

What about a literal hex value?

BOOM!

  LDA #$80

This loads the literal hex value $80 (e.g. 128) into the A register.

Other resources

Notes on 6502 Assembly

The NES runs a very slightly modified 6502 processor. What follows are some very introductory, and not at all exhaustive notes on 6502 Assembly, or ASM.

If you find this at all interesting, Easy 6502 is a really great introductory primer on 6502 Assembly that lets you get your hands dirty right from a web browser.

Numbers

Numbers prefixed with one of the following:

  • $ are hexadecimal format
  • # are literal numbers

Any other number without either of these prefixes refers to a memory location.

So,

    LDA #$01

Loads the hex value $01 into register A.

Registers and flags

There are 3 primary registers,

  • A
  • X
  • Y

A is usually called the accumulator.

Each register holds a single byte

SP is the stack pointer, a register that is decremented every time a byte is pushed onto the stack and incremented whenever a byte is popped off the stack.

PC is the program counter. PC is how the processor keeps track of where in the currently running program it is.

Processor flags

Each flag is 1 bit, so all 7 flags can live in a single byte

More info on registers and flags.

Instructions

In 6502 Assembly instructions are like words in Forth, or functions in a higher order programming language. Every instruction takes 0 or 1 arguments.

An example of some instructions,

    LDA #$c0  ; Load the hex value $c0 into the A register
    TAX       ; Transfer the value in the A register to X
    INX       ; Increment the value in the X register
    ADC #$c4  ; Add the hex value $c4 to the A register
    BRK       ; Break - we're done

For a full list of 6502 ASM instructions see,

6502 ASM has a handful of branching instructions — they almost all rely on flags to determine what branch to follow.

Addressing modes

The 6502 has 65536 bytes of available memory. These bytes are typically described using the HEX range $0000 - $ffff.

When the 6502 refers to addressing modes, it really means What is the source of the data used in this instruction?”

The different modes are,

Absolute: $c000

With absolute addressing, the full memory location is used as the argument to the instruction.

Zero page: $c0

All instructions that support absolute addressing (with the exception of the jump instructions) also have the option to take a single-byte address. This type of addressing is called zero page” - only the first page (the first 256 bytes) of memory is accessible. This is faster, as only one byte needs to be looked up, and takes up less space in the assembled code as well.

Zero page,X: $c0,X

In this mode, a zero page address is given, and then the value of the X register is added.

Zero page,Y: $c0,Y

This is the equivalent of zero page,X, but can only be used with LDX and STX.

Absolute,X and absolute,Y: $c000,X and $c000,Y

These are the absolute addressing versions of zero page,X and zero page,Y.

Immediate: #$c0

Immediate addressing doesn’t strictly deal with memory addresses - this is the mode where actual values are used. For example, LDX #$01 loads the value $01 into the X register. This is very different to the zero page instruction LDX $01 that loads the value at memory location $01 into the X register.

Relative: $c0 (or label)

Relative addressing is used for branching instructions. These instructions take a single byte, which is used as an offset from the following instruction.

Implicit

Some instructions don’t deal with memory locations, for example, INX - increment the X register. These have implicit addressing because the argument is implied by the instruction.

Indirect: ($c000)

Indirect addressing uses an absolute address to look up another address. The first address gives the least significant byte of the address, and the following byte gives the most significant byte.

Indexed indirect: ($c0,X)

This one’s kinda weird. It’s like a cross between zero page,X and indirect. Basically, you take the zero page address, add the value of the X register to it, then use that to look up a two-byte address.

Indirect indexed: ($c0),Y

Indirect indexed is like indexed indirect, but instead of adding the X register to the address before de-referencing, the zero page address is de-referenced, and the Y register is added to the resulting address.

For more on the different modes of addressing,

The stack

The current depth of the stack is measured by the stack pointer, a special register. The stack lives in memory between $0100 and $01ff. The stack pointer is initially $ff, which points to memory location $01ff. When a byte is pushed onto the stack, the stack pointer becomes $fe, or memory location $01fe, and so on.

Jumping

Jumping is like branching with two main differences:

  • First, jumps are not conditionally executed
  • Second, they take a two-byte absolute address

For small programs, this second detail isn’t important, as you’ll be using labels, and the assembler works out the correct memory location from the label. For larger programs though, jumping is the only way to move from one section of the code to another.

Other Resources

Because these are but the barest of minimum notes, here are some more resources for continued reference.