I'm reading chapter 3.2.4 of pcasm-book.pdf (http://www.drpaulcarter.com/pcasm/). The NOT operation.
Quote
The NOT operation is a unary operation(i.e. it acts on one operand,
not two like binary operations such as AND).The NOT of a bit is the
opposite value of the bit as the truth table in Table 3.4 shows.
....
Note that the NOT finds the one's complement
I'm just messing around with the bitwise NOT looking at the output in the console, but the results are not what I expected.
For instance:
not 10 ; not 10 = 5
; 1010
; 0101 = 5
Now I'll list the actual code I compiled and show the output. I'll also comment on what I thought the result would be.
start:
mov eax, 10
not eax
print str$(eax) ; Thought the output would be 5
; 1010
; 0101 = 5
Output:
-11
start:
mov al, 1
not al
print str$(eax) ; Thought it would be 14
; 0001
; 1110 = 14
Output:
254
What am I doing wrong here?
The instruction affects all of the bits in the operand, not just the lower 4. Also, when you are working with AL and displaying the result as the value in EAX, you need to ensure that the upper 24 bits of EAX are zeroed.
Here is a small test piece so you can experiment with various instructions. This one uses the NOT mnemonic.
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
include \masm32\include\masm32rt.inc
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
comment * -----------------------------------------------------
Build this template with
"CONSOLE ASSEMBLE AND LINK"
----------------------------------------------------- *
.code
start:
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
call main
inkey
exit
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
main proc
LOCAL pbuf :DWORD
LOCAL buffer[64]:BYTE
mov pbuf, ptr$(buffer)
mov eax, 12345678 ; decimal number input
not eax ; operate on it
invoke dw2bin_ex,eax,pbuf ; convert it to ascii binary notation
print pbuf,13,10 ; display result to console
ret
main endp
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
end start
Result is,
11111111010000111001111010110001
Press any key to continue ...
[EDIT]
Thanks for the reply Michael. This output makes sense to me now (254) based on what you said. I can't say that for the other
thing though.
start:
xor eax, eax
mov al, 1
not al
print str$(eax) ; Thought it would be 14
; 0001
; 1110 = 14
Output:
254
Quote
One's complement
The second method is known as one's complement representation.The
one's complement of a number is found by reversing each bit in the number.
(Another way to look at it is that the new bit value is 1 - old_bit_value.) For
example, the one's complement of 00111000 (+56) is 11000111. In one's com-
plement notation, computing the one's complement is equivalent to nega-
tion.Thus, 11000111 is the representation for -56. Note that the sign bit
was automatically changed by one's complement and that as one would ex-
pect taking the one's complement twice yields the original number. As for
the first method, there are two representations of zero:
00000000(+0) and 1111111( 0).Arithmetic with one's complement numbers is
complicated.
According to the above, "computing the one's complement is
equivalent to negation."
"Thus, 11000111 is the representation for -56"
Lets compile this and look at the output.
start:
xor eax, eax
mov eax, 56
not eax
print str$(eax) ;// From what I've read above, I'm thinking the result will be -56 (negative fifty-six)
exit
Output:
-57
Why is it not -56?
Keeping these two things in mind:
Quote
Note that the NOT finds the one's complement.
Computing the one's complement is equivalent to negation.
Why is the output -57 and not -56 if computing the one's complement is equivalent to negation! *bangs head on wall*
Thanks hutch-- I'll mess with this and keep reading till I understand it better.
The x86 processors use a two's complement representation for negative numbers, so the conversion procedures expect a two's complement value instead of one's complement. With two's complement, to convert a number from positive to negative, or from negative to positive, you invert all bits and add one, and ignore any carry out of the most significant bit. Going through the steps starting with 56:
00111000 ; 56
11000111 ; not 56, result is -57 in two's complement
00000001 ; add 1
11001000 ; -56 in two's complement (-57+1 = -56)
You can verify that 11001000 represents -56 by adding 56 to it:
11001000 ; -56
00111000 ; add 56
00000000 ; result is zero, carry ignored
In case you are not familiar with the rules for binary addition:
0+0 = 0
0+1 = 1
1+0 = 1
1+1 = 0 carry 1
Thanks Micheal for walking me through that.
Quote from: MichaelWThe x86 processors use a two's complement representation for negative numbers, so the conversion procedures expect a two's complement value instead of one's complement.
I must have read about two's complement 10 times and still forgot about that. :red
Remembering that might have saved some frustration huh?
Thanks again for your explanation.
Hi!
I got very curious where did you read that about one's complement? I must admit that though I studied computer science I've never heard about the one's complement having 2 representations for zero (all bits cleard or set). Well, there are several number representations with different arithmetics. For x86 (but as I can remember for Commodore machines too) the arithmetic is exactly how MichaelW very well described.
Just a last thing that was not mentioned and could make the story whole:
The sum of a number and its one's complement give an odd number, for 8 bit numbers the sum will be 11111111b, generally for n bit numbers 2^n-1. (0101 + 1010 = 1111 <-> 5 + 10 = 15)
It is quite simple to conclude that the sum of a number and its 2-complement give an even number, for n bits 2^n. (0101 + 1011 = 1|0000 <-> 5 + 11 = 16).
Greets, Gábor