Question

I would like to know when using IA32 architecture, by default are the operands signed or unsigned.

On Linux Operating systems, I am using as (GNU assembler) to store 253 in the %eax register. The actual value stored in %al (i.e lsb of eax) shows -3. I am assuming here that al is using range -127 to +127 and not 0 to 255.

I am using gdb to check the register values.

So wanted to know if the operands are by default signed in IA32 architecture.

No correct solution

OTHER TIPS

In assembler, you only discern between signed values and unsigned values when you make decisions (i.e. conditional jumps). The ALU operates the same way for signed integers and unsigned integers. Some ALU flags, like OF (overflow) make sense if operands and result are interpreted as signed numbers. Others, like CF (carry) make sense if they are interpreted as unsigned numbers. Finally, others like ZF (zero) can be used with both interpretations.

For example: let's say you are using 8-bit registers (just for the sake of simplicity) and you come up with this code:

mov $0xff,%al
mov $0x01,%bl
add %bl,%al

This sequence can be interpreted two ways (remember that both operands and result are 8 bits wide):

  • Add -1 + 1, with result 0
  • Add 255 + 1, with result 0

How you interpret this depends on the decisions you make upon the result of this operation. Imagine that you want to check whether this operation worked ok, and there wasn't any kind of overrun with the result.

If you consider that your values are unsigned, then you added 255 plus 1, resulting in a number (256) which cannot be store in 8 bits, hence there is overrun and the actual result is not what you expected (0). You have to look at the carry flag to check for this:

jc .operation_overrun

This particular example will make this jump to be taken, because there was an overrun condition.

But if you consider that your values are signed, then you added -1 plus 1, which equals 0. To check for overruns here, you have to look at the overflow flag:

jo .operation_overrun

This particular example will make this jump to not to be taken, because there wasn't an overrun.


Another example: imagine that you compare two numbers held in two 32-bit registers, %eax and %ebx. You want to jump to another place if the number in %eax is greater than the number in %ebx. How do you do this? You must decide how you will interpret your numbers, because if you may come up with, for example, EAX=0xFFFFFFFF and EBX=0x00000001 ...

;...If both numbers are considered to be unsigned...
cmp %ebx,%eax  ;compare EAX with EBX
ja .another_place   ; ...this will jump because EAX=4294967295, 
                    ; which way much greater than EBX=1

;...But if both numbers are considered to be signed...
cmp %ebx,%eax  ;compare EAX with EBX
jg .another_place   ; ...this will NOT jump because EAX=-1, 
                    ; which is not greater than EBX=1

Once again, the operation (comparison, actually a substraction) is the same in these two cases, but the decision we make (the flags we look at when we perform a conditional jump) is different for each case. The first jump, ja, looked at the value of the carry flag (CF) and zero flag (ZF). The second jump, jg looked at the value of the sign flag (SF) and the overflow flag (OF).

For a detailed list of which flags are used for each particular conditional jump, see http://www.unixwiz.net/techtips/x86-jumps.html

It is merely a matter of interpretation. The bit pattern for an 8 bit value 253 and -3 is identical. When performing operations it is similarly a matter of interpretation, the resulting bit pattern of say 1 + 253 (254) and 1 - 3 (-2) are similarly identical.

Whatever is displaying -3 for al (your debugger perhaps) is only doing so perhaps because the default interpretation in the absence of any other information is signed. You have to tell it to interpret as unsigned if that is what you want.

Registers contain bits, and thats it. Its the instructions that massage and interpret those bits that determine the meaning. Don't be confused by the output produced by your compiler, printing, or other debugging routines.

Lets say you're using al to store a 16 bit signed integer in twos-complement arithmetic and you want to cast that to a 32-bit signed integer. You have to perform sign extension on the 16-bit value in order to convert it to a 32-bit signed integer.

For instance, the 8-bit value 1111 1110 is -2 in twos complement. The 16-value 0000 0000 1111 1110 is not -2, instead it is 254. If we had performed sign extension when switching from 8-bit to 16-bit, we would have gone from 1111 1110 (8-bit's version of -2) to 1111 1111 1111 1110 (16-bit's version of -2).

Remember how I said that these are just bits in a register and it depends on the instructions that you use that give them meaning? In the above example you'd use the instruction cbw to convert from twos-complement 8-bit to twos-complement 16-bit. From wikipedia, how to convert:

using the instructions cbw, cwd, cwde, and cdq: convert byte to word, word to doubleword, word to extended doubleword, and doubleword to quadword, respectively (in the x86 context a byte has 8 bits, a word 16 bits, a doubleword and extended doubleword 32 bits, and a quadword 64 bits);

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top