1

The problem I am facing is trying to add all the bytes defined in
data db 0x00,0x10,0x20,0x30,0x40,0x50,0x60,0x70,0x80,0x90.
I want to average them, which means dividing the sum by 10.

%include "io.inc"

SECTION .text

global CMAIN

CMAIN:

mov ebp, esp                      ; for correct debugging
SUB AX, AX                        ; clear the register AX
MOV ECX, 0x000A                   ; set counter to 10
MOV ESI, 0                        ; set offset to 0

AW:                               ; Destination of Loop AW as long as counter != 0
                                  
ADD AL,[ds:data + ESI]            ; Add data in the data array into AL
ADD ESI, 1                        ; Increase ESI and move along the data array
LOOP AW                           ; Loop to label AW,CX=CX-1 Keep looping until CX= 0


SUB EDX, EDX                      ; Clear EDX
MOV CX, 0x000A                    ; Set the divider to 10 data
DIV CX                            ; Divide AX with CX, AX = AX / 10
MOV [ds:result], EAX              ; The result is stored into location pointed by result
PRINT_HEX 4, result               ; Display "result" in the output window
    ret                           ; end program

SECTION .data

data db 0x00,0x10,0x20,0x30,0x40,0x50,0x60,0x70,0x80,0x90
result db 0x0    

I have tried, and this is the closest I can get. Initially I used EAX instead of AL (I took the 8 least significant bits), and the values I got were very huge and not in the least want I expected.

I add all the values together but it overflows and results in a value of 14 instead of the correct value of 0x48. I am wondering if there is a way to add byte data without causing overflow? Or is this just the limitation of the assembly? Or I am dumb?

Sep Roland
  • 33,889
  • 7
  • 43
  • 76
  • 2
    You can either manually propagate carry, e.g. by `adc ah, 0` or extend the value first before using a 16 bit addition, e.g. `movzx dx, [data+esi]; add ax, dx` – Jester Dec 17 '22 at 13:08
  • 1
    This is NASM, so `data` doesn't magically imply an operand size. `movzx edx, byte [data + esi]` / `add eax,edx`. Just like you'd get from compiler output summing an array of `unsigned char` into an `int total`; look at compiler output on https://godbolt.org/ – Peter Cordes Dec 17 '22 at 19:49
  • The reason your values were huge when using `eax` instead of `al` was because loading any `e??` register with a value from memory will fetch four bytes in a row, starting at the memory address in brackets, and load it into your destination as though it were a 32-bit number. So for example if you did `mov eax,[data]` you would load `eax` with `0x30201000`. – puppydrum64 Dec 21 '22 at 14:37

1 Answers1

1
data db 0x00,0x10,0x20,0x30,0x40,0x50,0x60,0x70,0x80,0x90
result db 0x0

The array contains byte-sized values, so logically the average of these values will fit in the byte-sized result variable, and you can perform the calculation using the byte-sized division that divides AX by the number of elements.

Don't use the slow loop instruction! And since you already are using an offset in the array, use the value from the offset to control the loop:

  xor   ebx, ebx                      ; Offset
  xor   eax, eax                      ; Sum
AW:
  movzx ecx, byte [ds:data + ebx]     ; Load element
  add   eax, ecx
  inc   ebx
  cmp   ebx, 10
  jb    AW

Now perform the byte-sized division. You have a suitable divider (10) in the BL register:

  div   bl                            ; AX / BL
  mov   [ds:result], al

If the data were a bit more random, then you would appreciate some rounding for that average. Next methods work for any array with at most 255 byte-sized elements:

  • Before division: adding half of the denominator (BX) to the nominator (AX).

    mov   cx, bx
    shr   cx, 1
    add   ax, cx
    div   bl                            ; AX / BL
    mov   [ds:result], al
    
  • After division: incrementing the quotient (AL) if double of the remainder (AH) is greater than the denominator (BX).

    div   bl                            ; AX / BL
    movzx cx, ah
    shl   cx, 1
    cmp   cx, bx
    cmc
    adc   al, 0
    mov   [ds:result], al
    
Sep Roland
  • 33,889
  • 7
  • 43
  • 76
  • Just to explain why `loop` is slow: https://stackoverflow.com/questions/35742570/why-is-the-loop-instruction-slow-couldnt-intel-have-implemented-it-efficiently It's fine on a 16-bit x86 CPU but shouldn't be used on 32-bit or higher architectures. – puppydrum64 Dec 21 '22 at 14:38