mov eax,[myint+4]Note that [myPtr] is int "0", while [myPtr+4] is int "1". This means int "i" is at [myPtr+4*i]. Amazingly, x86 actually supports this very expression! Which is good, because this style is very handy for accessing arrays. It even works if myPtr and i are variables stored in registers.
ret
myint:
dd 3
dd 7
(Try this in NetRun now!)
extern read_input
call read_input
mov rdx,myint ; pointer to start of array
mov eax,[rdx+4*rax] ; access array element rax
ret
myint:
dd 3
dd 7
(Try this in NetRun now!)
Memory (like real estate) in theory could be used by anybody for
anything at any time (the anarchist squatter's paradise!). Of
course, in practice, it works a lot better to set up rules by which you
can figure out what memory's yours, and what isn't (e.g., deeds,
leases, rental contracts). So a piece of memory can be:
The bottom line is you really need to claim memory before using it,
and then only use the part you claimed. It's easy to accidentally
run off the end of an array (owned by you, class 1 memory) into other
bytes of memory owned by some other part of the program (class 2
memory), or delete an array (so it's no longer owned by you) and use it
later, etc. Sadly, in C++ it's up to you the programmer to make
sure your uses of memory are correct, unlike Java or C# where pointers
aren't allowed and array indices are all carefully checked by the
compiler.
Anyway, there are a bunch of different ways for your code to legally claim some memory, including:
mov edi, 40; malloc's first (and only) parameter: number of bytes to allocateRather than copy via the ecx register, you can specify you want a 32-bit memory write and read using "DWORD" in front of the brackets, like this:
extern malloc
call malloc
; on return, rax points to our newly-allocated memory
mov ecx,7; set up a constant
mov [rax],ecx; write it into memory
mov edx,[rax]; read it back from memory
mov eax,edx; copy into return value register
ret
mov edi, 40; malloc's first (and only) parameter: number of bytes to allocate
extern malloc
call malloc
; on return, rax points to our newly-allocated memory
mov DWORD [rax],7; write constant into memory
mov eax,DWORD [rax]; read it back from memory
ret
mov edi,10 ; ten integers in our arrayTo allocate an array of n 64-bit "long" values, you just need to replace the "4" bytes/integer above with "8" bytes/long:
imul edi,4 ; multiply by 4 to get a byte count
extern malloc
call malloc
; rax is a pointer to the allocated space
mov rdi,10; n
mov rcx,0 ; i
jmp testloop
initloop:
mov DWORD[rax+4*rcx],ecx; write to integer at index rcx
add rcx,1 ; i++
testloop:
cmp rcx,rdi
jl initloop
mov eax,DWORD[rax+4*7] ; pull out the integer at index 7
ret
mov edi,10 ; ten longs in our array
imul edi,8 ; multiply by 8 to get a byte count
extern malloc
call malloc
; rax is a pointer to the allocated space
mov rdi,10; n
mov rcx,0 ; i
jmp testloop
initloop:
mov QWORD[rax+8*rcx],rcx; write to long at index rcx
add rcx,1 ; i++
testloop:
cmp rcx,rdi
jl initloop
mov rax,QWORD[rax+8*7] ; pull out the long at index 7
ret
Address |
Contents |
|
0x000...000 |
"low memory" |
|
unused stack area |
(you can claim this space) |
|
rsp-> |
end of reserved data |
"top of the stack" |
reserved stack data |
(main's variables) |
|
0xfff...fff |
"high memory" |
sub rsp,16 ; I claim the next sixteen bytes in the name of... me!Here's how we'd allocate one hundred long integers on the stack, then use just one of them:
mov QWORD [rsp],1492 ; store a long integer into our stack space
mov rax,QWORD [rsp] ; read our long from where we stored it
add rsp,16 ; Hand back the stack space
ret
sub rsp,800 ; I claim the next eight hundred bytesThese are handy if you've only got one integer to stick on or pull off the stack. In 32-bit mode, push and pop are really useful for function arguments, which by convention in 32-bit mode are stored on top of the stack when you call the function:
mov rdi,rsp ; points to the start of our 100-integer array
add rdi,320 ; jump down to integer 40 in the array
mov QWORD [rdi],1492 ; store an integer into our stack space
mov rax,QWORD [rdi] ; read our integer from where we stored it
add rsp,800 ; Hand back the stack space
ret
push 19
extern print_int
call print_int
pop eax ; MUST clean up the stack
ret
(Try this in NetRun now!) (32-bit mode)
This prints the "19" that's stored on top of the stack. In 32-bit mode, all function arguments are stored on the stack (unlike registers for 64-bit code). This means the stack is a rather funny mix of function arguments, local and temporary variables, totally unused space for alignment, etc.push rbp; stash old value of rbp on the stackrbp isn't used very often in 64-bit mode, but in 32-bit mode it's almost standard. The piece of the stack around the base pointer is often called the function's "stack frame": negative offsets get to the function's local variables, positive offsets get to the caller's parameters, and directly at rbp is the saved copy of the old rbp. This effectively makes a chain of rbp pointers (assuming every function uses the frame pointer correctly); on some machines you can "unwind the stack" or print a "stack trace" by following this chain of pointers.
mov rbp,rsp; rbp == stack pointer at start of function
sub rsp,1000 ; make some room on the stack
mov QWORD[rbp-4],7 ; local variables are at negative offsets from the base pointer
mov eax,QWORD[rbp-4]; same local variable
mov rsp,rbp; restore stack pointer (easier than figuring the correct "add"!)
pop rbp; restore rbp
ret
(Try this in NetRun now!)