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?

4
  • 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. Commented Apr 15, 2017 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. Commented Apr 15, 2017 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? Commented Apr 15, 2017 at 20:38
  • To clear up confusion I assume your question is how you can initialize an array that is allocated on the stack? Commented Apr 16, 2017 at 17:15

5 Answers 5

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.

Sign up to request clarification or add additional context in comments.

3 Comments

In this case the OPs array is on the stack so what you are suggesting doesn't apply.
@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. ;-)
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.
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].

4 Comments

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.
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.
@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.
Yeah, surprised nobody even mentioned a better solution at the time, not even a down-counter like 16-bit FizzBuzz in x86 NASM assembly. 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" is the idiom / phrase you're looking for.)
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 

Comments

0

With gas (at&t syntax) you can use

array: .space size, value 

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

Comments

-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

2 Comments

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.
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 / FizzBuzz in assembly - segmentation fault and x86-64 Assembly - Sum of multiples of 3 or 5)

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.