-1

I have a program that can add two arrays of binary numbers

I'm sorry, I'm not very good at asm

I can draw the conclusion myself, but to understand how to convert an array to a number, I don’t really understand how to do it

example:

section .text
global _start
_start:
    mov esi, 0
    mov ecx, len_arr_1

_add_two_array:

    mov eax, 0
    add al, [array_1 + esi]
    add al, [array_2 + esi]
    inc esi
    mov [res + esi], ax
    loop _add_two_array

mov esi, len_res - 1

_check:
    Call update

    dec esi
    cmp esi, -1
    jne _check
    jmp output_set

update:
    mov ebx, 2
    cmp [res + esi], bl 
    jge return

    ret
    
return:

    sub [res + esi], bl
    mov ebx, 1
    add [res + esi - 1], bl
    ret

output_set:
    mov esi, 0
    mov ecx, len_res

output:

    push ecx
    
    mov ecx, [res + esi]
    inc esi
    add ecx, 48
    mov [msg], ecx
    mov edx, msg_len
    mov ecx, msg
    mov ebx, 1
    mov eax, 4
    int 0x80

pop ecx

    loop output

exit:
    nop
    mov eax, 1
    mov ebx, 0
    int 80H

section .data

array_1 db 1, 1, 0
len_arr_1 equ $ - array_1

array_2 db 0, 1, 1
len_arr_2 equ $ - array_2

res db 0, 0, 0, 0
len_res equ $ - res

msg db '', 0xa
msg_len equ $ - msg
```

output: 1010 // need 10

how to display the resulting answer by converting it to a decimal number?

Cthulhu
  • 39
  • 2
  • 5
  • 1
    After converting your array of base2 digits to a binary integer in a register (i.e. packing the bits, e.g. with shift/or), use [How do I print an integer in Assembly Level Programming without printf from the c library?](https://stackoverflow.com/a/46301894). At least I assume that's what the problem is; you don't show what format you end up with from addition, just its inputs. I'm assuming you have the same format as `array_1` and 2, a big-endian(?) array of base-2 digits? Not really a [mcve], and [no attempt at this homework](https://meta.stackoverflow.com/q/334822/224132) – Peter Cordes Apr 29 '22 at 00:34
  • Your current code is weird since it does a 4-byte load from somewhere, and a 4-byte store to some temporary location, but only tells the `write` system call to copy one of those bytes to stdout. Also inconveniently using ECX as a loop counter even though you need it for something else inside the loop. – Peter Cordes Apr 29 '22 at 00:37
  • If you were doing this for real (not homework where you probably only want to use scalar instructions), you'd use SSE2 `movq xmm0, [array]` / `pslld xmm0, 7` / `pmovmskb eax, xmm0` to pack bytes down to bits, keeping the low bit of each byte. Maybe with some masking, shifting, or padding if the array isn't 4, 8, or 16 bytes. That would give you least-significant-first, i.e. little-endian, otherwise you'd need to byte-reverse with pshufb or maybe scalar `bswap` without SSSE3. – Peter Cordes Apr 29 '22 at 00:40
  • I'm sorry, but I don't even understand half of what you mean. : – Cthulhu Apr 29 '22 at 00:49
  • The actual output of this program is `1001`, not `1010`. Turns out it is using big-endian order for the digits, MSD at the lowest address (with a really cumbersome 2-pass addition that first adds bits in their respective bytes, then loops again to propagate carry). – Peter Cordes Apr 29 '22 at 01:05
  • 1
    So yes, your base-2 printing order is correct, so yeah just `total = total*2 + digit` (using a left shift or LEA of course) while looping forward will give you an integer in a register, as in [NASM Assembly convert input to integer?](https://stackoverflow.com/a/49548057) but your digits are binary not ASCII so that part can be simplified out. And you have an explicit length. You can then convert it to an ASCII string using the answer I linked in the first comment. – Peter Cordes Apr 29 '22 at 01:05

1 Answers1

2

You throw un-commented asm code at us without any description what with what and how it does or does not. Most of us will not bother to look further.

You should clarify for example what your array represents Is it bigint or something else? like fixed point, floating point ... Is it MSB first or last?

Here few things I noticed just by quick look without any deeper analysis (Assuming unsigned "big" int):

  1. I do not see any carry propagation during long num addition

    Only the first addition (lowest bit/word) should use add and all others should be adc !!! or all are adc if you clear carry flag before addition loop.

  2. your input is array of BYTEs and output is array of WORDs ?

    That makes no sense and even if it is then you inconsistently update esi as you use it for both BYTEs and WORDs at the same time while incrementing only by 1. If not then your target digits overlap as you write ax so depending on MSB first/last order you might overwrite already computed digits...

  3. Your test case is wrong due to previous bullets !!!

    You know 110b + 011b = 1001b however without the carry propagation from #1 you got 101b wrongly interpreted due #2 as 1010b ...

  4. You never convert binary to decadic

    Now you should convert base which you do not do anywhere. For integers That is done by dividing the number by printing base (10) and printing the remainders in reverse order. So if your number is not too big convert your result array to single register value (using bitshifts and or) and then convert to decadic string...

    1001b -> 1<<3 | 0<<2 | 0<<1 | 1<<0 = 9 // conversion to single register
    9 / 10 = 0 + remainder 9 -> "9" // division
    

    If the result would be 1010b it would be:

    1001b -> 1<<3 | 0<<2 | 1<<1 | 0<<0 = 10 // conversion to single 
    10 / 10 = 1 + remainder 0 -> "0"  // division
    1  / 10 = 0 + remainder 1 -> "10" // division
    

    If the number is for examle 123:

    123/10 = 12 remainder 3 -> "3"
    12 /10 =  1 remainder 2 -> "23"
    1  /10 =  0 remainder 1 -> "123"
    

    In case your array is big (does not fit into single register) you have to implement bignum division too ...

You are failing in many things at once !!! You should focus on single stuff/task not all bugs together ... I would start with printing some register hardcoded value in binary first (ignore rest of code), then coumulate array to such value, and only then try addition...

Here is my old lib for printing numbers to strings in NASM I wrote decades ago (just digged it out of my archive so you have some inspiration):

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; txtnum: ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
txtnum:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;.hex16     num ax >> string hex16 [cs:si], si=end of string adress at zero
;.dec16     num ax >> string dec16 [cs:si], si=end of string adress at zero
;.dec32     num eax >> string dec32 [cs:si], si=end of string adress at zero
;.txt32     num eax << string dec32 [cs:si], si=end of string adress after zero
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
.hex16  pusha
    mov cx,4
.hex16l:rol ax,4
    mov bl,al
    and bl,15
    add bl,'0'
    cmp bl,'9'
    jbe .hex16r
    add bl,'A'-'0'-10
.hex16r:mov [cs:si],bl
    inc si
    loop    .hex16l
    mov [cs:si],cl
    popa
    add si,4
    ret
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
.dec16: push    ax
    push    bx
    push    dx
    mov bx,10
    inc si
    cmp ax,10
    jb  .dec16r
    inc si
    cmp ax,100
    jb  .dec16r
    inc si
    cmp ax,1000
    jb  .dec16r
    inc si
    cmp ax,10000
    jb  .dec16r
    inc si
.dec16r:mov [cs:si],bh
    push    si
.dec16l:xor dx,dx
    div bx
    add dl,'0'
    dec si
    mov [cs:si],dl
    cmp ax,0
    jnz .dec16l
    pop si
    pop dx
    pop bx
    pop ax
    ret
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
.dec32: push    eax
    push    ebx
    push    edx
    mov ebx,10
    inc si
    cmp eax,10
    jb  .dec32r
    inc si
    cmp eax,100
    jb  .dec32r
    inc si
    cmp eax,1000
    jb  .dec32r
    inc si
    cmp eax,10000
    jb  .dec32r
    inc si
    cmp eax,100000
    jb  .dec32r
    inc si
    cmp eax,1000000
    jb  .dec32r
    inc si
    cmp eax,10000000
    jb  .dec32r
    inc si
    cmp eax,100000000
    jb  .dec32r
    inc si
    cmp eax,1000000000
    jb  .dec32r
    inc si
.dec32r:mov [cs:si],bh
    push    si
.dec32l:xor edx,edx
    div ebx
    add dl,'0'
    dec si
    mov [cs:si],dl
    cmp eax,0
    jnz .dec32l
    pop si
    pop edx
    pop ebx
    pop eax
    ret
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
.txt32: push    ebx
    push    ecx
    push    edx
    mov eax,0
    mov ebx,10
    mov ecx,0
.txt32l:mov cl,[cs:si]
    inc si
    or  cl,cl
    jz  .txt32e
    cmp cl,'0'
    jb  .txt32e
    cmp cl,'9'
    ja  .txt32e
    sub cl,'0'
    mul ebx
    add eax,ecx
    jmp .txt32l
.txt32e:or  edx,edx
    jz  .txt32x
    mov eax,-1
.txt32x:pop edx
    pop ecx
    pop ebx
    ret
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; end. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Spektre
  • 49,595
  • 11
  • 110
  • 380
  • This code looked probably-wrong to me, too, so I single-stepped it. [I commented yesterday](https://stackoverflow.com/questions/72051460/output-array-of-binary-digits-as-a-decimal-number-in-nasm/72057554#comment127311995_72051460). Carry-propagation is big-endian, done separately after the add loop (which could have just been a dword `add` :P), in a very cumbersome way with a function call inside the `_check:` `Call update` / ... loop that goes backwards. If it didn't do carry propagation, it would print `202`. – Peter Cordes Apr 30 '22 at 04:41
  • 1
    The only actual correctness bug in the addition is writing past the end of `res`, stepping on the `0xa` after the 0 bytes reserved by `db ''` because it stores AX instead of AL. (At least for these inputs; they'd write the byte before `res` if there was carry-out.) But man is it ever ugly, in terms of loop structure, 2-pass design of the algorithm, and details like apparently they don't know `cmp byte [res + esi], 2`, instead putting a number into EBX to use BL to imply an operand-size. And thus using `sub` instead of `mov` to zero a byte that had carry to propagate. – Peter Cordes Apr 30 '22 at 04:45
  • [How do I print an integer in Assembly Level Programming without printf from the c library?](https://stackoverflow.com/a/46301894) is IMO a good example of how to do uint32_t -> ASCII decimal string in NASM for 64-bit or 32-bit, especially if you just want to feed it to a Linux `write` system call so you don't care where the first character ends up, as long as it's within your buffer and you know where it is when you're done. The OP is writing 32-bit code for Linux, so your code with something "weird" like a `[cs:si]` addressing mode might be a trickier starting point. – Peter Cordes Apr 30 '22 at 04:50