XlogicX Blog

Assembly_Is_Too_High-Level_-_Redundant_Mnemonics

Out of all things that don't translate 1-to-1 from assembly language to machine code, this may be one the most well known ones. For those that don't know, there are multiple mnemonics in assembly language that can end up translating to identical machine code. Even though I'll start with my discussion with conditionals, these are not the only redundant mnemonics. This post will cover most of the less complicated ones (because there are possibly quite a bit more examples than from just this series of posts).

 

Conditional Mnemonics:

First of all, there's more than just the conditional jump (Jcc); there's MOVcc, FCMOVcc, and SETcc. Before we jump into the mnemonics and machine code, let's first consider what conditional statements are logically equivalent:

above = not below or equal

above or equal = not below = not carry

below = carry = not above or equal

below or equal = not above

equal = zero

greater = not less or equal

greater or equal = not less

less = not greater or equal

less or equal = not greater

not equal = not zero

not parity = parity odd

parity = parity even

Above are 26 individual conditions, yet they only represent 12 actually machine opcodes. This isn't bad, it allows an assembly programmer the flexibility to say “jump if greater” or “jump if not less than or equal to,” depending on what makes the most sense to them at the time, given the context/flow of their program. Even though, logically, both of those statements are exactly the same thing (and in machine code, they would be as well).

Let's see a side-by-side of the 2 above assembly statements and look at what gets produced from a debugger:

jccasm     jccedb

 

You'll see that I state jg (jump to start if greater) and then jnle (jump to start if not less than or equal), but the disassembler (edb) says that they are both jnle. Notice that the opcode for both is 7F. The only difference is the operand (FE vs FC), however, considering that this is a relative jump short, FE being -2 and FC being -4; they would both jump right back to the same place. This is why you see the 0x0000000000400080 address as the operand (translated by edb) after both jnle's.

Now that's out of the way, lets look at all of the conditional jump shorts with a 1-byte immediate operand:

72 = JB, JC, and JNAE (below, carry, not above or equal)

73 = JAE, JNB, and JNC (above or equal, not below, not carry)

74 = JE and JZ (equal, zero)

75 = JNE and JNZ (not equal, not zero)

76 = JBE and JNA (below or equal, not above)

77 = JA and JNBE (above, not below or equal)

7A = JP and JPE (parity, parity even)

7B = JNP and JPO (not parity, parity odd)

7C = JL and JNGE (less, not greater or equal)

7D = JGE and JNL (greater or equal, not less)

7E = JLE and JNG (less or equal, not greater)

7F = JG and JNLE (greater, not less or equal)

Some takeaways:

There may seem to be an overwhelming amount of conditional jump opcodes, but it turns out that it gets cut in half when you factor in the redundancy. Also, not that it matters so much, but if you're the type that likes to reverse code, know that if a C programmer said if (variable > 5) {, note that this may get converted to a jnle (if not less than or equal to). It's pretty much like C code saying if !(variable <= 5) {...which is the same thing actually.

Waiting:

On a smaller note, the wait and fwait instructions are the same thing. They both get assembled to 0x9b. This isn't a secret either; The Intel manual states this on the description for the wait instruction.