3

I want to initialize an array in assembly with specific values. I tried doing it in a loop first but got junk in the arrays. I then tried doing it manually and got the same junk. I want the array to repeat 0 1 2 for an n amount of times. Here is some example code I tried.

This is my attempt at manully loading the array. The first value loads just fine. The second value however, loads in junk when I examine it in GDB.

sub esp, 260
mov [ebp - 12], dword -1
mov [ebp - 16], byte 0
mov [ebp - 17], byte 1
mov [ebp - 18], byte 2
mov [ebp - 19], byte 0
mov [ebp - 20], byte 1
mov [ebp - 21], byte 2
mov [ebp - 22], byte 0
mov [ebp - 23], byte 1
mov [ebp - 24], byte 2
mov [ebp - 25], byte 0

Here was my attempt at doing it automatically.

    sub esp, 260
    mov [ebp - 12], dword -1

again:
    add [ebp - 12], dword 1
    lea eax, [ebp - 16]
    sub eax, [ebp - 12]
    mov [eax], byte 0

    add [ebp - 12], dword 1
    lea eax, [ebp - 16]
    sub eax, [ebp - 12]
    mov [eax], byte 1

    add [ebp - 12], dword 1
    lea eax, [ebp - 16]
    sub eax, [ebp - 12]
    mov [eax], byte 2

    cmp [ebp - 12], dword 255
    jne again
    jmp elsewhere

Using NASM, x86-32, Intel syntax.

EDIT: When I convert this code to store the array values as DWORDs instead of bytes both methods work. Why is that?

Michael Petch
  • 46,082
  • 8
  • 107
  • 198
frillybob
  • 600
  • 2
  • 12
  • 26
  • A tip: write what you want to do in C and set compiler to output assembly, look at what the compiler generated and adapt that to your needs. – slashmais Apr 15 '17 at 20:20
  • It does something very similar to my manual way. I will attempt emulating what it does but I don't see how mine is any different. – frillybob Apr 15 '17 at 20:29
  • I can't copy the assembly line for line because it won't compile for some reason. But I get the same junk regardless. EDIT When I convert this to storing dwords instead of bytes it works either way. Why is that? – frillybob Apr 15 '17 at 20:38
  • To clear up confusion I assume your question is how you can initialize an array that is allocated on the stack? – Michael Petch Apr 16 '17 at 17:15

5 Answers5

1

With NASM, you can easily initialise repeating data by using the times prefix. For instance, to repeat the sequence "0 1 2" n times as requested in your question, you can do something similar to the following:

section .data

    my_array: times n db 0, 1, 2 

Simply replace n by the constant value you want. More information about the times prefix can be found in the NASM Manual.

Pyves
  • 6,333
  • 7
  • 41
  • 59
  • In this case the OPs array is on the stack so what you are suggesting doesn't apply. – Michael Petch Apr 16 '17 at 16:23
  • @MichaelPetch: well actually, if I'm not mistaken, I don't think the OP states that an array on the stack is required, he just wants to "initialize an array". Even if his attempt relies on the stack, he may want to do things differently and go with the simple approach suggested by my answer. ;-) – Pyves Apr 16 '17 at 16:40
  • 2
    I realize what he said in his question and never mentioned the stack, BUT If you look at his code you'll see he allocates a chuck of the stack for the array and then attempts to fill it in. Your answer doesn't apply to the stack because it has no fixed location in memory that you can use the `times` directive on. Initializing arrays on the stack require a programmatic approach. – Michael Petch Apr 16 '17 at 17:11
1

Assuming you want your array of bytes to occupy the stackspace from [EBP-260] to [EBP-16], it will hold 260 - 16 + 1 = 245 bytes. Looking at your examples I see that the last element is a 0. Considering that the number of elements is not divisable by 3, I can work out that the first element needs to be a 1.

(1, 0) followed by 81 times (2, 1, 0)

Taking the first two elements out of the loop creates an opportunity to write an efficient solution with a couple of nested loops:

  push ebp
  mov  ebp, esp
  sub  esp, 260

  ; Initializing the array with 245 bytes (2 + 81 * 3)
  mov  edx, esp
  mov  eax, 1
  mov  [edx], ax         ; (1, 0)
  inc  eax               ; 1 -> 2
  add  edx, eax
  mov  ecx, 81           ; 81 triplets (2, 1, 0)
Outer:
Inner:
  mov  [edx], al
  inc  edx
  dec  eax               ; 2 -> 1 -> 0 -> -1
  jns  Inner
  add  eax, 3            ; -1 -> 2
  dec  ecx
  jnz  Outer
  mov  byte [edx], -1    ; (*)

But we can do much better if we write 32 bits at a time. The array in memory presents itself as:

| EBP-260 == ESP                                      | EBP-20    | EBP-16
v                                                     v           v
1, 0, 2, 1, 0, 2, 1, 0, 2, 1, 0, 2, 1, 0, 2, 1, ... , 1, 0, 2, 1, 0
\--------/  \--------/  \--------/  ^               ^ \--------/  -
    EAX         EBX         ECX     | repeats       |     EAX     BL
                                    | from here on  |     left-overs

In the 245-byte array, these 3 dwords (12 bytes) can repeat 20 times. And because the address where the array starts (ESP) is dword aligned, I'll put the repetitions at the low end.

  push ebp
  mov  ebp, esp
  sub  esp, 260
  mov  byte [ebp-15], -1 ; (*)

  ; Initializing the array with 245 bytes (20 * 12 + 5)

  mov  eax, 01020001h    ; (1, 0, 2, 1)
  mov  ebx, 00010200h    ; (0, 2, 1, 0)
  mov  ecx, 02000102h    ; (2, 1, 0, 2)
  lea  edx, [ebp-32]
  mov  [ebp-16], bl
  mov  [ebp-20], eax
More:
  mov  [edx+8], ecx
  mov  [edx+4], ebx
  mov  [edx], eax
  sub  edx, 12
  cmp  edx, esp
  jae  More

(*) If the intended use of this -1 is to terminate the list, then having a byte -1 at [ebp-15] makes more sense than a dword -1 at [ebp-12].

Sep Roland
  • 33,889
  • 7
  • 43
  • 76
  • 1
    compact and pretty nice (although I'd use EDX as a pointer and leave EBP alone), but IDK about "efficient". The repeating pattern is 3 bytes long, and you can conveniently store 4 bytes at a time. Two options are unaligned and overlapping dword stores (3 useful bytes per iteration), or unroll the pattern to LCM(3,4) = 12 bytes = 3 registers (or 3 `mov`-immediate), with this as cleanup for non-multiple-of-12. Or just peel/unroll the cleanup as a couple maybe-overlapping `mov` immediate, or in this case 4-byte + 1-byte mov. – Peter Cordes Jan 12 '21 at 03:33
  • Or with `fild`/`fist` or MMX store 8 bytes at a time, like maybe 8/7 overlapping stores to do a 5x 3 pattern (or SSE movups). Or mix 8 and 4 byte aligned stores to do groups of 12 with 2 instead of 3 stores. Or unroll to LCM(3,8) or LCM(3,16) = 48 to use aligned movaps. – Peter Cordes Jan 12 '21 at 03:35
  • @PeterCordes Would you believe I was going to add this already? As soon as I had logged out yesterday I came up with the idea to store 3 unique dwords and repeat them. Where I wrote "efficient", I was mainly thinking about how the other answers managed to use **division** to solve this simple task. That was also the only reason why I took it on me to answer this 4 year old question. – Sep Roland Jan 13 '21 at 19:47
  • 1
    Yeah, surprised nobody even mentioned a better solution at the time, not even a down-counter like [16-bit FizzBuzz in x86 NASM assembly](https://codereview.stackexchange.com/a/56896). Pretty sure I've seen Q&As before with a repeating 3-byte pattern (probably of pixels), so this isn't new, but definitely worth at least mentioning, would +1 again. (BTW, English usage note: "[took it upon myself](https://www.ldoceonline.com/dictionary/take-it-upon-on-yourself-to-do-something)" is the idiom / phrase you're looking for.) – Peter Cordes Jan 13 '21 at 20:20
0

This may trigger more ideas about allocating bytes or dwords on the stack.

You may want to build a library of personal functions or macros that you may use often or find some other library to %include.

You can construe your array data in byte, dword, or other size as along as you treat all the data elements the same way. You have to always be mindful of the array element size (e.g., 1 vs 4 bytes..). I'll leave any alignment issues up to you to resolve.

section .data
        _len equ 64
section .text
        %macro mod 2
        mov eax, %1
        mov ebx, %2
        xor edx, edx
        div ebx                 ; div/mul are very slow; but ok for small loops
        mov eax, edx            ; return eax if you wish
        %endmacro

        global _start
_start:
        nop

        sub esp, _len                   ; 

        mov ecx, 1
        L1:

        mod ecx, 3                      ; n mod 3 
        mov byte [esp+ecx], al

        inc ecx
        cmp ecx, _len                   ; array size
        jb L1

        add sp, _len

_exit:
        mov eax, 1
        mov ebx, 0
        int 0x80

GDB example x/64d $esp using next....

(gdb) 
0xffffd180:     0       1       2       0       1       2       0       1
0xffffd188:     2       0       1       2       0       1       2       0
0xffffd190:     1       2       0       1       2       0       1       2
0xffffd198:     0       1       2       0       1       2       0       1
0xffffd1a0:     2       0       1       2       0       1       2       0
0xffffd1a8:     1       2       0       1       2       0       1       2
0xffffd1b0:     0       1       2       0       1       2       0       1
0xffffd1b8:     2       0       1       2       0       1       0       0
31      v eax, 1
(gdb) 
0xffffd180:     0       1       2       0       1       2       0       1
0xffffd188:     2       0       1       2       0       1       2       0
0xffffd190:     1       2       0       1       2       0       1       2
0xffffd198:     0       1       2       0       1       2       0       1
0xffffd1a0:     2       0       1       2       0       1       2       0
0xffffd1a8:     1       2       0       1       2       0       1       2
0xffffd1b0:     0       1       2       0       1       2       0       1
0xffffd1b8:     2       0       1       2       0       1       0       0
25      cx, 3                   ; n mod 3 
(gdb) 
0xffffd180:     0       1       2       0       1       2       0       1
0xffffd188:     2       0       1       2       0       1       2       0
0xffffd190:     1       2       0       1       2       0       1       2
0xffffd198:     0       1       2       0       1       2       0       1
0xffffd1a0:     2       0       1       2       0       1       2       0
0xffffd1a8:     1       2       0       1       2       0       1       2
0xffffd1b0:     0       1       2       0       1       2       0       1
0xffffd1b8:     2       0       1       2       0       1       0       0
27      cx
(gdb) 
0xffffd180:     0       1       2       0       1       2       0       1
0xffffd188:     2       0       1       2       0       1       2       0
0xffffd190:     1       2       0       1       2       0       1       2
0xffffd198:     0       1       2       0       1       2       0       1
0xffffd1a0:     2       0       1       2       0       1       2       0
0xffffd1a8:     1       2       0       1       2       0       1       2
0xffffd1b0:     0       1       2       0       1       2 

  0   
InfinitelyManic
  • 760
  • 1
  • 6
  • 13
0

With gas (at&t syntax) you can use

array:
  .space size, value

you can also use .skip which is the same as .space.

Ilan Schemoul
  • 1,461
  • 12
  • 17
-1

Here is what i got:

;ecx = how much values  ;ds:edi = destination
function:

    pushad                    ;push all 32bit registers to stack

    xor eax,eax               ;clear eax

    function_loop:

        push eax              
        mov ebx,0x00000003    ;we divide by 3

        xor edx,edx           ;clear edx 

        div ebx               ;this stores the remainder of eax/0x03 in edx
        mov DWORD [edi],edx   ;move it to your array

        pop eax               ;we dont need the result of the division

        inc eax 
        add edi,4             ;increment pointer 4 bytes because 4 bytes are one DWORD

    loop function_loop        ;loop til ecx is 0

    popad                     ;pop all 32bit registers
ret

i wrote it out of my head so please report any bug with it

but the thing you have to do is increment a register and do a modulo operation with 3 on the incrementing register and then you have the 0,1,2 pattern

just be sure you have enough space on the desitnation to store all n values

SeeSoftware
  • 531
  • 6
  • 17
  • 1
    `div edx` is almost always wrong since the _DIV_ instruction with a 32-bit operand divides an operand into the 64-bit value in EDX:EAX. So you'd be doing EDX:EAX/EDX which will in almost all likelihood raise a divide exception because the result(quotient) won't fit in a 32-bit register. – Michael Petch Apr 15 '17 at 22:41
  • Using `div` in the loop is incredibly slow. For a small constant repeat length, just unroll by 3 so it goes away. For a larger or runtime-variable pattern repeat count, use an up or down counter that you reset once per n iterations. (see [16-bit FizzBuzz in x86 NASM assembly](https://codereview.stackexchange.com/a/56896) / [FizzBuzz in assembly - segmentation fault](https://stackoverflow.com/a/37494090) and [x86-64 Assembly - Sum of multiples of 3 or 5](https://codereview.stackexchange.com/a/253716)) – Peter Cordes Jan 12 '21 at 03:42