arraysloopsassemblyx86-16emu8086

8086 - loop over array using CX as index


I am writing .asm for 8086 using emu8086.

I want to loop over an array of bytes, using the loop instruction. Inside the loop I want to access the data in the array, in order (from the lowest memory address to the highest).

I can do it with the following code:

array               DB 9,1,4,7,2
length              DW 5

                    mov cx, length  ;cx <-- desired iterations
                    lea si, array   ;si <-- address of first array byte
my_loop:            mov al, [si]    ;load from array
                    
                    ;perform operations
                    ;on AL here
                    
                    inc si          ;si points to the next array element
                    loop my_loop

I am wondering if there is a better way to do it exploiting just one register, since CX and SI are basically doing the same job (the offset from the first array element stems naturally from the iteration count).

In this example at each iteration the values of CX and SI will be the following:

CX: [5, 4, 3, 2, 1]
SI: [array + 0, array + 1, array + 2, array + 3, array + 4]

Now at each iteration the value of SI is given by the formula si = addr(array) + length - cx where array and length are known at compile time.

Inside the loop this would look like (this syntax explains what I want to do but is most likely wrong):

mov al, offset(array) + length - cx

I am a newbie with addressing modes. Is there a way to avoid using SI altogether or can you explain why it's unavoidable to use it?


Solution

  • array               DB 9,1,4,7,2
    length              DW 5
    

    array and length are known at compile time.

    There's a good reason to not define length as a memory-based variable, but rather as an equate by writing: length equ $ - array.

    I want to loop over an array of bytes, using the loop instruction.

    If you insist on using loop, then you will have to use an additional register. On , the CX register can never be used in an addressing mode.

    I am a newbie with addressing modes, is there a way to avoid using SI altogether or can you explain why it's unavoidable to use it?

    The SI register is not unavoidable, since on you can replace it by BX, DI, and even BP. (BP is special because it uses the SS segment register by default, so don't use it as your first choice!)

    I am wondering if there is a better way to do it exploiting just one register

    You can quite easily write the loop using a single address register (one of BX, SI, DI, or BP). Below are some methods:

      xor  bx, bx           ; 0
    my_loop:
      mov  al, [array + bx]
      ...
      inc  bx               ; Increment the 'offset' BX
      cmp  bx, length       ; 5
      jb   my_loop          ; Loop for as long as BX below 5
    

    and:

      mov  bx, OFFSET array
    my_loop:
      mov  al, [bx]
      ...
      inc  bx               ; Increment the 'address' BX
      cmp  bx, OFFSET array + length
      jb   my_loop          ; Loop for as long as BX below (array + 5)
    

    and as suggested by @ecm and @PeterCordes (shaving off an instruction):

      mov  bx, -length
    my_loop:
      mov  al, [array + length + bx]
      ...
      inc  bx               ; Increment the 'offset' BX
      jnz  my_loop          ; Loop for as long as BX negative [-5,-1]
    

    Inside the loop I want to access the data in the array, in order (from the lowest memory address to the highest).

    That's a pity (but not so much), because if it were allowable for the task at hand, you could reverse the direction and shave off an instruction:

      mov  bx, length - 1   ; 5 - 1
    my_loop:
      mov  al, [array + bx]
      ...
      dec  bx               ; Decrement the 'offset' BX
      jge  my_loop          ; Loop for as long as BX positive [0,4]