Search code examples
assemblymasmirvine32signed-integer

how to print negative integer using SWORD PTR(assembly lang)


I'm learning x86 assembly language and have no idea how to print negative integer using Irvine32 library.

INCLUDE Irvine32.inc

.data

arry    SWORD   10, -20, 30

.code
main PROC
    mov     eax,    0
    mov     esi,    OFFSET arry
    mov     ax,     SWORD PTR [esi]
    add     esi,    TYPE arry
    add     ax,     SWORD PTR [esi]
    call    WriteInt
    exit
    ;call   DumpRegs
    

main ENDP
end main

The result I was expecting is -10 but the console printed +65526. I don't know how WriteInt functions works but seems like it recognizes the value in ax as unsigned value.

If I modify my code like this, then WriteInt function gives me proper result.(-10)

...
    mov     eax,    10
    sub     eax,    20
    call    WriteInt
...

I want to read data from memory and add or subtract integers. If I want to print as signed integer how can I?


Solution

  • movsx eax, SWORD PTR [esi] to sign-extend it into a dword register for WriteInt.

    WriteInt always looks at the whole 32 bits, and the 32-bit number you gave it (with bit-pattern 0x0000fff6), interpreted as 32-bit 2's complement, represents a positive number.

    If there was a WriteShort function, you could call it and it would look at bit #15 instead of bit #31 to decide if the number was negative, but there's no need for one when we can just sign-extend up to a full register.


    Notice the mov eax,0 you used before loading AX: you are explicitly zero-extending by doing that, as an inefficient way to emulate movzx eax, word ptr [esi]. (If you didn't do that, the high 16 bits of EAX would hold whatever garbage was there from the code that called main.)

    There are other ways to do 2's complement sign-extension, i.e. copy the sign-bit of a number to higher bits of the full register. For example, cwde after already loading into AX, or even shl eax, 16 / sar eax, 16. But MOVSX is the most efficient when you don't already have the narrow value in a register.

    Or with a 16-bit add result in AX, cwde is just a more efficient way to write movsx eax, ax.

    (Fun fact: CWDE is the 32-bit version of an instruction that dates back to 8086. Before 386, you could only sign-extend efficiently with your number in AL or AX, not from anywhere to any register.)


    To avoid the possibility of signed overflow in the add, you may want to movsx both inputs before add. e.g. this makes it possible to get 0x7fff + 0x7fff = 0x0000fffe, 65534.
    Not 0xfffffffe, aka -2, which you'd get if you sign-extended after a 16-bit add of two positive numbers overflowed to produce a negative result.

    (The sum or difference of two n-bit numbers takes at most n+1 bits to represent exactly, so widening at all makes overflow impossible. To overflow a 32-bit sum, you'd have to add about 2^16 words.)