Take the 2-minute tour ×
Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free, no registration required.

I'm reading a relatively 'friendly' and simple book about compilers named "Let's Build A Compiler" (originally a series of articles).

The compiler written in the book generates instructions in some assembly language (68000 assembly).

Since I don't know this assembly language, I decided that my compiler will also generate 'assembly' instructions (e.g. MOVE #4,D0 has the same meaning: "put the number 4 in register D0"), only they will be executed by a program I will write, instead of by a physical computer. A kind of "virtual machine".

So far I think this shouldn't be a problem, but I have a question regarding this:

What is a "generic" way to handle if statements in assembly? For example, what would be the simplest assembly translation for this code:

if(a>b){
    // do stuff
}
share|improve this question
    
Ironically, such tasks are accomplished with jumps or branches, meaning that on machine level, every conditions are handled like gotoes. –  Pierre Arlaud Jul 28 at 7:52

2 Answers 2

up vote 10 down vote accepted

In common assembly, it's accomplished by various conditional jumps, e.g.

cmp %eax, 1; 
je label

Which roughly translate to "compare register eax and constant 1; if equal, jump to label. Otherwise, the next instruction is executed.

The result of cmp is stored in the status register. It's a register containing results from arithmetic operations such as the zero flag ZF or the sign flag SF.

cmp is implemented as a substract. If both operands were the same, ZF is set. If the sign flag is set, it means the result of the operation was negative, thus that second operand was bigger than the first.

The various jump instructions read the sign and zero flags and perform the jump based on their value.

AFAIK there is only one status register.

share|improve this answer
    
Thanks. In terms of what this actually does in a CPU (so I know how to implement it in my VM): I'm guessing that cmp saves the result of the evaluation in some register. Question 1: am I right? Is there a single register to store the value of the latest cmp (i.e. every cmp overrides what's in the register with a new value)? Question 2: if I'm right, what is saved in the register? A simple 1 or 0 to signal if the values are equal or not? Or something else? –  Aviv Cohn Jul 27 at 22:44
    
You're right, cmp modifies a hidden register. I'm updating my answer with more details. –  toasted_flakes Jul 27 at 22:51
    
Thanks for the explanation. Now I understand how this works in a real CPU, but I have a simpler idea of how to implement this in a program. Please say if you think this is reasonable: in my 'VM' every cmp will result in 3 'registers' being set: equals, greater, and smaller. So for example cmp d0,d1 where d0 is 5 and d1 is 3 will result in register greater being set to 1 while the other two are set to 0. Then operations such as je look at these 'registers' to decide whether or not to act. Does this sound reasonable? –  Aviv Cohn Jul 27 at 23:18
    
That's one way to do it. You really only need two, EQUALS and one of the unequal cases. –  John R. Strohm Jul 27 at 23:42

Lets go back to an earlier day, when things were simple.

Lets go back to the days of the MOS 6502 that powered the Atari, Apple II, and several other systems from the late 70s. You can still find it in embedded systems and is apparently still produced in volumes of hundreds of millions (while this is old technology, its not outdated). By understanding how this system worked, it becomes easier to understand how more modern and complex systems worked.

The 6502 had only a few registers:

  • Program counter
  • Stack Pointer
  • Accumulator (this is the 'important' one)
  • X and Y 'index' registers
  • Processor Status (this is the other 'important' one in this situation)
    • C Carry flag
    • Z Zero flag (set if the last operation had a result of 0)
    • D Decimal mode
    • I Interput enable/disable
    • B Break
    • V Overflow
    • S Sign (set of the result of an operation is negative - sometimes that is designated the N flag for negative)

When you did a CMP #$44 this would compare the Accumulator with the value 0x44. In doing so, it would set the ZCS flags appropriately. C is set if the Accumulator is greater than or equal to value, and Z is set if the two are equal. This was not too much more than doing A - M where M is the value (read from memory, or a hard value), set the flags and throw away the result (compare with SBC that would store the result in the Accumulator).

At this point, you've got the status register set, but you need to do something with it. There were two instructions for this. BEQ (Branch if EQual) and BNE (Branch if Not Equal). These two instructions would each test Z of the status register - BNE would branch if Z was 0, BEQ would branch if Z was 1. While CMP does set more flags, BEQ and BNE don't care about them - just Z.

And really, thats how conditionals work when you are down to that level. There's a status register that is floating around somewhere many times. This register doesn't exist when you get into the pipelined, superscalar, and speculative processors. These tend to go for more explicit branching statements or use arbitrary registers for the status result rather than a dedicated register (see the MIPS instruction set and look at the bgt, blt, beqz and the like).


As an aside, if you are poking about virtual machines and assembly, delve a bit into Core War and the MARS virtual machine.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.