Microoperations
For more information on p6as syntax, see p6as page.
The general instruction format is:
[flow_modifier] destination = operation[.modifier1[.modifier2[...]]](source1, source2 [, segment] [, ubits/OA/IA values])
- flow_modifier - Flow control flags (dot-separated, e.g., EOM.SEOM)
- destination - Target register (default: SINK if omitted)
- operation - Microcode operation with optional modifiers (dot-separated)
- source1, source2 - Source operands (registers, constrom, const, alias)
- Segment selectors (SS, DS, ES, FS, GS, CS, TR, ...)
- Special fields: U1.X, U2.X, U3.X, U4.X, OA.X, IA.X (where X is hex value)
On Pentium Pro (first P6 microprocessor) microcode word in triplet is 72 bits wide and has the following format:
| 7 | 6 | 5 | 4 | |3 | | 2 | | 1 | 0| |1 0 9 8|7 6 5 4 3 2 1 0 9 8 7 6|5 4 3 2 1 0 9 8 7|6 5 4 3 2 1 0 9|8 7 6 5 4 3 2 1|0 9 8 7 6 5 4 3|2|1 0 9 8|7 6 5 4 3|2 1 0 9 8 7 6 5 4|3 2 1 0| |opalias| opcode | | LSrc2 | LSrc1 | LDest | | LSeg | ImmAlias| Immediate | FlowM |
There are also additional bits 72-79 on Pentium 2, total 80 bits (see notes on Peter Bosch work below). Each MSROM/MSRAM line has 231 bits, so some bits are shared across all opcodes in triplet.
Pentium 3 has slightly different format compared to Pentium 2.
Pentium M introduced new 8th bit for register addressing (LDest,LSrc1,LSrc2), we call this EXLDEST, EXLSRC1, EXLSRC2 respectively. This is used mainly for SSE2 XMMn registers.
Core CPUs (Yonah) are using very similar encoding as Pentium M.
Core2 seems to have introduced new opcode bit - which is called EXOPCODE in our represenatation (work in progress, to be further analyzed).
- Named registers: From regmap (e.g., EAX, EBX, ESP, etc.)
- Generic format: REG.XX (where XX is 2-digit hex)
- Constants: CONST_0, CONST_XX, CONST_XX+YY
- ROM constants: CONSTROM.XX, CONSTROM2.XX
- Aliases: ALIAS.XX, M_IMM, REG_OP_Size, virt_ip, next_virt_ip
- Control registers: From creg_map
Branch Targets
For branch operations, source2 is a branch target:
- Label reference: my_label
- Explicit address: UROM_XXXX
Operation Modifiers
Operations can have up to 3 modifiers separated by dots: operation.modifier1.modifier2.modifier3
The number and type of modifiers depends on the specific operation.
Direct Opcode Specification
For unknown or special opcodes: UOP.XXX where XXX is the hexadecimal opcode value.
Flow markers define where each original instruction (macroinstruction) begins and ends in terms of its translated logical micro-operations (micro-ops).
- A Beginning of Macroinstruction (BOM) marker shows that the micro-op it’s attached to is the first micro-op of that macroinstruction.
- An End of Macroinstruction (EOM) marker shows that the micro-op is the last one for that macroinstruction.
- If a macroinstruction translates into just one micro-op, that single micro-op will carry both BOM and EOM markers.
- Meaning of 2 bits 2 and 3 is unknown (marked as Fl2 and Fl3)
opalias probably changes opcode meaning. To be further analyzed.
Many opcodes have modifiers which further specify operation (e.g. data size, DSZ32/DSZ16/DSZ8/etc.)
AP-526 on pp. 66 brings some light to this.
Core2 seems to have additional opcode bit (that is present in approx. 25% of the MSROM code). Verified on this - result is rax=000000000000AA51 ([[BTR]] 0xaa55,2) rbx=00000000C6D6C5D5 (WUCONCAT.dsz16 0xE5F555AAA5B5C5D5, 0xE6F655AAA6B6C6D6), rcx=9BD956AA9ADB1B5B (ROL E6F655AAA6B6C6D6,2):
.org 0x7f80 RBX = UOP.506 (RSI , RBP , U4.00400000) # this is in fact WUCONCAT! RAX = UOP.506 (RAX , RCX , U4.00000000) # this is BTR.DSZ16 (506) RCX = MOVE.DSZ64 (RSI , RCX , U4.00400000) # this becomes ROL ! EOM MOVE.DSZ32 (CONST , CONST_0 , U4.00040000) EOM MOVE.DSZ32 (CONST , CONST_0 , U4.00040000) EOM MOVE.DSZ32 (CONST , CONST_0 , U4.00040000) .end b855aa0000b9020000000f33c3c3c3c3c3
FF00: 0FC2D3F0 04253F00 07047E82 00000000 05404400 04801803 00000000 00000000 FF08: 00020000 00200008 00000000 40000000 00000000 04809012 00040000 20000100
WUCONCAT can also be encoded as "UOP.502 (RSI , RBP , U4.00400000)" - seems that not all opcode bits are decoded.
Looks like these are bit values (OA.C = OA.4|OA.8)
- OA.1 - used with LEA
- OA.2
- OA.4 - likely assumes DSZ from instruction operand size (prefix 66 makes difference)
- OA.6 - likely assumes DSZ from instruction operand size
- OA.7
- OA.8 - used with LOAD/STA (prefix 67 makes difference)
- OA.9 - used with LOAD/STA
- OA.C - used with LOAD/STA (seems to be the same as OA.4 combined with OA.8)
- OA.D - used with LOAD/STA
Fields dest, src1, src2 contain register ID (e.g. AL=0x08, AH=0x0C, AX=0x28, EAX=0x38, ST0=0x10, 0x18=TMP6,). Register indexes have variable bit width. For more information see registers page.
SINK register (ID 0x01) has special meaning, it is in LDEST and is used to discard the uop result. p6dis simply omits uop assignment for SING register, p6as emits SINK when there is no uop result assignment.
If uop is taking only 1 argument, such as MOVE/BSF/BSR/MOVEFROMCREG/etc, usually lsrc2 is used (lsrc1 is ignored, but used by execution unit for dependency handling)
List of const usage in CPUID 6D8 MSROM:
- CONST_00
- CONST_01 - used only with UOP.419
- CONST_04 - immediate optional
- CONST_05 - always immediate, memory operatoins - LEA,LOAD, STA and UOP.841 UOP.842 UOP.84C UOP.84D UOP.8CC UOP.942 UOP.94B UOP.9C2 UOP.9C3 UOP.A42 UOP.AC2 UOP.B41 UOP.B42 UOP.B43 UOP.B49 UOP.B4C UOP.B4D UOP.BC3 UOP.CCC UOP.E41
- CONST_06 - no immediate!
- CONST_08 - single use "UROM_1C81 TMPA = AND.DSZ32 (CONST_08+0FF , TMP1 )"
- 0x0C - constrom, address in imm ; sprintf(result, "CONSTROM_%03X", imm);
- 0x0D - retrieves values from constrom, however they are repated weird way...
- CONST_0E - typical usage with MOVEFROMCREG / MOVETOCREG, but also LOAD, LEA, and UOP.204, UOP.661, sometimes ALU ops (ADD), or WUCONCAT
- Looks like in load-stores it multiplies SIGNED value by 4. So CONST_0E+1FF gets expanded to "-4".
- STA.M0.SC2.DSZ?(CONST_0E+1FF, ESP_20, SS, OA.D) - this stores at ESP-n (magic with CONST_e; 0x1ff gets expanded to -1 and mutliplied by operand size? probably
- CONST_0F - used with INTEXTRACT.HI16, LOAD, LOAD40, LEA
- CONST_10
- CONST_11 - Alias (further depends on imm field, which is in range of 0x00-0x1F, then repeating each 0x20 values)
- if (imm == 0x0) { strcpy(result, "M_IMM");
- ALIAS_0x08- unknown
- (imm == 0x10) - REG_OP_Size - see Intel AP-526 pp. 84
- if (imm == 0x11) { strcpy(result, "virt_ip");
- if (imm == 0x12) { strcpy(result, "next_virt_ip");
- ALIAS_0x14- unknown
- CONST_14
- CONST_16
- CONST_18 - used with UOP.130, UOP.131, UOP.132, STA40 LOAD40 UOP.CC9 UOP.CCB UOP.94B
- CONST_1C - used only with UOP.C44 and UOP.C48
- CONST_1D - used only with UOP.CC1
Real CPU smaples of various CONST_qq on Yonah CPUID 6E8 plat 20:
EAX = MOVE.DSZ32 (CONST , CONST_QQ+001 ) EBX = MOVE.DSZ32 (CONST , CONST_QQ+012 ) ECX = MOVE.DSZ32 (CONST , CONST_QQ+123 ) EDX = MOVE.DSZ32 (CONST , CONST_QQ+1FF )Result:
CONST_00: EAX=00000000 EBX=00000000 ECX=00000000 EDX=00000000 CONST_01: EAX=00000000 EBX=00000000 ECX=00000000 EDX=00000000 CONST_02: EAX=00000000 EBX=00000000 ECX=00000000 EDX=00000000 CONST_03: EAX=00000000 EBX=00000000 ECX=00000000 EDX=00000000 CONST_04: EAX=00000000 EBX=00000000 ECX=00000000 EDX=00000000 CONST_05: EAX=00000000 EBX=00000000 ECX=00000000 EDX=00000000 CONST_06: EAX=00000000 EBX=00000000 ECX=00000000 EDX=00000000 CONST_07: EAX=00000000 EBX=00000000 ECX=00000000 EDX=00000000 CONST_08: EAX=00000000 EBX=00000000 ECX=00000000 EDX=00000000 CONST_09: EAX=00000000 EBX=00000000 ECX=00000000 EDX=00000000 CONST_0A: EAX=00000000 EBX=00000000 ECX=00000000 EDX=00000000 CONST_0B: EAX=00000000 EBX=00000000 ECX=00000000 EDX=00000000 CONST_0C: EAX=000006d0 EBX=ffdbb22a ECX=e03f003f EDX=fffefbff CONST_0D: EAX=80040e14 EBX=803f4fd7 ECX=803e0006 EDX=bfffffff CONST_0E: EAX=00000000 EBX=00000000 ECX=00000000 EDX=00000000 CONST_0F: EAX=00000000 EBX=00000000 ECX=00000000 EDX=00000000 CONST_10: EAX=00000001 EBX=00000012 ECX=ffffff23 EDX=ffffffff CONST_11: EAX=080483e4 EBX=08048547 ECX=080487e7 EDX=1a0c8164 CONST_12: EAX=00000001 EBX=00000012 ECX=ffffff23 EDX=ffffffff CONST_13: EAX=080483e4 EBX=08048547 ECX=080487e7 EDX=1a0c8164 CONST_14: EAX=00000000 EBX=00000000 ECX=00000000 EDX=00000000 CONST_15: EAX=00000000 EBX=00000000 ECX=00000000 EDX=00000000 CONST_16: EAX=00000001 EBX=00000012 ECX=ffffff23 EDX=ffffffff CONST_17: EAX=080483e4 EBX=08048547 ECX=080487e7 EDX=3a0c8164 CONST_18: EAX=00000000 EBX=00000000 ECX=00000000 EDX=00000000 CONST_19: EAX=00000000 EBX=00000000 ECX=00000000 EDX=00000000 CONST_1A: EAX=00000001 EBX=00000012 ECX=ffffff23 EDX=ffffffff CONST_1B: EAX=080483e4 EBX=08048547 ECX=080487e7 EDX=1a0c8164 CONST_1C: EAX=00000000 EBX=00000000 ECX=00000000 EDX=00000000 CONST_1D: EAX=00000000 EBX=00000000 ECX=00000000 EDX=00000000 CONST_1E: EAX=00000000 EBX=00000000 ECX=00000000 EDX=00000000 CONST_1F: EAX=00000000 EBX=00000000 ECX=00000000 EDX=00000000
Real CPU smaples of ALIAS.xx values on Yonah CPUID 6E8 plat 20:
000: a2000000 (this is from opcode "0F A2"; not exactly an immediate, but ...?) 001: 080483e4 002: 08048547 003: 080487e7 004: 3f083118 005: 3f0cb3fc 006: 3f0cb55f 007: 3a0c8164 008: 7d89d875 009: 080483e4 00A: 08048547 00B: 080487e7 00C: 3f083118 00D: 3f0cb3fc 00E: 3f0cb55f 00F: 3a0c8164 (sometimes 3a0c8164) 010: 00000004 - DSZOP ? 011: 080483e4 - last called routine (cpuid asm in this case) 012: 08048547 - address after last called routine 013: 080487e7 - ??? 014: 3f083118 015: 3f0cb3fc 016: 3f0cb55f 017: 3a0c8164 018: 01a20000 019: 080483e4 - same as 11 01A: 08048547 - same as 1A 01B: 080487e7 01C: 3f083118 01D: 3f0cb3fc - same as 0D ? 01E: 3f0cb55f - same as 0E ? 01F: 1a0c8164 - same as 0F ?
Any of operands can contain immediate value, that is added to source. Typically used with constants. Meaning of alias item is unknown, likely it is used to select expansion from 9bit imm field to deisred length.
Constant ROM
Is 512-dword memory specific to each CPU that holds various constants. For example you can find there CPUID "GenuineIntel" doublewords. It is different from FPROM.
Branch target has 17 bits and is encoded to src2 register and immediate value:
LSrc2 = (btarg >> 9) & 0xff; (39) Immmediate = btarg & 0x1ff; (4)
SEG
Segment specification for memory/IO operations
Possible values:
{0x00, "SEG_SINK"},
{0x01, "LINSEG"},
{0x02, "SEG_02"},
{0x03, "SEG_03"},
{0x04, "SEG_04"},
{0x05, "SEG_05"},
{0x06, "GDTR"},
{0x07, "LDTR"},
{0x08, "ES"},
{0x09, "CS"},
{0x0A, "SS"},
{0x0B, "DS"},
{0x0C, "FS"},
{0x0D, "GS"},
{0x0E, "IDTR"},
{0x0F, "TR"},
These are typically unknown values U1,U2,U3,U4 that belong to the uop. Values U1..U3 have same meaning across all CPUs, U4 is microarchitecture-dependent, used only starting Pentium III.
There are also unknown bits per uop:
| Name | Position | Size | CPUs | Notice |
| U1 | 22 | 1 bit | all | |
| U2 | 47-55 | 9 bits | all | |
| U3 | 72-79 (not shown in the format above) | 8 bits | all | These bits were taken from Peter Bosch's Python code, their meaning/structure is not known |
| U4 | 80-...(not shown in the format above) | ? | Pentium 3,Pentium M, Yonah, Core2 | The U4 range is temporary, used for bits with unknown meaning (possibly some other Ux bits) |
bit 22 - U1.1 seems to be negated implicit supervisor access (0=implicit supervisor access, no perms checked), use with STA/LDA
U3.01 seems to be paired with these opcodes: TRANSPORTUIP, U_JCC_N.xxx U_JCC_T.xxx, U_JMP_NT UOP.010 (From Pentium II to Core2)
U3 bits 3-4 - Weird P-II Peter Bosch's code
U3 bits 3-4 for triad slot 2 are NEVER assigned!
uop[2][73] = right_1[2]; // U3 bit1 uop[2][74] = right_1[5]; // U3 bit2 // uop[75] never set? // U3 bit3 // uop[76] never set? // U3 bit4
U3 bits 5,6 - Shared bits - P-II Peter Bosch's code
These bits U3 bits 5-6 (values 0x20 and 0x40) are shared between all slots in triad.
uop[0][77] = left_1[0]; // U3 bit 5 uop[0][78] = left_1[1]; // U3 bit 6 uop[1][77] = left_1[0]; uop[1][78] = left_1[1]; uop[2][77] = left_1[0]; uop[2][78] = left_1[1];
U4
These bits/flags do not have same function across platforms. Used only in Pentium 3, Pentium M, Core, Core2.
Single unknown bit, seems to go along with UOP.051
.utripletbits 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001 UROM_237C UOP.051 (ALIAS.199 , ST5 , U3.01, U4.00440000 /* U4:0000 0000 0100 0100 0000 0000 0000 0000 */) UROM_237D Fl3 REG.47 = MOVE.DSZ32 (CONST , CONST_16+0FF , U4.00240000 /* U4:0000 0000 0010 0100 0000 0000 0000 0000 */) UROM_237E loc_237E: ; xref: 102E -- .utripletbits 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001 UROM_259C UOP.051 (ALIAS.1F0 , R11B_4x , U3.01, U4.00440000 /* U4:0000 0000 0100 0100 0000 0000 0000 0000 */) UROM_259D Fl3 REG.47 = MOVE.DSZ32 (CONST , CONST_16+0FF , U4.00240000 /* U4:0000 0000 0010 0100 0000 0000 0000 0000 */) UROM_259E loc_259E: ; xref: 2F6C -- .utripletbits 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001 UROM_25A0 UOP.051 (ALIAS.1F0 , R11B_4x , U3.01, U4.00440000 /* U4:0000 0000 0100 0100 0000 0000 0000 0000 */) UROM_25A1 Fl3 REG.47 = MOVE.DSZ32 (CONST , CONST_16+0FF , U4.00240000 /* U4:0000 0000 0010 0100 0000 0000 0000 0000 */) UROM_25A2 loc_25A2: ; xref: 2558 -- .utripletbits 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001 UROM_4B94 UOP.051 (ALIAS.199 , ST5 , U3.01, U4.00440000 /* U4:0000 0000 0100 0100 0000 0000 0000 0000 */) UROM_4B95 MOVE.DSZ8 (CONST , CONST_0 ) UROM_4B96 MOVE.DSZ8 (CONST , CONST_0 ) UROM_4B98 U_JMP.NT (CONST , UROM_4BA4 , IA.11, U3.01, U4.00440028 /* U4:0000 0000 0100 0100 0000 0000 0010 1000 */) -- .utripletbits 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001 UROM_550C UOP.051 (ALIAS.11D , CL , U3.01, U4.00440000 /* U4:0000 0000 0100 0100 0000 0000 0000 0000 */) UROM_550D MOVE.DSZ8 (CONST , CONST_0 ) UROM_550E MOVE.DSZ8 (CONST , CONST_0 ) UROM_5510 U_JMP.NT (CONST , UROM_2249 , IA.11, U3.01, U4.00440028 /* U4:0000 0000 0100 0100 0000 0000 0010 1000 */)
- AP-526 kib.kiev.ua/x86docs/Intel/Intel-OptimGuide/242816-001.pdf↗
- github.com/chip-red-pill/uCodeDisasm↗
- patents.google.com/patent/US5559974A/en?oq=US5559974↗
- US6378061B1
- US5185872A
The author is not affiliated with, endorsed by, or sponsored by Intel Corporation or its affiliates. All trademarks, including but not limited to Intel, Pentium, and any other registered or unregistered marks mentioned herein, are the property of their respective owners. Their use in this context is solely for descriptive and informational purposes and constitutes nominative fair use under applicable trademark laws.