6502 to ARM

From ARMwiki
Jump to: navigation, search

Migrating from a simple 8 bit processor to the ARM is not as difficult as it may seem. There are many differences, so this document will guide you through the main ones.


The 6502 has many instructions that directly use memory, for example ADC will add a memory location to the accumulator. INC will increment a memory location.

The ARM, on the other hand, is a Load/Store architecture processor. There are few operations that affect memory. Most of the operations affect registers. Consequently there is no Accumulator.

To increment a number in memory, in ARM code, you would:

  LDR  R0, [number_pointer]       ; read number from memory into R0
  ADD  R0, R0, #1                 ; increment it
  STR  R0, [number_pointer]       ; write it back

While this may seem a lot more wasteful than a single INC, it wins in the large number of registers and the flexibility to rethink your algorithm. Memory accesses are always slower than the processor, so the more you can keep in registers, the better.

Consider a simple encoding scheme where a series of bytes are to be EORed with a certain key value which changes depending on the value of the previous byte. With a processor such as the 6502, you need to load it in a byte at a time, and you can't reserve a register for indexing as the files may be 200KiB long...
On the ARM, you can go haywire - a register pointing to the source memory block, one pointing to the destination memory block, one being a pointer into the block(s), and one being the extent. Then you can word-load data into R0, split it up into individual bytes, run the algorithm (using a register to hold the current EOR key, and the last-byte value), reassemble these bytes back into the word in R0, and store it. Thus, only the necessary memory access for data read/write is required, the entire rest of the routine runs in registers.


The 6502 has three registers - X, Y, and A.

The ARM has - as far as you are likely to be concerned - 16. These are called R0 to R15. Of these, three have special meanings:

R13 Stack Pointer
R14 Return address
R15 Program Counter

As you can see, there are several differences of note:

  • There is no SP register. By convention, R13 is used as the Stack Pointer.
  • A [B|BL] is like a JSR, but nothing is written to the stack. A return address is placed in R14. It it up to you to work out if you need to preserve it.
  • There is no specific Program Counter like on the 6502. It is the register R15 which, apart from its dedicated use as the Program Counter, is like any other register. This matters, because you can use maths writing to R15 to implement a jump table, and so on.

LD* and ST*

The 6502 offers intructions to load data into specific registers: LDA, LDX, LDY, and corresponding ST*<code> versions.

On the ARM, there are several methods, all of which are register-blind.

  • To load a word into a register from memory, you would use:
  LDR   R*, [address]    ; where * is any required register number
  • To load multiple words from memory into multiple registers, you would use:
  LDM## R*, {R*-R*}

This is somewhat more complicated, please refer to the [LDM] documentation for details.

  • Finally, to set R* to point to a memory location, you can either LDR an address pointer, or if the memory location is close (within 4KiB), you can:
  ADR   R*, address

Note that [ADR] is not a real instruction, it is a command to the assember to take a note of where the address is, and set up R* as necessary in order to point to it.

Personal tools