Home Up
Home Teaching Glossary ARM Processors Supplements Prof issues About

MIPS to ARM Conversion

Some students will be approaching my text having already taken a course based on the MIPS microprocessor. Although the differences between MIPS and ARM are generally small in the scheme of things, they may look rather different at first sight.

This short article is intended to demonstrate that the transition from MIPS to ARM is easier that you might think.

First, I must emphasize that I am not suggesting that you translate MIPS code to ARM code line-by-line because that might not be efficient in many cases. Both MIPS and ARM instruction stet architectures have preferred ways of doing things. The purpose of this article is merely ARM familiarization for the MIPS programmer.

The Register Set

In a way, the register set is the easiest aspect of the conversion process to deal with. MIPS has 32 registers and ARM has 16. This means, of course, the ARM programmer has fewer registers to play with and, consequently, registers must be reused more frequently.

MIPS has an unusual naming convention because its registers are called $0 to $31. All MIPS registers use a dollar sign. ARM registers are called r0 to r15.

Note that MIPS registers have aliases. The alias of a register is a special name that indicates to programmers how it should be used. This is a suggestion or convention and is not enforced in hardware. It exists to make programs portable and to allow projects to be divided among groups of co-workers. ARM also has a convention governing the use and renaming of registers.

ARM registers r0 to r13 are general-purpose. Register r14 is the link register used to hold the return address after a subroutine call (branch and link). In the MIPS world, register $31 performs exactly the same function.

ARM’s In assembly language programs r15 can be written as pc because r15 is te ARM’s program counter. Very few other computers make the program counter visible to the programmer in the same way as the ARM. You cannot use ARM’s r15 as a general purpose register. Ever. Register r15 is typically used to return from a subroutine by writing mov pc,lr (or mov r15,r14).

MIPS uses register $0 in a special way. Register $0 always returns 0 when read and is not affected by a write. This feature gives you the constant 0 simply by writing $0; for example, or $1,$2,$0 performs a logical or between registers $2 and $0 and deposits the result in $1. Since register $0 returns zero, and ORing X with zero yields X, this operation is equivalent to move $1,$2. In other words, using register $0 as a zero allows us to generate new instructions (i.e., pseudo instructions) from existing instructions.

Let’s look at an example of a simple sequence of operations in MIPS and ARM code. This sequence is constructed to demonstrate how similar the two processors are for much of the time.

MIPS code                             ARM code                      Comment

la   $1,X         ADR r1,X       Load a pointer. Note use of pseudo instruction in both cases

la   $2,Y         ADR r2,Y  

la   $3,Z         ADR r3,Z  

1w   $1,0($1)     LDR r1,[r1]    Load the value of X

1w   $2,0($2)     LDR r2,[r2]  

1w   $3,0($3)     LDR r3,[r3]  

addu $2,$2,$1     ADD r2,r2,r1   Add X and Y

subu $2,$2,$3     SUB r2,r2,r3   Subtract Z

la   $4,P         ADR r4,P       Load pointer to P

sw   $2,0($4)     STR r2,[r4]  


The differences between MIPS and ARM are really entirely stylistic. Registers have different names. Mnemonics are different; for example lw (load word) for MIPS and LDR (load register) for ARM. By convention, MIPS programmers tend to use lower-case for mnemonics and ARM programmers upper-case. In register indirect addressing, MIPS uses round brackets and ARM square ones.

Note also that both MIPS and ARM use pseudo instructions to load 32-bit literals in registers.

We can repeat this example, using register indirect addressing with an offset assuming that the data items are stored consecutively in memory. In this example, we’ve used MIPS preferred names; that is, $t0 to $t7 as temporary registers for use in calculations.












We’ve also added data region (which MIPS requires prefixing with the .data directive). Again, it’s all a matter of convention. MIPS labels have a colon; ARM labels don’t. Similarly, MIPS assembler directives are prefixed by a period. Finally, the offset in register indirect addressing is outside round brackets for MIPS, and is inside square brackets and prefixed by a hash for ARM. ARM code requires that all literals (i.e., an immediate or constant) be prefixed by a hash.

Let’s look at a few more basic operations in both MIPS and ARM worlds.

MIPS code                                            ARM code                                 Comment

  # data in $t1-$t5     ; data in r1-r5    Yes, even comments are handled differently

  mul   $t1,$t2,$t3     MUL r1,r2,r3       The MIPS multiplication is a pseudo instruction

  addiu $t1,$t1,1       ADD r1,r1,#1       Add 1 to register 1

  ori   $t1,$t1,$t4     AND r1,r1,r4       Do an AND

  ori   $t1,$1,0xFF     ADD r1,r1,#0xFF    Do a logical OR with a literal

  move  $t5,$t1         MOV r5,r1          Note that the MIPS move is a pseudo instruction


Many basic data-processing operations are similar in MIPS and ARM. The MIPS move is a pseudo instruction because it’s implemented as or $t5,$t1,$0 which performs a logical OR with register 0 to give $t1. This is the advantage of having a register that is always zero.

Note the MIPS multiply instruction. That too is a pseudo instruction and requires some explanation. ARM implements a 32-bit by 32-bit multiplication that provides a 32-bit result in the destination register (i.e., only the 32 least-significant bits are preserved and the multiplication is really 16-bit by 16-bit). MIPS provides a true 32-bit by 32-bit multiplication and puts the entire 64-bit product in two internal registers reserved for multiplication and division. These registers are called hi and lo. The special instructions mfhi $t0, mflo $t0, mthi $t0, mtlo $t0 are used move from and to these registers, respectively.

However, the move pseudo instruction uses two operations, one to perform the multiplication and one to copy the low-order half of the product to the destination register.

Finally, the ARM’s multiplication instruction comes with a restriction. Because of the way in which internal registers are used for temporary storage during multiplication, you have to specify different destination and first source registers. You cannot write mul r1,r1,r2 in ARM code.

Unconditional Jump and Branch Group

The unconditional  jump and branch instructions are those that force a change in the flow of control. Essentially, they are absolute jumps to a give memory location, relative jumps (with respect to the program counter) or register indirect jumps to the contents of a location specified by a register.

Fortunately MIPS and ARM do not differ greatly in principle, but there are differences in detail.


MIPS code

ARM code

Comment

  la   $t1,X

 ADR r1,X

Load a pointer. Note use of pseudoinstruction in both cases

  1w   $t1,0($t1)

 LDR r1,[r1]

Load the value of X  

  1w   $t2,4($t2)

 LDR r2,[r2,#4]


  lw   $t3,8($t3)

 LDR r3,[r3,#8]


  addu $t2,$2,$t1

 ADD r2,r2,r1

Add X and Y

  subu $t2,$t2,$t3

 SUB r2,r2,r3

Subtract Z

  sw   $t2,12($t4)

 STR r2,[r4,#8]


  .data



X: DCW 0x1234

X DCW 0x1234

Declare X and put 0x1234 in memory

Y: .word 0x5678

Y DCW 0x5678


Z: .word 0xABCD

Z DCW 0xABCD

Declare P and reserve 4 bytes of memory

P: .space 4

P SPACE 4

Declare P and reserve 4 bytes of memory

Click to go to MIPS instruction encoding