Xtensa instructions (ESP32-S3, ESP32)
Instruction reference: i386 · x64 · ARM32 (Thumb-2) · ARM64 (AArch64) · RISC-V (RV32) · Xtensa
224 instructions for .arch xtensa: name, operands, the bit encoding (24 bits, 16 bits for the short instructions) and a short description with the operation in pseudo code. Included is what the cores of the ESP32-S3 (Xtensa LX7) run for ordinary programs: the core set, the 16-bit instructions (code density), the windowed registers, the zero-overhead loops, multiplication and division, minimum and maximum, the boolean registers, the floating point instructions (single precision), the special and user registers and the instructions for exceptions, interrupts and the debugger. The ESP32 (Xtensa LX6) has the same encodings; only salt and saltu are missing there (they are marked). Not included are the vector instructions of the ESP32-S3 (ee.*) and MAC16.
How to read the table
An Xtensa instruction has 24 bits (three bytes) or 16 bits; the lowest 4 bits (op0) tell which: 0 to 7 are 24 bits, 8 to 13 the 16-bit instructions with .n in their name. In memory the lowest byte comes first. The encoding shows the bits from left (highest bit number) to right: fixed bits as digits, the names are fields; hover a field to see its range of bits. The registers are in three fields of 4 bits, r (bits 15 to 12), s (11 to 8) and t (7 to 4): ar, as, at are the address registers a0 to a15, fr … the float registers f0 to f15, br … the boolean registers b0 to b15. Below are the value of the fixed bits and the mask in hex: an instruction belongs to this form if (word & mask) == value.
Register windows. The 16 registers a0 to a15 are a window onto 64 registers. call8 keeps the return address and the wish to turn the window by 8; the first instruction of the function, entry, turns it and makes the stack frame. Then a10 to a15 of the caller are a2 to a7 of the function (the arguments), a1 is the stack pointer, a0 the return address, whose two top bits tell the turn. retw turns back. Without windows, call0 and ret work.
Constants. movi loads 12 bits with a sign, movi.n -32 to 95. Larger constants and addresses come with l32r from a literal pool: a word with dd before the instruction (up to 256 KB before it, its address a multiple of 4). The branches with a constant (beqi, bltui …) take only the 16 values of the table b4const or b4constu. As GNU as does, Asmonite writes the 16-bit form when the operands fit (add becomes add.n, l32i with a small offset l32i.n …).
Every encoding in this table is checked against the decoder of Asmonite (with random values in the fields it gives this instruction again), and the decoder against GNU objdump of Espressif (more than a million instruction words, without a difference). The special registers (rsr.ps a3, wsr.epc6 a2 …) are those of the ESP32-S3; the ESP32 has nearly the same. The pseudo code is written in English like source code; <s and <u compare signed and unsigned, mem32[x] is the word in memory at address x.
| Instruction | Operands | Encoding | Description |
|---|---|---|---|
| abs | ar, at | 01100000 ar 0001 at 00000x600100 / mask 0xFF0F0F | Absolute valuear = |at|Arithmetic |
| abs.s | fr, fs | 11111010 fr fs 000100000xFA0010 / mask 0xFF00FF | Floating point absolute valuefr = |fs|Floating point (scalar) |
| add | ar, as, at | 10000000 ar as at 00000x800000 / mask 0xFF000F | Addition of two registersar = as + atArithmetic |
| add.n | ar, as, at | ar as at 10100x000A / mask 0x000F | Addition of two registers (16 bit)ar = as + at16-bit instructions (code density) |
| add.s | fr, fs, ft | 00001010 fr fs ft 00000x0A0000 / mask 0xFF000F | Floating point additionfr = fs + ftFloating point (scalar) |
| addexp.s | fr, fs | 11111010 fr fs 111000000xFA00E0 / mask 0xFF00FF | Add the exponents (step of division and square root)Floating point (scalar) |
| addexpm.s | fr, fs | 11111010 fr fs 111100000xFA00F0 / mask 0xFF00FF | Add the exponents, from the mantissa (step of division and square root)Floating point (scalar) |
| addi | at, as, imm8 | imm8 1100 as at 00100x00C002 / mask 0x00F00F | Addition of a constant (8 bit, signed); with -1 or 1..15 the assembler writes addi.n, with 0 mov.nat = as + sext(imm8)Arithmetic |
| addi.n | ar, as, imm | ar as imm4 10110x000B / mask 0x000F | Addition of a constant -1 or 1..15 (16 bit; the field 0 is -1)ar = as + imm16-bit instructions (code density) |
| addmi | at, as, imm | imm8 1101 as at 00100x00D002 / mask 0x00F00F | Addition of a constant times 256 (-32768..32512)at = as + (sext(imm8) << 8)Arithmetic |
| addx2 | ar, as, at | 10010000 ar as at 00000x900000 / mask 0xFF000F | Addition, the first register times 2 (the address of a halfword)ar = (as << 1) + atArithmetic |
| addx4 | ar, as, at | 10100000 ar as at 00000xA00000 / mask 0xFF000F | Addition, the first register times 4 (the address of a word)ar = (as << 2) + atArithmetic |
| addx8 | ar, as, at | 10110000 ar as at 00000xB00000 / mask 0xFF000F | Addition, the first register times 8ar = (as << 3) + atArithmetic |
| all4 | bt, bs | 000000001001 bs bt 00000x009000 / mask 0xFFF00F | 1 if all 4 boolean registers are 1bt = bs & bs+1 & bs+2 & bs+3Boolean registers |
| all8 | bt, bs | 000000001011 bs bt 00000x00B000 / mask 0xFFF00F | 1 if all 8 boolean registers are 1bt = bs & ... & bs+7Boolean registers |
| and | ar, as, at | 00010000 ar as at 00000x100000 / mask 0xFF000F | ANDar = as & atLogic and bits |
| andb | br, bs, bt | 00000010 br bs bt 00000x020000 / mask 0xFF000F | AND of boolean registersbr = bs & btBoolean registers |
| andbc | br, bs, bt | 00010010 br bs bt 00000x120000 / mask 0xFF000F | AND with the complement of the secondbr = bs & ~btBoolean registers |
| any4 | bt, bs | 000000001000 bs bt 00000x008000 / mask 0xFFF00F | 1 if any of 4 boolean registers is 1 (the group from b0, b4, b8, b12)bt = bs | bs+1 | bs+2 | bs+3Boolean registers |
| any8 | bt, bs | 000000001010 bs bt 00000x00A000 / mask 0xFFF00F | 1 if any of 8 boolean registers is 1 (from b0 or b8)bt = bs | ... | bs+7Boolean registers |
| ball | as, at, label | imm8 0100 as at 01110x004007 / mask 0x00F00F | Branch if all bits of the mask are setif (~as & at) == 0: pc = labelBranches and calls |
| bany | as, at, label | imm8 1000 as at 01110x008007 / mask 0x00F00F | Branch if any bit of the mask is setif (as & at) != 0: pc = labelBranches and calls |
| bbc | as, at, label | imm8 0101 as at 01110x005007 / mask 0x00F00F | Branch if the bit (its number in a register) is 0if as[at & 31] == 0: pc = labelBranches and calls |
| bbci | as, bit, label | imm8 011 bit[4] as bit[3-0] 01110x006007 / mask 0x00E00F | Branch if the bit (a constant 0..31) is 0if as[bit] == 0: pc = labelBranches and calls |
| bbs | as, at, label | imm8 1101 as at 01110x00D007 / mask 0x00F00F | Branch if the bit (its number in a register) is 1if as[at & 31] == 1: pc = labelBranches and calls |
| bbsi | as, bit, label | imm8 111 bit[4] as bit[3-0] 01110x00E007 / mask 0x00E00F | Branch if the bit (a constant 0..31) is 1if as[bit] == 1: pc = labelBranches and calls |
| beq | as, at, label | imm8 0001 as at 01110x001007 / mask 0x00F00F | Branch if equal (-128..127 bytes away)if as == at: pc = labelBranches and calls |
| beqi | as, b4const, label | imm8 r as 001001100x000026 / mask 0x0000FF | Branch if equal to a constant (-1, 1..8, 10, 12, 16, 32, 64, 128, 256)if as == b4const: pc = labelBranches and calls |
| beqz | as, label | imm12 as 000101100x000016 / mask 0x0000FF | Branch if zero (up to 2 KB away)if as == 0: pc = labelBranches and calls |
| beqz.n | as, label | imm[3-0] as 10 imm[5-4] 11000x008C / mask 0x00CF | Branch if zero, forward only, 0..63 bytes (16 bit)if as == 0: pc = label16-bit instructions (code density) |
| bf | bs, label | imm8 0000 bs 011101100x000076 / mask 0x00F0FF | Branch if the boolean register is 0if not bs: pc = labelBoolean registers |
| bge | as, at, label | imm8 1010 as at 01110x00A007 / mask 0x00F00F | Branch if greater or equal (signed)if as >=s at: pc = labelBranches and calls |
| bgei | as, b4const, label | imm8 r as 111001100x0000E6 / mask 0x0000FF | Branch if greater than or equal to a constant (signed)if as >=s b4const: pc = labelBranches and calls |
| bgeu | as, at, label | imm8 1011 as at 01110x00B007 / mask 0x00F00F | Branch if greater or equal (unsigned)if as >=u at: pc = labelBranches and calls |
| bgeui | as, b4constu, label | imm8 r as 111101100x0000F6 / mask 0x0000FF | Branch if greater than or equal to an unsigned constantif as >=u b4constu: pc = labelBranches and calls |
| bgez | as, label | imm12 as 110101100x0000D6 / mask 0x0000FF | Branch if not negativeif as >=s 0: pc = labelBranches and calls |
| blt | as, at, label | imm8 0010 as at 01110x002007 / mask 0x00F00F | Branch if less (signed)if as <s at: pc = labelBranches and calls |
| blti | as, b4const, label | imm8 r as 101001100x0000A6 / mask 0x0000FF | Branch if less than a constant (signed)if as <s b4const: pc = labelBranches and calls |
| bltu | as, at, label | imm8 0011 as at 01110x003007 / mask 0x00F00F | Branch if less (unsigned)if as <u at: pc = labelBranches and calls |
| bltui | as, b4constu, label | imm8 r as 101101100x0000B6 / mask 0x0000FF | Branch if less than an unsigned constant (2..8, 10, 12, 16, 32, 64, 128, 256, 32768, 65536)if as <u b4constu: pc = labelBranches and calls |
| bltz | as, label | imm12 as 100101100x000096 / mask 0x0000FF | Branch if negativeif as <s 0: pc = labelBranches and calls |
| bnall | as, at, label | imm8 1100 as at 01110x00C007 / mask 0x00F00F | Branch if not all bits of the mask are setif (~as & at) != 0: pc = labelBranches and calls |
| bne | as, at, label | imm8 1001 as at 01110x009007 / mask 0x00F00F | Branch if not equalif as != at: pc = labelBranches and calls |
| bnei | as, b4const, label | imm8 r as 011001100x000066 / mask 0x0000FF | Branch if not equal to a constantif as != b4const: pc = labelBranches and calls |
| bnez | as, label | imm12 as 010101100x000056 / mask 0x0000FF | Branch if not zeroif as != 0: pc = labelBranches and calls |
| bnez.n | as, label | imm[3-0] as 11 imm[5-4] 11000x00CC / mask 0x00CF | Branch if not zero, forward only, 0..63 bytes (16 bit)if as != 0: pc = label16-bit instructions (code density) |
| bnone | as, at, label | imm8 0000 as at 01110x000007 / mask 0x00F00F | Branch if no bit of the mask is setif (as & at) == 0: pc = labelBranches and calls |
| break | imm4, imm4 | 000000000100 imm4 00000x004000 / mask 0xFFF00F | Breakpoint: stops in the debugger (debug exception), the numbers tell the handler the reasonSystem and privileged |
| break.n | imm4 | 1111 imm4 001011010xF02D / mask 0xF0FF | Breakpoint (16 bit); Asmonite puts it for breakpoints on 16-bit instructions16-bit instructions (code density) |
| bt | bs, label | imm8 0001 bs 011101100x001076 / mask 0x00F0FF | Branch if the boolean register is 1if bs: pc = labelBoolean registers |
| call0 | label | offset 0001010x000005 / mask 0x00003F | Call without register window: the return address in a0 (the target a multiple of 4)a0 = pc + 3; pc = labelBranches and calls |
| call12 | label | offset 1101010x000035 / mask 0x00003F | Call that turns the register window by 12a12 = return address (top bits: 3); PS.CALLINC = 3; pc = labelWindowed registers (call4/8/12, entry, retw) |
| call4 | label | offset 0101010x000015 / mask 0x00003F | Call that turns the register window by 4 (when the function runs entry): a4..a7 of the caller are its a0..a3a4 = return address (top bits: 1); PS.CALLINC = 1; pc = labelWindowed registers (call4/8/12, entry, retw) |
| call8 | label | offset 1001010x000025 / mask 0x00003F | Call that turns the register window by 8: a10..a15 of the caller are the arguments a2..a7 of the functiona8 = return address (top bits: 2); PS.CALLINC = 2; pc = labelWindowed registers (call4/8/12, entry, retw) |
| callx0 | as | 000000000000 as 110000000x0000C0 / mask 0xFFF0FF | Call of the address in the register, without register windowa0 = pc + 3; pc = asBranches and calls |
| callx12 | as | 000000000000 as 111100000x0000F0 / mask 0xFFF0FF | call12 to the address in the registera12 = return address; PS.CALLINC = 3; pc = asWindowed registers (call4/8/12, entry, retw) |
| callx4 | as | 000000000000 as 110100000x0000D0 / mask 0xFFF0FF | call4 to the address in the registera4 = return address; PS.CALLINC = 1; pc = asWindowed registers (call4/8/12, entry, retw) |
| callx8 | as | 000000000000 as 111000000x0000E0 / mask 0xFFF0FF | call8 to the address in the registera8 = return address; PS.CALLINC = 2; pc = asWindowed registers (call4/8/12, entry, retw) |
| ceil.s | ar, fs, imm4 | 10111010 ar fs imm4 00000xBA0000 / mask 0xFF000F | Convert to an integer, rounded upar = ceil(fs * 2^imm4)Floating point (scalar) |
| clamps | ar, as, bits | 00110011 ar as b-7 00000x330000 / mask 0xFF000F | Clamp to the range of a signed number of b+1 bits (7..22)ar = min(max(as, -2^b), 2^b - 1)Arithmetic |
| const.s | fr, imm4 | 11111010 fr imm4 001100000xFA0030 / mask 0xFF00FF | Load a float constant (0, 1, 2, 0.5 ... by imm4)fr = constant[imm4]Floating point (scalar) |
| div0.s | fr, fs | 11111010 fr fs 011100000xFA0070 / mask 0xFF00FF | Start value for a divisionFloating point (scalar) |
| divn.s | fr, fs, ft | 01111010 fr fs ft 00000x7A0000 / mask 0xFF000F | Last step of a division (after div0.s, nexp01.s ...)Floating point (scalar) |
| dsync | 0000000000100000001100000x002030 / mask 0xFFFFFF | Wait until written special registers apply to memory accessesBarriers and others | |
| entry | as, framesize | imm12 as 001101100x000036 / mask 0x0000FF | The first instruction of a function with register window: turns the window by PS.CALLINC * 4 and makes its stack frame (framesize 0..32760, a multiple of 8; the address a multiple of 4)WINDOWBASE += PS.CALLINC; as = as(old window) - framesizeWindowed registers (call4/8/12, entry, retw) |
| esync | 0000000000100000001000000x002020 / mask 0xFFFFFF | Wait until written special registers apply to the executionBarriers and others | |
| excw | 0000000000100000100000000x002080 / mask 0xFFFFFF | Wait until the exceptions of the instructions before are raisedBarriers and others | |
| extui | ar, at, shift, width | w-1 010 sh[4] ar sh[3-0] at 00000x040000 / mask 0x0E000F | Extract an unsigned bit field: width bits from bit shift (shift + width up to 32)ar = (at >> shift) & ((1 << width) - 1)Shift and rotate |
| extw | 0000000000100000110100000x0020D0 / mask 0xFFFFFF | Wait until all earlier accesses have reached the outsideBarriers and others | |
| float.s | fr, as, imm4 | 11001010 fr as imm4 00000xCA0000 / mask 0xFF000F | Convert a signed integer to float, divided by 2^imm4fr = float(as) / 2^imm4Floating point (scalar) |
| floor.s | ar, fs, imm4 | 10101010 ar fs imm4 00000xAA0000 / mask 0xFF000F | Convert to an integer, rounded downar = floor(fs * 2^imm4)Floating point (scalar) |
| idtlb | as | 010100001100 as 000000000x50C000 / mask 0xFFF0FF | Invalidate an entry of the data TLBinvalidate DTLB[as]System and privileged |
| iitlb | as | 010100000100 as 000000000x504000 / mask 0xFFF0FF | Invalidate an entry of the instruction TLBinvalidate ITLB[as]System and privileged |
| ill | 0000000000000000000000000x000000 / mask 0xFFFFFF | Illegal instruction: raises an exceptionSystem and privileged | |
| ill.n | 11110000011011010xF06D / mask 0xFFFF | Illegal instruction (16 bit)16-bit instructions (code density) | |
| isync | 0000000000100000000000000x002000 / mask 0xFFFFFF | Fetch the instructions again (after changed code or special registers)Barriers and others | |
| j | label | offset 0001100x000006 / mask 0x00003F | Jump (up to 128 KB away)pc = labelBranches and calls |
| jx | as | 000000000000 as 101000000x0000A0 / mask 0xFFF0FF | Jump to the address in the registerpc = asBranches and calls |
| l16si | at, as, offset | imm8 1001 as at 00100x009002 / mask 0x00F00F | Load of a halfword, signedat = sext(mem16[as + (imm8 << 1)])Loads and stores |
| l16ui | at, as, offset | imm8 0001 as at 00100x001002 / mask 0x00F00F | Load of a halfword, unsigned (offset 0..510, even)at = zext(mem16[as + (imm8 << 1)])Loads and stores |
| l32ai | at, as, offset | imm8 1011 as at 00100x00B002 / mask 0x00F00F | Load of a word with acquire ordering (later accesses only after it)at = mem32[as + (imm8 << 2)]; acquireLoads and stores |
| l32e | at, as, offset | 00001001 imm4 as at 00000x090000 / mask 0xFF000F | Load for the handlers of the window exceptions (offset -64..-4)at = mem32[as + ((imm4 << 2) - 64)]Windowed registers (call4/8/12, entry, retw) |
| l32i | at, as, offset | imm8 0010 as at 00100x002002 / mask 0x00F00F | Load of a word (offset 0..1020, a multiple of 4); with a small offset the assembler writes l32i.nat = mem32[as + (imm8 << 2)]Loads and stores |
| l32i.n | at, as, offset | imm4 as at 10000x0008 / mask 0x000F | Load of a word, offset 0..60 (16 bit)at = mem32[as + (imm4 << 2)]16-bit instructions (code density) |
| l32r | at, label | imm16 at 00010x000001 / mask 0x00000F | Load of a word from the literal pool: a constant up to 256 KB before the instruction (its address a multiple of 4); the way of 32-bit constants into a registerat = mem32[((pc + 3) & ~3) + (0xFFFF0000 | imm16) * 4]Loads and stores |
| l8ui | at, as, imm8 | imm8 0000 as at 00100x000002 / mask 0x00F00F | Load of a byte, unsignedat = zext(mem8[as + imm8])Loads and stores |
| lddr32.p | as | 000000000111 as 111000000x0070E0 / mask 0xFFF0FF | Debug mode: load a word into DDR and add 4 to the address (fast memory reads of the debugger)DDR = mem32[as]; as += 4System and privileged |
| loop | as, label | imm8 1000 as 011101100x008076 / mask 0x00F0FF | Zero-overhead loop: the part up to label runs as times (as = 0: 2^32 times); LBEG, LEND, LCOUNTLCOUNT = as - 1; LBEG = pc + 3; LEND = labelZero-overhead loops (loop) |
| loopgtz | as, label | imm8 1010 as 011101100x00A076 / mask 0x00F0FF | Loop as loop, skipped if as is not greater than 0if as <=s 0: pc = label else loopZero-overhead loops (loop) |
| loopnez | as, label | imm8 1001 as 011101100x009076 / mask 0x00F0FF | Loop as loop, skipped if as = 0if as == 0: pc = label else loopZero-overhead loops (loop) |
| lsi | ft, as, offset | imm8 0000 as ft 00110x000003 / mask 0x00F00F | Load of a floatft = mem32[as + (imm8 << 2)]Floating point (scalar) |
| lsip | ft, as, offset | imm8 1000 as ft 00110x008003 / mask 0x00F00F | Load of a float, then add to the addressft = mem32[as]; as += imm8 << 2Floating point (scalar) |
| lsx | fr, as, at | 00001000 fr as at 00000x080000 / mask 0xFF000F | Load of a float, the address from two registersfr = mem32[as + at]Floating point (scalar) |
| lsxp | fr, as, at | 00011000 fr as at 00000x180000 / mask 0xFF000F | Load of a float, then add to the addressfr = mem32[as]; as += atFloating point (scalar) |
| madd.s | fr, fs, ft | 01001010 fr fs ft 00000x4A0000 / mask 0xFF000F | Multiply and addfr = fr + fs * ftFloating point (scalar) |
| maddn.s | fr, fs, ft | 01101010 fr fs ft 00000x6A0000 / mask 0xFF000F | Multiply and add, rounded to nearest (a step of division and square root)fr = round(fr + fs * ft)Floating point (scalar) |
| max | ar, as, at | 01010011 ar as at 00000x530000 / mask 0xFF000F | Maximum, signedar = (as >s at) ? as : atCompare and exchange |
| maxu | ar, as, at | 01110011 ar as at 00000x730000 / mask 0xFF000F | Maximum, unsignedar = (as >u at) ? as : atCompare and exchange |
| memw | 0000000000100000110000000x0020C0 / mask 0xFFFFFF | Memory barrier: earlier accesses before later ones (for peripheral registers)Barriers and others | |
| min | ar, as, at | 01000011 ar as at 00000x430000 / mask 0xFF000F | Minimum, signedar = (as <s at) ? as : atCompare and exchange |
| minu | ar, as, at | 01100011 ar as at 00000x630000 / mask 0xFF000F | Minimum, unsignedar = (as <u at) ? as : atCompare and exchange |
| mkdadj.s | fr, fs | 11111010 fr fs 110100000xFA00D0 / mask 0xFF00FF | Make the exponent adjustment for a divisionFloating point (scalar) |
| mksadj.s | fr, fs | 11111010 fr fs 110000000xFA00C0 / mask 0xFF00FF | Make the exponent adjustment for a square rootFloating point (scalar) |
| mov.n | at, as | 0000 as at 11010x000D / mask 0xF00F | Copy of a register (16 bit)at = as16-bit instructions (code density) |
| mov.s | fr, fs | 11111010 fr fs 000000000xFA0000 / mask 0xFF00FF | Copy a float registerfr = fsFloating point (scalar) |
| moveqz | ar, as, at | 10000011 ar as at 00000x830000 / mask 0xFF000F | Copy if the third register is 0if at == 0: ar = asConditional select |
| moveqz.s | fr, fs, at | 10001011 fr fs at 00000x8B0000 / mask 0xFF000F | Copy a float register if at is 0if at == 0: fr = fsFloating point (scalar) |
| movf | ar, as, bt | 11000011 ar as bt 00000xC30000 / mask 0xFF000F | Copy if the boolean register is 0if not bt: ar = asConditional select |
| movf.s | fr, fs, bt | 11001011 fr fs bt 00000xCB0000 / mask 0xFF000F | Copy a float register if the boolean register is 0if not bt: fr = fsFloating point (scalar) |
| movgez | ar, as, at | 10110011 ar as at 00000xB30000 / mask 0xFF000F | Copy if the third register is not negativeif at >=s 0: ar = asConditional select |
| movgez.s | fr, fs, at | 10111011 fr fs at 00000xBB0000 / mask 0xFF000F | Copy a float register if at is not negativeif at >=s 0: fr = fsFloating point (scalar) |
| movi | at, imm12 | imm[7-0] 1010 imm[11-8] at 00100x00A002 / mask 0x00F00F | Load of a constant (12 bit, signed, -2048..2047); larger ones come with l32rat = sext(imm12)Constants and addresses |
| movi.n | as, imm7 | imm[3-0] as 0 imm[6-4] 11000x000C / mask 0x008F | Load of a constant -32..95 (16 bit)as = imm716-bit instructions (code density) |
| movltz | ar, as, at | 10100011 ar as at 00000xA30000 / mask 0xFF000F | Copy if the third register is negativeif at <s 0: ar = asConditional select |
| movltz.s | fr, fs, at | 10101011 fr fs at 00000xAB0000 / mask 0xFF000F | Copy a float register if at is negativeif at <s 0: fr = fsFloating point (scalar) |
| movnez | ar, as, at | 10010011 ar as at 00000x930000 / mask 0xFF000F | Copy if the third register is not 0if at != 0: ar = asConditional select |
| movnez.s | fr, fs, at | 10011011 fr fs at 00000x9B0000 / mask 0xFF000F | Copy a float register if at is not 0if at != 0: fr = fsFloating point (scalar) |
| movsp | at, as | 000000000001 as at 00000x001000 / mask 0xFFF00F | Set the stack pointer so that the register windows of the callers are saved firstat = as (after spilling the windows when needed)Windowed registers (call4/8/12, entry, retw) |
| movt | ar, as, bt | 11010011 ar as bt 00000xD30000 / mask 0xFF000F | Copy if the boolean register is 1if bt: ar = asConditional select |
| movt.s | fr, fs, bt | 11011011 fr fs bt 00000xDB0000 / mask 0xFF000F | Copy a float register if the boolean register is 1if bt: fr = fsFloating point (scalar) |
| msub.s | fr, fs, ft | 01011010 fr fs ft 00000x5A0000 / mask 0xFF000F | Multiply and subtractfr = fr - fs * ftFloating point (scalar) |
| mul.s | fr, fs, ft | 00101010 fr fs ft 00000x2A0000 / mask 0xFF000F | Floating point multiplicationfr = fs * ftFloating point (scalar) |
| mul16s | ar, as, at | 11010001 ar as at 00000xD10000 / mask 0xFF000F | Multiplication of the low 16 bits, signedar = sext(as[15:0]) * sext(at[15:0])Arithmetic |
| mul16u | ar, as, at | 11000001 ar as at 00000xC10000 / mask 0xFF000F | Multiplication of the low 16 bits, unsignedar = zext(as[15:0]) * zext(at[15:0])Arithmetic |
| mull | ar, as, at | 10000010 ar as at 00000x820000 / mask 0xFF000F | Multiplication, the low 32 bitsar = (as * at)[31:0]Arithmetic |
| mulsh | ar, as, at | 10110010 ar as at 00000xB20000 / mask 0xFF000F | Multiplication, signed, the high 32 bitsar = (as *s at)[63:32]Arithmetic |
| muluh | ar, as, at | 10100010 ar as at 00000xA20000 / mask 0xFF000F | Multiplication, unsigned, the high 32 bitsar = (as *u at)[63:32]Arithmetic |
| neg | ar, at | 01100000 ar 0000 at 00000x600000 / mask 0xFF0F0F | Negationar = -atArithmetic |
| neg.s | fr, fs | 11111010 fr fs 011000000xFA0060 / mask 0xFF00FF | Floating point negationfr = -fsFloating point (scalar) |
| nexp01.s | fr, fs | 11111010 fr fs 101100000xFA00B0 / mask 0xFF00FF | Bring the mantissa to 1..2 and negate the exponent (step of division and square root)Floating point (scalar) |
| nop | 0000000000100000111100000x0020F0 / mask 0xFFFFFF | No effect; written as nop.nBarriers and others | |
| nop.n | 11110000001111010xF03D / mask 0xFFFF | No effect (16 bit)16-bit instructions (code density) | |
| nsa | at, as | 010000001110 as at 00000x40E000 / mask 0xFFF00F | Number of the sign bits less 1 (normalize, signed)at = count of the leading bits equal to as[31], less 1Logic and bits |
| nsau | at, as | 010000001111 as at 00000x40F000 / mask 0xFFF00F | Number of the leading zero bits (32 for 0)at = clz(as)Logic and bits |
| oeq.s | br, fs, ft | 00101011 br fs ft 00000x2B0000 / mask 0xFF000F | Comparison: 1 if equal (no NaN)br = fs == ftFloating point (scalar) |
| ole.s | br, fs, ft | 01101011 br fs ft 00000x6B0000 / mask 0xFF000F | Comparison: 1 if less or equal (no NaN)br = fs <= ftFloating point (scalar) |
| olt.s | br, fs, ft | 01001011 br fs ft 00000x4B0000 / mask 0xFF000F | Comparison: 1 if less (no NaN)br = fs < ftFloating point (scalar) |
| or | ar, as, atalso shown as: mov | 00100000 ar as at 00000x200000 / mask 0xFF000F | OR; with the same source twice the copy of a register (mov, as mov.n)ar = as | atLogic and bits |
| orb | br, bs, bt | 00100010 br bs bt 00000x220000 / mask 0xFF000F | OR of boolean registersbr = bs | btBoolean registers |
| orbc | br, bs, bt | 00110010 br bs bt 00000x320000 / mask 0xFF000F | OR with the complement of the secondbr = bs | ~btBoolean registers |
| pdtlb | at, as | 010100001101 as at 00000x50D000 / mask 0xFFF00F | Probe the data TLB for an addressat = probe DTLB(as)System and privileged |
| pitlb | at, as | 010100000101 as at 00000x505000 / mask 0xFFF00F | Probe the instruction TLB for an addressat = probe ITLB(as)System and privileged |
| quos | ar, as, at | 11010010 ar as at 00000xD20000 / mask 0xFF000F | Division, signedar = as /s atArithmetic |
| quou | ar, as, at | 11000010 ar as at 00000xC20000 / mask 0xFF000F | Division, unsigned (by 0: an exception)ar = as /u atArithmetic |
| rdtlb0 | at, as | 010100001011 as at 00000x50B000 / mask 0xFFF00F | Read an entry of the data TLB (virtual address)at = DTLB[as].vaddrSystem and privileged |
| rdtlb1 | at, as | 010100001111 as at 00000x50F000 / mask 0xFFF00F | Read an entry of the data TLB (translation)at = DTLB[as].paddrSystem and privileged |
| recip0.s | fr, fs | 11111010 fr fs 100000000xFA0080 / mask 0xFF00FF | Start value for a reciprocalFloating point (scalar) |
| rems | ar, as, at | 11110010 ar as at 00000xF20000 / mask 0xFF000F | Remainder of the division, signedar = as %s atArithmetic |
| remu | ar, as, at | 11100010 ar as at 00000xE20000 / mask 0xFF000F | Remainder of the division, unsignedar = as %u atArithmetic |
| rer | at, as | 010000000110 as at 00000x406000 / mask 0xFFF00F | Read an external registerat = ER[as]System and privileged |
| ret | 000000000000 s 100000000x000080 / mask 0xFFF0FF | Return after call0 (to a0); written as ret.npc = a0Branches and calls | |
| ret.n | 1111 s 000011010xF00D / mask 0xF0FF | Return after call0 (16 bit)pc = a016-bit instructions (code density) | |
| retw | 000000000000 s 100100000x000090 / mask 0xFFF0FF | Return from a function with register window: turns the window back; written as retw.npc = a0 (top bits from pc); WINDOWBASE -= a0[31:30]Windowed registers (call4/8/12, entry, retw) | |
| retw.n | 1111 s 000111010xF01D / mask 0xF0FF | Return with register window (16 bit)pc = a0; WINDOWBASE -= a0[31:30]16-bit instructions (code density) | |
| rfdd | 111100011110 s 000100000xF1E010 / mask 0xFFF0FF | Return from a debug exception in the debug modeSystem and privileged | |
| rfde | 0000000000110010000000000x003200 / mask 0xFFFFFF | Return from a double exceptionpc = DEPCSystem and privileged | |
| rfdo | imm4 | 111100011110 imm4 000000000xF1E000 / mask 0xFFF0FF | Return from the debug mode (OCD: the debugger lets the core run)pc = EPC6; PS = EPS6System and privileged |
| rfe | 0000000000110000000000000x003000 / mask 0xFFFFFF | Return from an exception of level 1pc = EPC1; PS.EXCM = 0System and privileged | |
| rfi | imm4 | 000000000011 imm4 000100000x003010 / mask 0xFFF0FF | Return from an interrupt of level imm4pc = EPC[imm4]; PS = EPS[imm4]System and privileged |
| rfr | ar, fs | 11111010 ar fs 010000000xFA0040 / mask 0xFF00FF | Copy a float register to an address register (the bits)ar = bits(fs)Floating point (scalar) |
| rfwo | 0000000000110100000000000x003400 / mask 0xFFFFFF | Return from the handler of a window overflowWindowed registers (call4/8/12, entry, retw) | |
| rfwu | 0000000000110101000000000x003500 / mask 0xFFFFFF | Return from the handler of a window underflowWindowed registers (call4/8/12, entry, retw) | |
| ritlb0 | at, as | 010100000011 as at 00000x503000 / mask 0xFFF00F | Read an entry of the instruction TLB (virtual address)at = ITLB[as].vaddrSystem and privileged |
| ritlb1 | at, as | 010100000111 as at 00000x507000 / mask 0xFFF00F | Read an entry of the instruction TLB (translation)at = ITLB[as].paddrSystem and privileged |
| rotw | imm4 | 0100000010000000 imm4 00000x408000 / mask 0xFFFF0F | Turn the register window (in the handlers of the window exceptions)WINDOWBASE += imm4Windowed registers (call4/8/12, entry, retw) |
| round.s | ar, fs, imm4 | 10001010 ar fs imm4 00000x8A0000 / mask 0xFF000F | Convert to an integer, rounded (times 2^imm4)ar = round(fs * 2^imm4)Floating point (scalar) |
| rsil | at, imm4 | 000000000110 imm4 at 00000x006000 / mask 0xFFF00F | Read PS and set the interrupt level (interrupts up to imm4 off)at = PS; PS.INTLEVEL = imm4System and privileged |
| rsqrt0.s | fr, fs | 11111010 fr fs 101000000xFA00A0 / mask 0xFF00FF | Start value for a reciprocal square rootFloating point (scalar) |
| rsr | sr, at | 00000011 sr at 00000x030000 / mask 0xFF000F | Read a special register; also written rsr.name at (rsr.ps a3)at = SR[sr]Special and user registers |
| rsync | 0000000000100000000100000x002010 / mask 0xFFFFFF | Wait until written special registers apply to register readsBarriers and others | |
| rur | ar, ur | 11100011 ar ur 00000xE30000 / mask 0xFF000F | Read a user register (threadptr, fcr, fsr ...); also rur.name arar = UR[ur]Special and user registers |
| s16i | at, as, offset | imm8 0101 as at 00100x005002 / mask 0x00F00F | Store of a halfwordmem16[as + (imm8 << 1)] = at[15:0]Loads and stores |
| s32c1i | at, as, offset | imm8 1110 as at 00100x00E002 / mask 0x00F00F | Conditional store (compare and swap): stores at if the word equals SCOMPARE1; at gets the old valueold = mem32[a]; if old == SCOMPARE1: mem32[a] = at; at = oldLoads and stores |
| s32e | at, as, offset | 01001001 imm4 as at 00000x490000 / mask 0xFF000F | Store for the handlers of the window exceptions (offset -64..-4)mem32[as + ((imm4 << 2) - 64)] = atWindowed registers (call4/8/12, entry, retw) |
| s32i | at, as, offset | imm8 0110 as at 00100x006002 / mask 0x00F00F | Store of a word; with a small offset the assembler writes s32i.nmem32[as + (imm8 << 2)] = atLoads and stores |
| s32i.n | at, as, offset | imm4 as at 10010x0009 / mask 0x000F | Store of a word, offset 0..60 (16 bit)mem32[as + (imm4 << 2)] = at16-bit instructions (code density) |
| s32nb | at, as, offset | 01011001 imm4 as at 00000x590000 / mask 0xFF000F | Store of a word that bypasses the write buffer (offset 0..60)mem32[as + (imm4 << 2)] = atLoads and stores |
| s32ri | at, as, offset | imm8 1111 as at 00100x00F002 / mask 0x00F00F | Store of a word with release ordering (earlier accesses first)release; mem32[as + (imm8 << 2)] = atLoads and stores |
| s8i | at, as, imm8 | imm8 0100 as at 00100x004002 / mask 0x00F00F | Store of a bytemem8[as + imm8] = at[7:0]Loads and stores |
| salt | ar, as, at | 01110010 ar as at 00000x720000 / mask 0xFF000F | Signed comparison: 1 if less (ESP32-S3 only, not the ESP32)ar = (as <s at) ? 1 : 0Compare and exchange |
| saltu | ar, as, at | 01100010 ar as at 00000x620000 / mask 0xFF000F | Unsigned comparison: 1 if less (ESP32-S3 only, not the ESP32)ar = (as <u at) ? 1 : 0Compare and exchange |
| sddr32.p | as | 000000000111 as 111100000x0070F0 / mask 0xFFF0FF | Debug mode: store DDR and add 4 to the addressmem32[as] = DDR; as += 4System and privileged |
| sext | ar, as, bits | 00100011 ar as b-7 00000x230000 / mask 0xFF000F | Sign extension from bit b (7..22)ar = sext(as[b:0])Arithmetic |
| simcall | 0000000001010001000000000x005100 / mask 0xFFFFFF | Call of a simulator (no effect on the chip)System and privileged | |
| sll | ar, as | 10100001 ar as 000000000xA10000 / mask 0xFF00FF | Shift left by 32 less SAR (SAR set by ssl)ar = as << (32 - SAR)Shift and rotate |
| slli | ar, as, sa | 000 sa[4] 0001 ar as sa[3-0] 00000x010000 / mask 0xEF000F | Shift left by a constant (1..31; the fields hold 32 less sa)ar = as << saShift and rotate |
| sqrt0.s | fr, fs | 11111010 fr fs 100100000xFA0090 / mask 0xFF00FF | Start value for a square rootFloating point (scalar) |
| sra | ar, at | 10110001 ar 0000 at 00000xB10000 / mask 0xFF0F0F | Shift right signed by SARar = at >>s SARShift and rotate |
| srai | ar, at, sa | 001 sa[4] 0001 ar sa[3-0] at 00000x210000 / mask 0xEF000F | Shift right signed by a constant (0..31)ar = at >>s saShift and rotate |
| src | ar, as, at | 10000001 ar as at 00000x810000 / mask 0xFF000F | Shift right across two registers (64 bit) by SAR; with the same register twice a rotationar = ((as : at) >> SAR)[31:0]Shift and rotate |
| srl | ar, at | 10010001 ar 0000 at 00000x910000 / mask 0xFF0F0F | Shift right unsigned by SARar = at >>u SARShift and rotate |
| srli | ar, at, imm4 | 01000001 ar imm4 at 00000x410000 / mask 0xFF000F | Shift right unsigned by a constant (0..15; more with extui)ar = at >>u imm4Shift and rotate |
| ssa8b | as | 010000000011 as 000000000x403000 / mask 0xFFF0FF | Set SAR for a shift by whole bytes (big endian)SAR = 32 - as[1:0] * 8Shift and rotate |
| ssa8l | as | 010000000010 as 000000000x402000 / mask 0xFFF0FF | Set SAR for a shift by whole bytes (little endian)SAR = as[1:0] * 8Shift and rotate |
| ssai | sa | 010000000100 sa[3-0] 000 sa[4] 00000x404000 / mask 0xFFF0EF | Set SAR to a constantSAR = saShift and rotate |
| ssi | ft, as, offset | imm8 0100 as ft 00110x004003 / mask 0x00F00F | Store of a floatmem32[as + (imm8 << 2)] = ftFloating point (scalar) |
| ssip | ft, as, offset | imm8 1100 as ft 00110x00C003 / mask 0x00F00F | Store of a float, then add to the addressmem32[as] = ft; as += imm8 << 2Floating point (scalar) |
| ssl | as | 010000000001 as 000000000x401000 / mask 0xFFF0FF | Set SAR for a shift leftSAR = 32 - as[4:0]Shift and rotate |
| ssr | as | 010000000000 as 000000000x400000 / mask 0xFFF0FF | Set SAR for a shift right (from the low 5 bits)SAR = as[4:0]Shift and rotate |
| ssx | fr, as, at | 01001000 fr as at 00000x480000 / mask 0xFF000F | Store of a float, the address from two registersmem32[as + at] = frFloating point (scalar) |
| ssxp | fr, as, at | 01011000 fr as at 00000x580000 / mask 0xFF000F | Store of a float, then add to the addressmem32[as] = fr; as += atFloating point (scalar) |
| sub | ar, as, at | 11000000 ar as at 00000xC00000 / mask 0xFF000F | Subtraction of two registersar = as - atArithmetic |
| sub.s | fr, fs, ft | 00011010 fr fs ft 00000x1A0000 / mask 0xFF000F | Floating point subtractionfr = fs - ftFloating point (scalar) |
| subx2 | ar, as, at | 11010000 ar as at 00000xD00000 / mask 0xFF000F | Subtraction, the first register times 2ar = (as << 1) - atArithmetic |
| subx4 | ar, as, at | 11100000 ar as at 00000xE00000 / mask 0xFF000F | Subtraction, the first register times 4ar = (as << 2) - atArithmetic |
| subx8 | ar, as, at | 11110000 ar as at 00000xF00000 / mask 0xFF000F | Subtraction, the first register times 8ar = (as << 3) - atArithmetic |
| syscall | 0000000001010000000000000x005000 / mask 0xFFFFFF | System call: raises the exception SYSCALLSystem and privileged | |
| trunc.s | ar, fs, imm4 | 10011010 ar fs imm4 00000x9A0000 / mask 0xFF000F | Convert to an integer, truncatedar = trunc(fs * 2^imm4)Floating point (scalar) |
| ueq.s | br, fs, ft | 00111011 br fs ft 00000x3B0000 / mask 0xFF000F | Comparison: 1 if equal or NaNbr = fs == ft or unorderedFloating point (scalar) |
| ufloat.s | fr, as, imm4 | 11011010 fr as imm4 00000xDA0000 / mask 0xFF000F | Convert an unsigned integer to float, divided by 2^imm4fr = ufloat(as) / 2^imm4Floating point (scalar) |
| ule.s | br, fs, ft | 01111011 br fs ft 00000x7B0000 / mask 0xFF000F | Comparison: 1 if less or equal or NaNbr = fs <= ft or unorderedFloating point (scalar) |
| ult.s | br, fs, ft | 01011011 br fs ft 00000x5B0000 / mask 0xFF000F | Comparison: 1 if less or NaNbr = fs < ft or unorderedFloating point (scalar) |
| un.s | br, fs, ft | 00011011 br fs ft 00000x1B0000 / mask 0xFF000F | Comparison: 1 if one of the numbers is NaNbr = isnan(fs) or isnan(ft)Floating point (scalar) |
| utrunc.s | ar, fs, imm4 | 11101010 ar fs imm4 00000xEA0000 / mask 0xFF000F | Convert to an unsigned integer, truncatedar = utrunc(fs * 2^imm4)Floating point (scalar) |
| waiti | imm4 | 000000000111 imm4 000000000x007000 / mask 0xFFF0FF | Set the interrupt level and wait for an interrupt (saves power)PS.INTLEVEL = imm4; waitSystem and privileged |
| wdtlb | at, as | 010100001110 as at 00000x50E000 / mask 0xFFF00F | Write an entry of the data TLBDTLB[as] = atSystem and privileged |
| wer | at, as | 010000000111 as at 00000x407000 / mask 0xFFF00F | Write an external registerER[as] = atSystem and privileged |
| wfr | fr, as | 11111010 fr as 010100000xFA0050 / mask 0xFF00FF | Copy an address register to a float register (the bits)fr = bits(as)Floating point (scalar) |
| witlb | at, as | 010100000110 as at 00000x506000 / mask 0xFFF00F | Write an entry of the instruction TLBITLB[as] = atSystem and privileged |
| wsr | sr, at | 00010011 sr at 00000x130000 / mask 0xFF000F | Write a special register; also wsr.name atSR[sr] = atSpecial and user registers |
| wur | ur, at | 11110011 ur at 00000xF30000 / mask 0xFF000F | Write a user register; also wur.name atUR[ur] = atSpecial and user registers |
| xor | ar, as, at | 00110000 ar as at 00000x300000 / mask 0xFF000F | Exclusive ORar = as ^ atLogic and bits |
| xorb | br, bs, bt | 01000010 br bs bt 00000x420000 / mask 0xFF000F | Exclusive OR of boolean registersbr = bs ^ btBoolean registers |
| xsr | sr, at | 01100001 sr at 00000x610000 / mask 0xFF000F | Exchange a special register and a register; also xsr.name att = SR[sr]; SR[sr] = at; at = tSpecial and user registers |