R3000 instruction set

From Final Fantasy Hacktics Wiki
Revision as of 18:11, 23 December 2020 by Raijinili (talk | contribs) (→‎Memory: Alignment)
Jump to navigation Jump to search

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 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.

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

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

mfhi

Move from high

Example:

mfhi $d

mflo

Move from low

Example:

mflo $d

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