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.