Difference between revisions of "R3000 instruction set"

From Final Fantasy Hacktics Wiki
Jump to navigation Jump to search
(→‎Registers: new section)
 
(10 intermediate revisions by 4 users not shown)
Line 100: Line 100:
  
 
==== mult ====
 
==== mult ====
 +
<span id = "InstructionMult"></span>
  
 
  mult $s,$t
 
  mult $s,$t
Line 109: Line 110:
 
This instruction is slow, so the compiler might instead use a combination of shifts and additions for multiplications by constants.
 
This instruction is slow, so the compiler might instead use a combination of shifts and additions for multiplications by constants.
  
This instruction is sometimes used to multiply by positive decimals less than 1. For example, to multiply by 0.14, the compiler can multiply by an integer close to 0.14 * 2^32, and then only take the high part of the result.
+
This instruction is sometimes used to multiply by positive fractions less than 1. For example, to multiply by 0.14, the compiler can multiply by an integer close to 0.14 * 2^32, and then only take the high part of the result.
  
 
Example:
 
Example:
Line 120: Line 121:
 
==== multu ====
 
==== multu ====
  
Multiply unsigned
+
Multiply unsigned. Multiply as if the arguments are 32-bit unsigned integers.
  
 
Example:
 
Example:
Line 127: Line 128:
 
==== div ====
 
==== div ====
  
Divide
+
Divide (signed).
 +
 
 +
The quotient $s / $t is stored in the LO register, while the remainder $s % $t is stored in the HI register. See [[#mult]].
  
 
Example:
 
Example:
Line 138: Line 141:
 
Example:
 
Example:
 
  divu $s, $t
 
  divu $s, $t
 +
 +
==== mflo ====
 +
==== mfhi ====
 +
 +
Move from [low/high]
 +
 +
Copy from the [LO/HI] special register to the given destination register.
 +
 +
Used to retrieve results from [[#mult|multiplication]] and [[#div|division]] instructions.
 +
 +
Syntax:
 +
mfhi $d
 +
mflo $d
  
 
=== Memory ===
 
=== Memory ===
  
 
Getting data from/to the RAM.
 
Getting data from/to the RAM.
 +
 +
'''Warning:''' Memory instructions require aligned addresses.
 +
* '''word''' (= 32 bits = 4 bytes): Requires address divisible by 4. The hex address must end with 0, 4, 8, or C.
 +
* '''halfword''' (= 16 bits, = 2 bytes: Requires address divisible by 2. The hex address must end with one of: 0 2 4 6 8 A C E.
 +
* '''byte (= 8 bits): No requirements.
  
 
==== lw ====
 
==== lw ====
Line 177: Line 198:
 
Example:
 
Example:
 
  lbu $t,C($s)
 
  lbu $t,C($s)
 +
 +
==== lwl ====
 +
==== lwr ====
 +
 +
Load word left/right.
 +
 +
Used for unaligned loads (loads where you want a 4-byte word, but the address is not divisible by 4). For example, 0x80 is aligned, but 0x81, 0x82, and 0x83 are not aligned to 4.
 +
 +
lwr takes the first byte to load, while lwl takes the last byte.
 +
 +
Example: Assume r7 is aligned. To read the four bytes starting from r7+0x4e:
 +
  lwl r2,0x0051(r7)
 +
  lwr r2,0x004e(r7)
  
 
==== sw ====
 
==== sw ====
Line 203: Line 237:
 
Load upper immediate
 
Load upper immediate
  
Example:
+
Loads the given 16-bit constant into the top 16 bits of the given register.
lui $t,C
 
  
==== mfhi ====
+
Not a memory access, but often used with memory access instructions.
 
 
Move from high
 
  
 
Example:
 
Example:
  mfhi $d
+
  lui $t,C
 
 
==== mflo ====
 
 
 
Move from low
 
 
 
Example:
 
mflo $d
 
  
 
=== Comparison ===
 
=== Comparison ===
Line 338: Line 362:
 
Branch on equal
 
Branch on equal
  
Example:
+
Format:
 
  beq $s,$t,C
 
  beq $s,$t,C
  
Line 345: Line 369:
 
Branch on not equal
 
Branch on not equal
  
Example:
+
Format:
 
  bne $s,$t,C
 
  bne $s,$t,C
  
 
==== j ====
 
==== j ====
  
Jump
+
Jump. Jump to the given constant address.
  
Example:
+
Format:
 
  j C
 
  j C
  
 
==== jr ====
 
==== jr ====
  
Jump register
+
Jump register. Like [[#j]], but jumps to the address stored in the given register.
  
Example:
+
Format:
 
  jr $s
 
  jr $s
  
 
==== jal ====
 
==== jal ====
  
Jump and link
+
Jump and link. Used for calling subroutines.
  
Example:
+
Stores PC+8 (current instruction address plus 8) into r31, and then jump to the given constant. The idea is, when the subroutine is done, it will jump back to the instruction two steps after this one (thus, linking to PC+8).
 +
 
 +
Format:
 
  jal C
 
  jal C
 +
 +
 +
==== jalr ====
 +
 +
Jump and link register. Like jal, but jumps to the address in the source register instead of to a constant.
 +
 +
The two-argument form also states which register to store the link (PC+8) into. The one-argument form assumes r31 is the destination register.
 +
 +
'''Warning:''' [[psxfin]]'s disassembly has the arguments for the 2-arg form in the wrong order. This wiki and FFH's tools use the wrong order, for consistency with psxfin.
 +
 +
Format (psxfin):
 +
jalr $s
 +
jalr $s,$d
 +
 +
Format ([https://dannyqiu.me/mips-interpreter/mips_vol2.pdf MIPS specification], page 117):
 +
jalr $s
 +
jalr $d,$s
 +
 +
== See also ==
 +
 +
* [[PSX instruction set]]

Latest revision as of 16:35, 25 June 2023

Intro

Registers

Registers are 32-bit boxes that hold values during computation.

Registers that can be used as arguments to instructions.

  • r0: Always 0. Writing to this register does nothing.
  • r1: Reserved for the assembler. For ASM hacking, you're basically the assembler, so it might be safe to use, until someone figures out it isn't.
  • r2-r3: Used as scrap paper for work.
  • r4-r7: Used as arguments.
  • r8-r15: Temporaries.
  • r16-r23: Non-temporaries.
  • r24-r25: Temporaries.
  • r26-r27: Reserved. Do not modify.
  • r28: Global pointer. Do not modify.
  • r29: Stack pointer. Points to the first free address on the stack. Change this to make more room on the stack. As more things are put on the stack, this pointer decreases.
  • r30: Frame pointer. Do not modify.
  • r31: Typically the return address. The jal instruction implicitly writes to this register. Do not modify except to restore an earlier value.

The following registers are expected to be preserved across calls. If used, their original values must be restored before returning.

  • r16 - r23
  • r26 - r27 (maybe)
  • r28 - r31 (probably)

The following registers are expected NOT to be preserved across calls. A subroutine is free to change the value of these registers. After a call, these registers should be assumed to have garbage values (except r2).

  • r2-r3: If the function has a return value, that value is stored here.
  • r4-r7: The first four arguments of a call. (Additional arguments are passed on the stack.)
  • r8-r15, r24-r25: Nothing special.

For calculations, use r2 and r3, then r4-r15 and r24-r25. Use r16-r23 for values that need to be preserved while calling a function, but make sure to store their original values on the stack, and to restore them before the return.

There are other registers, but they are special and require special instructions to use.

Instructions

Note: This section is meant to be linked to, and used to generate tooltips (title text) for code pages.

The first paragraph of an instruction's description should be a short one-liner. It will be used as the tooltip. Think "reminder", rather than "explanation".

These are stolen from Wikipedia. Whoops.

nop

No operation

Used to cause delays between instructions, because some instructions step on each other's toes.

Example:

nop

Arithmetic

These instructions act on registers as 32-bit two's-complement integers.

add

Add

Example:

add $d,$s,$t

addu

Add unsigned

Example:

addu $d,$s,$t

This operation is used to add both signed and unsigned integers (and is in fact the default used by C compilers). The "unsigned" part is misleading: it means that, if the sign changes, don't set the "overflow" error flag, which can cause an exception handler to trigger.

sub

Subtract

Example:

sub $d,$s,$t

subu

Subtract unsigned

Example:

subu $d,$s,$t

addi

Add immediate

Example:

addi $t,$s,C

addiu

Add immediate unsigned

Example:

addiu $t,$s,C

mult

mult $s,$t

Multiply $s and $t.

Unlike most instructions, this instruction doesn't say where to store the result. The product of two 32-bit integers is at most 64 bits long, so the top and bottom parts of the result are stored separately. To access them, use the #mflo (move from low) and #mfhi (move from high) instructions.

This instruction is slow, so the compiler might instead use a combination of shifts and additions for multiplications by constants.

This instruction is sometimes used to multiply by positive fractions less than 1. For example, to multiply by 0.14, the compiler can multiply by an integer close to 0.14 * 2^32, and then only take the high part of the result.

Example:

mult r2,r3        Multiply
mflo r4           r4 = the lower part of the product
mfhi r5           r5 = the upper part of the product
                  (r5 * 2^32) + r4 == r2 * r3 (mathematically)

multu

Multiply unsigned. Multiply as if the arguments are 32-bit unsigned integers.

Example:

multu $s,$t

div

Divide (signed).

The quotient $s / $t is stored in the LO register, while the remainder $s % $t is stored in the HI register. See #mult.

Example:

div $s, $t

divu

Divide unsigned

Example:

divu $s, $t

mflo

mfhi

Move from [low/high]

Copy from the [LO/HI] special register to the given destination register.

Used to retrieve results from multiplication and division instructions.

Syntax:

mfhi $d
mflo $d

Memory

Getting data from/to the RAM.

Warning: Memory instructions require aligned addresses.

  • word (= 32 bits = 4 bytes): Requires address divisible by 4. The hex address must end with 0, 4, 8, or C.
  • halfword (= 16 bits, = 2 bytes: Requires address divisible by 2. The hex address must end with one of: 0 2 4 6 8 A C E.
  • byte (= 8 bits): No requirements.

lw

Load word

Example:

lw $t,C($s)

lh

Load halfword

Example:

lh $t,C($s)

lhu

Load halfword unsigned

Example:

lhu $t,C($s)

lb

Load byte

Example:

lb $t,C($s)

lbu

Load byte unsigned

Example:

lbu $t,C($s)

lwl

lwr

Load word left/right.

Used for unaligned loads (loads where you want a 4-byte word, but the address is not divisible by 4). For example, 0x80 is aligned, but 0x81, 0x82, and 0x83 are not aligned to 4.

lwr takes the first byte to load, while lwl takes the last byte.

Example: Assume r7 is aligned. To read the four bytes starting from r7+0x4e:

 lwl r2,0x0051(r7)
 lwr r2,0x004e(r7)

sw

Store word

Example:

sw $t,C($s)

sh

Store half

Example:

sh $t,C($s)

sb

Store byte

Example:

sb $t,C($s)

lui

Load upper immediate

Loads the given 16-bit constant into the top 16 bits of the given register.

Not a memory access, but often used with memory access instructions.

Example:

lui $t,C

Comparison

Comparing numbers.

Set $d to 1 if true, 0 if false.

slt

Set on less than

Example:

slt $d,$s,$t

slti

Set on less than immediate

Example:

slti $t,$s,C

Binary

These instructions act on registers as sequences of 0's and 1's.

and

And

Example:

and $d,$s,$t

andi

And immediate

Example:

andi $t,$s,C

or

Or

Example:

or $d,$s,$t

ori

Or immediate

Example:

ori $t,$s,C

xor

Exclusive or

Example:

xor $d,$s,$t

nor

Nor

Example:

nor $d,$s,$t

sll

Shift left logical immediate

Example:

sll $d,$t,shamt

srl

Shift right logical immediate

Example:

srl $d,$t,shamt

sra

Shift right arithmetic immediate

Example:

sra $d,$t,shamt

sllv

Shift left logical

Example:

sllv $d,$t,$s

srlv

Shift right logical

Example:

srlv $d,$t,$s

srav

Shift right arithmetic

Example:

srav $d,$t,$s

Control

These instructions may or will cause a jump to a different part of the program.

The instruction right after one of these will always be executed, even if there's a jump.

beq

Branch on equal

Format:

beq $s,$t,C

bne

Branch on not equal

Format:

bne $s,$t,C

j

Jump. Jump to the given constant address.

Format:

j C

jr

Jump register. Like #j, but jumps to the address stored in the given register.

Format:

jr $s

jal

Jump and link. Used for calling subroutines.

Stores PC+8 (current instruction address plus 8) into r31, and then jump to the given constant. The idea is, when the subroutine is done, it will jump back to the instruction two steps after this one (thus, linking to PC+8).

Format:

jal C


jalr

Jump and link register. Like jal, but jumps to the address in the source register instead of to a constant.

The two-argument form also states which register to store the link (PC+8) into. The one-argument form assumes r31 is the destination register.

Warning: psxfin's disassembly has the arguments for the 2-arg form in the wrong order. This wiki and FFH's tools use the wrong order, for consistency with psxfin.

Format (psxfin):

jalr $s
jalr $s,$d

Format (MIPS specification, page 117):

jalr $s
jalr $d,$s

See also