2

I am supposed to take signed integers from a user, calculate the sum of inputted numbers and then display the average. The problem is, negative numbers don't seem to be displaying correctly, although I know the sum and average are being calculated correctly.

What do I need to add to my procedure to account for negative numbers so they are displayed correctly?

.
.
.
writeVal PROC   USES eax
    LOCAL   resultString[11]:BYTE
    lea     eax, resultString
    push    eax
    push    [ebp + 8]
    call    intToStr
    lea     eax, resultString
    displayString eax ; print num
    ret     4

writeVal ENDP


intToStr PROC       USES eax ebx ecx
    LOCAL   tempChar:DWORD

    mov     eax, [ebp + 8]
    mov     ebx, 10
    mov     ecx, 0
    cld

divideByTen:
    cdq
    idiv    ebx
    push    edx
    inc     ecx
    cmp     eax, 0
    jne     divideByTen
    mov     edi, [ebp + 12] ; move into dest array
    jmp     storeChar

;store char in array

storeChar:
    pop     tempChar
    mov     al, BYTE PTR tempChar
    add     al, 48
    stosb
    loop    storeChar
    mov     al, 0
    stosb
    ret     8

intToStr ENDP
.
.
.
Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
halpme
  • 17
  • 3

2 Answers2

2

If the input is negative, record that fact somewhere (in a register) and neg your input number. (e.g. test eax,eax / jnl to jump over a block if it's not less-than-0, otherwise fall into the block that handles that case.)

Then do unsigned int -> string conversion (using div not idiv) the normal way into the buffer.

If you use a push/pop strategy for reversing digits, you can put a '-' into the buffer and increment your pointer right away. Otherwise, wait until the end to prepend a '-' if you're storing digits in least-signficant first order starting at the end of a buffer and decrementing a pointer (like in How do I print an integer in Assembly Level Programming without printf from the c library?)

Using unsigned handles the corner case of the most-negative integer -2147483648, bit-pattern 0x8000000. Its absolute value can't be represented as a 32-bit 2's complement positive integer, only as 32-bit unsigned.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
2

You can simply check if the number is less than zero and then use neg instruction to negate it and apply the negative sign - to the resultString buffer:

Code for writeVal will be:

writeVal PROC USES eax ecx edi
    LOCAL   resultString[11]:BYTE
    lea     eax, resultString

    mov     ecx, [ebp + 8]          ; load passed number to ebx
    test    ecx, ecx                ; test number to see if it's less than zero
    jnl     non_negative            ; jump if not less to non_negative

    neg     ecx                     ; else we have a negative number so neg to make it positive
    mov     byte ptr [eax], '-'     ; set resultString[0] to '-'
    inc     eax                     ; increase resultString ptr + 1

    non_negative:

    push    eax                  ; push the resultString + 1
    push    ecx                  ; push the number
    call    intToStr             ; convert the number
    lea     eax, resultString
    printc  eax                  ; print num
    ret     4
writeVal ENDP

Compile and run:

start:

    push -14286754
    call writeVal

    exit

end start

Will print:

-14286754
Christos Lytras
  • 36,310
  • 4
  • 80
  • 113
  • you're over-complicating your if body with redoing the LEA and using stos. Just `mov byte ptr [eax], '-'` ; `inc eax` before or after `neg ebx`, like I suggested in my answer. But use ECX or EDX instead of EBX; EBX is call-preserved in the standard calling conventions. (So is EDI). Don't modify them without saving/restoring them. – Peter Cordes Mar 16 '20 at 02:38
  • @PeterCordes done, thanks. I have more than 15 years to write `MASM` and important details like these are blured! I came up with this before I saw your answer. I wouldn't anser if I've seen it. – Christos Lytras Mar 16 '20 at 02:48
  • 1
    Thanks for actually writing up some code in detail. I know real examples are helpful to beginners but I sometimes skip that level of effort / detail for stuff that seems trivial enough to picture the code in my head. Do note that if you're calling the OP's `intToStr` function I this wrapper won't correctly handle `0x8000000` which is "still negative" after `neg` when that function treats is as signed. – Peter Cordes Mar 16 '20 at 02:54
  • Why `neg` can't handle `0x80000000`? I actually pushed `-2143123456` which is `0x80428800` and the program printed `-2143123456`. And with `-2147483647` (`0x80000001`) still prints `-2147483647`. I compile it using old `ml`, `Microsoft (R) Macro Assembler Version 6.14.8444`. – Christos Lytras Mar 16 '20 at 03:06
  • 1
    `neg` does handle it "correctly", but the most-negative number is a special case for 2's complement. https://en.wikipedia.org/wiki/Two%27s_complement#Most_negative_number. e.g. `-128` is its own 2's complement inverse in 8-bit 2's complement. i.e. it overflows back to -128 as signed. So you should treat the result as unsigned in this case. Which you aren't doing if you pass it to a function that does signed division by 10 with cdq/idiv. `xor edx,edx` / `div` would be fine, or even `xor edx,edx`/`idiv` to zero-extend and then do 64/32 => 32-bit signed division with 2 non-negative inputs. – Peter Cordes Mar 16 '20 at 03:11