Hello, and welcome to my latest installment of my unofficial Intro to Intel Assembly Language series. Today we're going to learn how to make if-else and switch constructs in assembly language!
First off, let's analyze a simple if statement:
Clearly the something() will only execute if condition is true. If not, the computer jumps over the something() and resumes execution at foo(). Jumps...wait...didn't we learn how to do that in part 3? Why don't we rewrite it like this:Code:if( condition ) { something(); } foo();
Now we're getting somewhere. Even though this code uses the tabooed goto statement, this is essentially how it's implemented in assembly language. So how do we do this? Well, first we need to compare condition to true. Then we jump based on the result:Code:if( !condition ) goto NEXT; something(); NEXT: foo();
So how do we compare stuff in assembly language? With the cmp instruction. cmp can be used just like add, sub etc; it follows all the same rules regarding restrictions on operands. However, it compares the two operands (subtracts dest from src) and sets the internal flags register with the results. You can then test this register and make decisions on where to jump. (Neither operand is modified, by the way.)Code:if( condition == FALSE) goto NEXT;
But wait! How does the CPU know if I want to check to see if two operands are equal, or if one is greater than the other?
Patience...all will be revealed.
Samples:
Now why are cmp eax,edx and cmp edx,eax not equivalent? Well...if you're testing for greater-than, eax > edx is not the same as edx > eax, now is it? Same principle.Code:;these two are NOT necessarily equivalent! cmp eax, edx cmp edx, eax ;you can use constants on either side... cmp 0, eax cmp DWORD PTR [esp], 0x0F85 ;still can't use two memory operands, though.
How do we check things, then?
Conditional jumps. There are about sixteen:
JE - Jump if equal
JNE - Jump if not equal
JA - Jump if above (unsigned >)
JAE - Jump if above or equal (unsigned >=)
JB - Jump if below (unsigned <)
JBE - Jump if below or equal (unsigned <=)
JG - Jump if greater than (signed >)
JGE - Jump if greater than or equal (signed >=)
JL - Jump if less than (signed <)
JLE - Jump if less than or equal (signed <=)
JO - Jump if overflow/underflow
JNO - Jump if no overflow/underflow
JPE - Jump if parity even (even number of bits set in resultant)
JPO - Jump if parity odd (odd number of bits set in resultant)
JC - Jump if carry (arithmetic operations)
JNC - Jump if no carry (arithmetic operations)
JS - Jump if sign bit set (i.e. negative)
JNS - Jump if sign bit not set (i.e. positive)
Some of these jump instructions also have aliases (alternate mnemonics that map to the same instruction). For example, JA has the alias JNBE, JG = JNLE, and so on. Some other aliases:
JPE = JP
JPO = JNP
JE = JZ (jump if zero)
JNE = JNZ (jump if not zero)
Anyway, we shouldn't get too mired in these. Point is, now we can make decisions! Let's take the following snippet of code, and convert it to assembly language:
Assuming a=eax and b=ebx...Code:if(a == b) do_something(); else do_something_else(); foo();
Clearly we can make some optimizations:Code:cmp eax,ebx je DO_TRUE jne DO_FALSE DO_TRUE: call do_something jmp RESUME DO_FALSE: call do_something_else jmp RESUME RESUME: call foo
Yes, you can make several tests in a row, provided that you only do tests consecutively. Don't use other instructions, as some of them modify flags and that'll screw things up for you. Of course, if you know what you're doing, then go ahead. Just remember I warned you.Code:cmp eax,ebx jne DO_FALSE ;if we get here, then eax=ebx call do_something jmp RESUME DO_FALSE: call do_something_else ;fall through RESUME: call foo
Switch Statements
The typical switch statement involves comparing one variable to a lot of values. It's very simple to code in assembly language:
Implementing a fall-through is also easy:Code:cmp eax, 1 je CASE_1 cmp eax, 2 je CASE_2 ... CASE_1: ;blah... jmp RESUME CASE_2: ;more blah... jmp RESUME ... RESUME: ;move on with life
Code:case 4: do_something(); //fall through case 5: do_another(); break;If you want to implement a default statement, just stick the code after your tests.Code:cmp eax,4 je CASE_4 cmp eax,5 je CASE_5 ... CASE_4: call do_something CASE_5: call do_another jmp RESUME ... RESUME: ;continue on
For LoopsCode:cmp eax,189024 je CASE_189024 DEFAULT_CASE: ;code... jmp RESUME ... CASE_189203: ... CASE_189204: ... RESUME: ;end of switch statement
Well, this is pretty easy:
Code:for(i = 0; i < 10; ++i) do_thing(); foo();While LoopsCode:mov eax,0 BEGIN_LOOP: cmp eax, 10 je END_LOOP call do_thing inc eax jmp BEGIN_LOOP END_LOOP: call foo
If you can do a for loop, you can do a while loop just as easily:
Do-While LoopsCode:BEGIN_LOOP: ;assume we have a boolean in EAX cmp eax, 0 je END_LOOP ;this is the inside of the loop ... jmp BEGIN_LOOP END_LOOP:
This actually takes fewer instructions to implement:
Tips and TricksCode:BEGIN_LOOP: ;code inside the loop ;this is the condition we're testing cmp eax, 0 jne BEGIN_LOOP END_LOOP:
1) Instead of doing this:
Do this:Code:;compiles to 6 bytes cmp eax, 0
It's a fairly standard way of comparing something to zero; since or changes the flags, we can check to see if the result was zero or not. ORing anything with itself doesn't change it, so we don't have to worry about inadvertently modifying our data. Note that you can't do this with a memory operand, because you'd have two memory operands for one instruction, which is illegal.Code:;compiles to 2 (sometimes 3) bytes or eax,eax
2) Instead of this:
Do this:Code:mov eax, 0
Again, we have the same restrictions as or eax,eax. The proof of why this works is left as an exercise to the reader.Code:xor eax,eax
Well, that's all I have time for today. We'll continue learning assembly language with my next tutorial, where I will (most likely) explain functions, how to call them, how to pass arguments, and all that fun stuff. I don't know when I'll be able to post the next tutorial...but until then, good luck, and have fun learning ASM!
Next In This Series
Intro to Intel Assembly Language: Part 5
Last edited by dargueta; 11-18-2010 at 07:19 PM.
sudo rm -rf /
Well done, and very clear. As you are showing, ASM isn't that hard, it just pushes you to work at a lower level.
Easy to read, easy to understand - the qualities of a well written tutorial! Nice work. +rep
What IDE / compiler do you use / recommend?
vim + nasm.
ConTEXT (Windows only) or SciTE (Linux) for editing, NASM for compiling.
Welcome back, MeTh0Dz.
sudo rm -rf /
There are currently 1 users browsing this thread. (0 members and 1 guests)
Bookmarks