psx.wiki
Log in
Main page
Hardware
Hardware tests
System software
SDKs
File formats
Games
Tools
History
All pages
page
view source
history
View source for CPU Instruction Set
← CPU Instruction Set
You do not have permission to edit this page.
Log in
to edit.
--- title: CPU Instruction Set type: register summary: Encoding, operation and timing of every R3000A instruction on the PlayStation. category: Hardware --- ## Summary The PlayStation CPU executes the R3000A instruction set. Every instruction is one 32-bit word, and bits 26 to 31 hold the primary opcode. Primary opcode 00h (SPECIAL) uses bits 0 to 5 as a secondary opcode, and primary opcode 01h (BcondZ) uses bits 16 to 20 to choose the branch condition.[^enc] An opcode marked N/A in the tables below causes a reserved instruction exception (excode 0Ah). Unused operand bits, such as bits 21 to 25 of LUI, should be zero, but setting them does not necessarily cause an exception.[^ill] ## Registers The CPU has 32 general-purpose registers of 32 bits, a program counter and the HI and LO registers that receive multiply and divide results. R0 always reads 0. The compiler assigns each register a use, and assembler code that works with compiled code must follow the same assignment. The [CPU](/hardware/cpu/) page lists the assignments in Sony's table.[^hw25][^reg] R31 (RA) is an ordinary register, but JAL, BLTZAL and BGEZAL write their return address there. JALR may write its return address to any register from R1 to R30 instead, and exceptions store theirs in the COP0 EPC register.[^reg] ## Instruction Encoding The fields of an instruction word are as follows:[^enc] Table: Instruction fields | Bits | Width | Field | | --- | --- | --- | | 26 to 31 | 6 | Primary opcode | | 21 to 25 | 5 | rs, source register | | 16 to 20 | 5 | rt, target register (or the BcondZ condition) | | 11 to 15 | 5 | rd, destination register | | 6 to 10 | 5 | imm5, shift amount | | 0 to 5 | 6 | Secondary opcode (SPECIAL) | | 0 to 15 | 16 | imm16, immediate or branch offset | | 0 to 25 | 26 | imm26, jump target | ### Primary Opcodes Table: Primary opcode field (bits 26 to 31) | Row | +0h | +1h | +2h | +3h | +4h | +5h | +6h | +7h | | --- | --- | --- | --- | --- | --- | --- | --- | --- | | 00h | SPECIAL | BcondZ | J | JAL | BEQ | BNE | BLEZ | BGTZ | | 08h | ADDI | ADDIU | SLTI | SLTIU | ANDI | ORI | XORI | LUI | | 10h | COP0 | COP1 | COP2 | COP3 | N/A | N/A | N/A | N/A | | 18h | N/A | N/A | N/A | N/A | N/A | N/A | N/A | N/A | | 20h | LB | LH | LWL | LW | LBU | LHU | LWR | N/A | | 28h | SB | SH | SWL | SW | N/A | N/A | SWR | N/A | | 30h | LWC0 | LWC1 | LWC2 | LWC3 | N/A | N/A | N/A | N/A | | 38h | SWC0 | SWC1 | SWC2 | SWC3 | N/A | N/A | N/A | N/A | ### Secondary Opcodes Table: Secondary opcode field (bits 0 to 5 of SPECIAL) | Row | +0h | +1h | +2h | +3h | +4h | +5h | +6h | +7h | | --- | --- | --- | --- | --- | --- | --- | --- | --- | | 00h | SLL | N/A | SRL | SRA | SLLV | N/A | SRLV | SRAV | | 08h | JR | JALR | N/A | N/A | SYSCALL | BREAK | N/A | N/A | | 10h | MFHI | MTHI | MFLO | MTLO | N/A | N/A | N/A | N/A | | 18h | MULT | MULTU | DIV | DIVU | N/A | N/A | N/A | N/A | | 20h | ADD | ADDU | SUB | SUBU | AND | OR | XOR | NOR | | 28h | N/A | N/A | SLT | SLTU | N/A | N/A | N/A | N/A | | 30h | N/A | N/A | N/A | N/A | N/A | N/A | N/A | N/A | | 38h | N/A | N/A | N/A | N/A | N/A | N/A | N/A | N/A | ### Instruction Formats Each group of instructions places its operands as shown below:[^enc] Table: Instruction formats | 31..26 | 25..21 | 20..16 | 15..11 | 10..6 | 5..0 | Group | | --- | --- | --- | --- | --- | --- | --- | | 000000 | | rt | rd | imm5 | 0000xx | Shift by immediate | | 000000 | rs | rt | rd | | 0001xx | Shift by register | | 000000 | rs | | | | 001000 | JR | | 000000 | rs | | rd | | 001001 | JALR | | 000000 | 20-bit comment | | | | 00110x | SYSCALL, BREAK | | 000000 | | | rd | | 0100x0 | MFHI, MFLO | | 000000 | rs | | | | 0100x1 | MTHI, MTLO | | 000000 | rs | rt | | | 0110xx | Multiply and divide | | 000000 | rs | rt | rd | | 10xxxx | ALU with registers | | 000001 | rs | 00000 | imm16 | | | BLTZ | | 000001 | rs | 00001 | imm16 | | | BGEZ | | 000001 | rs | 10000 | imm16 | | | BLTZAL | | 000001 | rs | 10001 | imm16 | | | BGEZAL | | 00001x | imm26 | | | | | J, JAL | | 00010x | rs | rt | imm16 | | | BEQ, BNE | | 00011x | rs | | imm16 | | | BLEZ, BGTZ | | 001xxx | rs | rt | imm16 | | | ALU with immediate | | 001111 | | rt | imm16 | | | LUI | | 100xxx | rs | rt | imm16 | | | Load rt from [rs+imm] | | 101xxx | rs | rt | imm16 | | | Store rt to [rs+imm] | | x1xxxx | Coprocessor specific | | | | | Coprocessor | ## Load and Store Instructions Loads and stores address memory as rs plus a sign-extended 16-bit offset. A load that faults leaves rt unchanged.[^ld] Table: Load and store instructions | Instruction | Opcode | Operation | | --- | --- | --- | | LB rt,imm(rs) | 20h | rt = byte at [imm+rs], sign-extended | | LBU rt,imm(rs) | 24h | rt = byte at [imm+rs], zero-extended | | LH rt,imm(rs) | 21h | rt = halfword at [imm+rs], sign-extended | | LHU rt,imm(rs) | 25h | rt = halfword at [imm+rs], zero-extended | | LW rt,imm(rs) | 23h | rt = word at [imm+rs] | | SB rt,imm(rs) | 28h | byte at [imm+rs] = rt AND FFh | | SH rt,imm(rs) | 29h | halfword at [imm+rs] = rt AND FFFFh | | SW rt,imm(rs) | 2Bh | word at [imm+rs] = rt | | LWL rt,imm(rs) | 22h | Load the left (upper) bytes of rt | | LWR rt,imm(rs) | 26h | Load the right (lower) bytes of rt | | SWL rt,imm(rs) | 2Ah | Store the left (upper) bytes of rt | | SWR rt,imm(rs) | 2Eh | Store the right (lower) bytes of rt | ### Alignment The R3000 requires strict address alignment. Word data must start at a multiple of 4 and halfword data at a multiple of 2, and a data load, data store or instruction fetch that breaks this rule causes an address error. Bytes have no alignment restriction.[^hw25][^ld] ### Load Delay The loaded data is not available to the next instruction, because the target register is updated only after that instruction completes. If the next instruction reads the target register it usually receives the old value. However, if an interrupt occurs between the two instructions, the load completes during the interrupt and the next instruction receives the new value.[^ldd] Reads from coprocessor registers with MFC2 and CFC2 have the same 1-instruction delay.[^ldd] ### Stores and the Write Queue A store enters the 4-word write queue and completes in one cycle unless the queue is full, in which case the CPU waits for a free entry. During an 8-bit or 16-bit store all 32 bits of the source register are placed on the bus. A narrow store to a 32-bit I/O register therefore writes the full register value.[^st] ### Unaligned Access LWL, LWR, SWL and SWR access a word at any address. LWR and SWR transfer the low bytes of rt up to the next word boundary, and LWL and SWL transfer the high bytes down to the previous boundary. The bytes not transferred stay unchanged in both rt and memory, so a pair of instructions covers a whole unaligned word.[^ul] Table: LWR and SWR byte transfer | Address | Bytes of rt | Memory | | --- | --- | --- | | N x 4 + 0 | All 32 bits | N x 4 + 0 to 3 | | N x 4 + 1 | Lower 24 bits | N x 4 + 1 to 3 | | N x 4 + 2 | Lower 16 bits | N x 4 + 2 to 3 | | N x 4 + 3 | Lower 8 bits | N x 4 + 3 | Table: LWL and SWL byte transfer | Address | Bytes of rt | Memory | | --- | --- | --- | | N x 4 + 0 | Upper 8 bits | N x 4 + 0 | | N x 4 + 1 | Upper 16 bits | N x 4 + 0 to 1 | | N x 4 + 2 | Upper 24 bits | N x 4 + 0 to 2 | | N x 4 + 3 | All 32 bits | N x 4 + 0 to 3 | LWL and LWR need no delay between them, even though both write the same register. The load delay applies only after the second of the two:[^ul] ``` lwl r2,$0003(t0) ;no delay required between these lwr r2,$0000(t0) ;although both access r2 nop ;load delay before reading r2 and r2,r2,0ffffh ;r2 now holds the unaligned data ``` ## ALU Instructions ADD, SUB and ADDI trap on signed overflow and leave the destination unchanged. The other arithmetic instructions never trap, and the shift instructions do not raise exceptions on overflow either.[^alu] Table: Arithmetic instructions | Instruction | Opcode | Operation | | --- | --- | --- | | ADD rd,rs,rt | 00h/20h | rd = rs + rt, with overflow trap | | ADDU rd,rs,rt | 00h/21h | rd = rs + rt | | SUB rd,rs,rt | 00h/22h | rd = rs - rt, with overflow trap | | SUBU rd,rs,rt | 00h/23h | rd = rs - rt | | ADDI rt,rs,imm | 08h | rt = rs + (-8000h to +7FFFh), with overflow trap | | ADDIU rt,rs,imm | 09h | rt = rs + (-8000h to +7FFFh) | SLTIU sign-extends its immediate and then compares without sign. Its immediate therefore covers 0 to 7FFFh and FFFF8000h to FFFFFFFFh.[^alu] Table: Comparison instructions | Instruction | Opcode | Operation | | --- | --- | --- | | SLT rd,rs,rt | 00h/2Ah | rd = 1 if rs < rt (signed), else 0 | | SLTU rd,rs,rt | 00h/2Bh | rd = 1 if rs < rt (unsigned), else 0 | | SLTI rt,rs,imm | 0Ah | rt = 1 if rs < sign-extended imm (signed), else 0 | | SLTIU rt,rs,imm | 0Bh | rt = 1 if rs < sign-extended imm (unsigned), else 0 | The logical instructions with an immediate zero-extend it:[^alu] Table: Logical instructions | Instruction | Opcode | Operation | | --- | --- | --- | | AND rd,rs,rt | 00h/24h | rd = rs AND rt | | OR rd,rs,rt | 00h/25h | rd = rs OR rt | | XOR rd,rs,rt | 00h/26h | rd = rs XOR rt | | NOR rd,rs,rt | 00h/27h | rd = FFFFFFFFh XOR (rs OR rt) | | ANDI rt,rs,imm | 0Ch | rt = rs AND (0000h to FFFFh) | | ORI rt,rs,imm | 0Dh | rt = rs OR (0000h to FFFFh) | | XORI rt,rs,imm | 0Eh | rt = rs XOR (0000h to FFFFh) | Unlike most instructions, the shifts take rt as their second operand rather than their third. A variable shift uses only the low 5 bits of rs.[^alu] Table: Shift instructions | Instruction | Opcode | Operation | | --- | --- | --- | | SLL rd,rt,imm | 00h/00h | rd = rt SHL (00h to 1Fh) | | SRL rd,rt,imm | 00h/02h | rd = rt SHR (00h to 1Fh) | | SRA rd,rt,imm | 00h/03h | rd = rt SAR (00h to 1Fh) | | SLLV rd,rt,rs | 00h/04h | rd = rt SHL (rs AND 1Fh) | | SRLV rd,rt,rs | 00h/06h | rd = rt SHR (rs AND 1Fh) | | SRAV rd,rt,rs | 00h/07h | rd = rt SAR (rs AND 1Fh) | | LUI rt,imm | 0Fh | rt = (0000h to FFFFh) SHL 16 | ## Multiply and Divide The multiply and divide instructions write their result to HI and LO. Starting the operation takes one cycle, and the CPU continues with the next instructions while the unit works. A read of HI or LO before the operation is complete halts the CPU until the result is ready.[^mul] Table: Multiply and divide instructions | Instruction | Opcode | Operation | | --- | --- | --- | | MULT rs,rt | 00h/18h | HI:LO = rs x rt (signed) | | MULTU rs,rt | 00h/19h | HI:LO = rs x rt (unsigned) | | DIV rs,rt | 00h/1Ah | LO = rs / rt, HI = rs mod rt (signed) | | DIVU rs,rt | 00h/1Bh | LO = rs / rt, HI = rs mod rt (unsigned) | | MFHI rd | 00h/10h | rd = HI | | MTHI rs | 00h/11h | HI = rs | | MFLO rd | 00h/12h | rd = LO | | MTLO rs | 00h/13h | LO = rs | A multiply takes longer as rs grows. A small value in rs times a large value in rt is therefore faster than the reverse. A divide always takes 36 cycles:[^mul] Table: Multiply and divide execution time | Instruction | Cycles | rs | | --- | --- | --- | | MULTU | 6 | 00000000h to 000007FFh | | MULTU | 9 | 00000800h to 000FFFFFh | | MULTU | 13 | 00100000h to FFFFFFFFh | | MULT | 6 | 00000000h to 000007FFh, or FFFFF800h to FFFFFFFFh | | MULT | 9 | 00000800h to 000FFFFFh, or FFF00000h to FFFFF801h | | MULT | 13 | 00100000h to 7FFFFFFFh, or 80000000h to FFF00001h | | DIV, DIVU | 36 | Any | For example, up to six cached ALU instructions, or one read from main RAM with its 6-cycle access time, fit between MULTU 123h,12345678h and MFLO without slowing the program.[^mul] A divide never raises an exception. A division by zero or the overflow of -80000000h / -1 gives the following results:[^mul] Table: Divide error results | Instruction | rs | rt | HI (remainder) | LO (result) | | --- | --- | --- | --- | --- | | DIVU | 0 to FFFFFFFFh | 0 | rs | FFFFFFFFh | | DIV | 0 to +7FFFFFFFh | 0 | rs | -1 | | DIV | -80000000h to -1 | 0 | rs | +1 | | DIV | -80000000h | -1 | 0 | -80000000h | psx-spx also notes an unconfirmed rule that HI and LO should not be touched in the 2 cycles after they are accessed.[^mul] ## Jump and Branch Instructions The instruction after a jump or branch, the branch delay slot, is always executed. A branch adds its sign-extended offset times 4 to the address of the delay slot, while J and JAL replace the low 28 bits of the program counter.[^jmp] Table: Jump and branch instructions | Instruction | Opcode | Operation | | --- | --- | --- | | J dest | 02h | pc = (pc AND F0000000h) + imm26 x 4 | | JAL dest | 03h | pc = (pc AND F0000000h) + imm26 x 4, ra = $+8 | | JR rs | 00h/08h | pc = rs | | JALR rd,rs | 00h/09h | pc = rs, rd = $+8 | | BEQ rs,rt,dest | 04h | if rs = rt then pc = $+4 + imm16 x 4 | | BNE rs,rt,dest | 05h | if rs <> rt then pc = $+4 + imm16 x 4 | | BLEZ rs,dest | 06h | if rs <= 0 then pc = $+4 + imm16 x 4 | | BGTZ rs,dest | 07h | if rs > 0 then pc = $+4 + imm16 x 4 | | BLTZ rs,dest | 01h, rt=00h | if rs < 0 then pc = $+4 + imm16 x 4 | | BGEZ rs,dest | 01h, rt=01h | if rs >= 0 then pc = $+4 + imm16 x 4 | | BLTZAL rs,dest | 01h, rt=10h | if rs < 0 then pc = $+4 + imm16 x 4; ra = $+8 | | BGEZAL rs,dest | 01h, rt=11h | if rs >= 0 then pc = $+4 + imm16 x 4; ra = $+8 | BLTZAL and BGEZAL write RA whether or not the branch is taken. When rs is RA, they compare the value RA held before the link.[^jmp] JR and JALR may jump to an unaligned address. The address error (AdEL) is then raised on the next instruction fetch.[^jmp] ### JALR Cautions Assemblers differ in their JALR syntax: the IDT79R3041 manual writes "jalr rs,rd" and the MIPS32 manual "jalr rd,rs". JALR must not use one register for both operands (for example "jalr r31,r31").[^jalr] The reason is the delay slot. If an interrupt occurs between JALR and its delay slot, BD is set and EPC points back to the JALR. The JALR therefore executes a second time, and by then its target register has been overwritten with the return address.[^jalr] ## Exception Instructions SYSCALL raises a system call exception and BREAK raises a breakpoint exception. Unlike jumps and branches they take effect at once, and the next instruction is not executed. The CPU ignores the 20-bit field in bits 6 to 25. The exception handler may read the field from the instruction word.[^exc] Table: Exception instructions | Instruction | Opcode | Operation | | --- | --- | --- | | SYSCALL imm20 | 00h/0Ch | System call exception (excode 08h) | | BREAK imm20 | 00h/0Dh | Breakpoint exception (excode 09h) | The [COP0](/hardware/cpu/cop0/) page describes exception handling. ## Coprocessor Instructions The coprocessor instructions move data to and from the data and control registers of a coprocessor and send it commands. On the PlayStation they reach COP0 and COP2 (the GTE), since COP1 and COP3 do not exist.[^cop][^iomap] Table: Coprocessor instructions | Instruction | Encoding | Operation | | --- | --- | --- | | MFCn rt,rd | 0100nn 00000 | rt = data register rd | | CFCn rt,rd | 0100nn 00010 | rt = control register rd | | MTCn rt,rd | 0100nn 00100 | data register rd = rt | | CTCn rt,rd | 0100nn 00110 | control register rd = rt | | BCnF dest | 0100nn 01000, rt=00000 | Branch if the coprocessor flag is false | | BCnT dest | 0100nn 01000, rt=00001 | Branch if the coprocessor flag is true | | COPn imm25 | 0100nn 1 | Execute coprocessor command imm25 | | LWCn rt,imm(rs) | 1100nn | Coprocessor data register rt = word at [rs+imm] | | SWCn rt,imm(rs) | 1110nn | word at [rs+imm] = coprocessor data register rt | | RFE | 010000 1 0000, bits 0 to 5 = 010000 | Return from exception (COP0) | COP0 accepts only the RFE command. The TLB commands TLBR, TLBWI, TLBWR and TLBP (COP0 commands 01h, 02h, 06h and 08h) cause a reserved instruction exception.[^cop][^misc] ### Coprocessor Load and Store Delays A read from a coprocessor register has the same 1-instruction load delay as a read from memory. Some documents give the delay as two instructions, but the PlayStation completes both COP0 and COP2 reads after one.[^cop] Writes to COP0 take effect at once, apart from the COP2 enable bit in SR bit 30, which takes about 2 cycles. Writes to COP2 registers take 2 to 3 cycles, the 3-cycle case being IRGB because it also changes IR1 to IR3. These delays are counted in clock cycles. A 3-cycle delay therefore needs 3 cached instructions or one uncached instruction.[^cop] ## Pseudo Instructions Assemblers provide pseudo instructions that expand to one or more real instructions. Some of them use R1 (AT) as a scratch register, so code that uses R1 must be sure its assembler does not change it behind its back. Sony's register table also reserves AT for the assembler, and C code may not use it.[^ps][^hw26] Table: Pseudo instructions of the native assemblers | Pseudo instruction | Expansion | | --- | --- | | nop | sll r0,r0,0 | | move rd,rs | addu rd,rs,r0 | | la rx,imm32 | lui rx / addiu rx | | li rx,imm32 | lui rx / ori rx | | li rx,imm16 | ori, range 0 to FFFFh | | li rx,-imm15 | addiu, range -1 to -8000h | | li rx,imm16 x 10000h | lui | | lw rx,imm32 | lui rx / lw rx,rx | | sw rx,imm32 | lui r1 / sw rx,r1 (changes r1) | | alu rx,op | alu rx,rx,op | | jalr rx | jalr ra,rx | | subi(u) rt,rs,imm | addi(u) rt,rs,-imm | | beqz rx,dest | beq rx,r0,dest | | bnez rx,dest | bne rx,r0,dest | | b dest | beq r0,r0,dest | | bal dest | bgezal r0,dest | ## Stack Convention The CPU has no stack instructions. By convention R29 (SP) is the stack pointer, and the compiler stores the head address of the function frame in FP.[^hw27][^sp] The BIOS uses a full decrementing stack with wasted argument space. SP is decremented to allocate, and it points to the lowest allocated word. A caller that passes N arguments reserves N words at SP+0 upward, and the called function may use and destroy them. For example, a function that saves RA, R16 and R17 subtracts 20h from SP and stores RA at SP+14h, R16 at SP+18h and R17 at SP+1Ch. The 20h bytes are then used as follows:[^sp] Table: Stack frame for saving three registers | Offset | Use | | --- | --- | | SP+00h to 0Fh | Wasted stack (may be used by called functions) | | SP+10h to 13h | 8-byte alignment padding | | SP+14h to 1Fh | Saved registers | ## References [^hw25]: Sony Computer Entertainment Inc. PlayStation Hardware. August 1998. Page 2-5. [^hw26]: Sony Computer Entertainment Inc. PlayStation Hardware. August 1998. Page 2-6. [^hw27]: Sony Computer Entertainment Inc. PlayStation Hardware. August 1998. Page 2-7. [^reg]: psx-spx. CPU Specifications, CPU Registers. https://psx-spx.consoledev.net/cpuspecifications/ [^sp]: psx-spx. CPU Specifications, R29 (SP) - Full Decrementing Wasted Stack Pointer. https://psx-spx.consoledev.net/cpuspecifications/ [^enc]: psx-spx. CPU Specifications, CPU Opcode Encoding. https://psx-spx.consoledev.net/cpuspecifications/ [^ill]: psx-spx. CPU Specifications, Illegal Opcodes. https://psx-spx.consoledev.net/cpuspecifications/ [^ld]: psx-spx. CPU Specifications, CPU Load/Store Opcodes. https://psx-spx.consoledev.net/cpuspecifications/ [^ldd]: psx-spx. CPU Specifications, Caution - Load Delay. https://psx-spx.consoledev.net/cpuspecifications/ [^st]: psx-spx. CPU Specifications, Store instructions and Caution - 8/16-bit writes to certain IO registers. https://psx-spx.consoledev.net/cpuspecifications/ [^ul]: psx-spx. CPU Specifications, Unaligned Load/Store. https://psx-spx.consoledev.net/cpuspecifications/ [^alu]: psx-spx. CPU Specifications, CPU ALU Opcodes. https://psx-spx.consoledev.net/cpuspecifications/ [^mul]: psx-spx. CPU Specifications, Multiply/divide. https://psx-spx.consoledev.net/cpuspecifications/ [^jmp]: psx-spx. CPU Specifications, CPU Jump Opcodes. https://psx-spx.consoledev.net/cpuspecifications/ [^jalr]: psx-spx. CPU Specifications, JALR cautions. https://psx-spx.consoledev.net/cpuspecifications/ [^exc]: psx-spx. CPU Specifications, exception opcodes. https://psx-spx.consoledev.net/cpuspecifications/ [^cop]: psx-spx. CPU Specifications, CPU Coprocessor Opcodes. https://psx-spx.consoledev.net/cpuspecifications/ [^misc]: psx-spx. CPU Specifications, COP0 - Misc. https://psx-spx.consoledev.net/cpuspecifications/ [^iomap]: psx-spx. I/O Map, Coprocessor Registers. https://psx-spx.consoledev.net/iomap/ [^ps]: psx-spx. CPU Specifications, CPU Pseudo Opcodes; Unpredictable Things, CPU Register Problems. https://psx-spx.consoledev.net/cpuspecifications/