I am writing a simple os in nasm and I am new to assembly. From c I am used to be able to declare pointers without reserving memory for them and moving them around as I please. How can I do this in nasm? If I declare a variable e.g. by
var: resb 1
I understand that I declare a pointer and can access the value of the variable e.g. by
mov eax, [var]
Though, I cannot move the pointer to another address by
mov var, 0x1234
then I get "invalid combination of opcode and operands". So how can I declare and move pointers in memory?
mov var, 0x1234
gives you an error becausevar
is not an addressing mode,[var]
would be but, nevertheless, you'd still get an error as NASM cannot tell the size of the operation.mov DWORD [var], 0x1234
would do (assuming your memory model uses 32-bit near pointers).x86 doesn't have indirect address moves, while in C you can use pointers "directly from memory" (roughly speaking), in assembly you have to first load the pointer into a register and then use that register as an address.
See for your self at Godbolt.
The deference operation takes an extra instruction, this may "looks like" as using two deferences but it is not (actually, in NASM, one could see the name of a variable as a pointer to it but lets keep things simple).
You can treat the pointer as any other variable (including copy it around) by simply copying it in a register and passing it around (the first
mov
in the example above does that).E.g.
Regarding reserving memory: any variable when stored in memory occupies some space, that's what makes the program state.
resb
is used for uninitialised data, this makes uses of the ELF features to save space in the compiled binary when on disk.If you are making your own OS you may not use ELF at all, thus
resb
(and similar) may simply fallback as allocating a zero-initialised var (NASM will warn about this).You can use the stack to temporarily store your vars if they have a limited scope; this will reuse the same space there by limiting the memory footprint.
Alternatively you can use
%define
to define assembly-level symbols, these are like (but not identical at all to) C#define
s.For example:
This won't allocate space for
MY_PTR_TO_SOMETHING
since it is just an alias for the number0x1234
. You can think of it as C#define MY_PTR_TO_SOMETHING ((int*)0x1234)
. Or likestatic const int *MY_PTR_TO_SOMETHING = (int*)0x1234;
with a compiler that does optimize away the actual static storage for the pointer object itself.Note however that a level of indirection is gone thanks to the fact that the pointer value is now available as a known constant.
Of course, you can still pass it around:
Working with pointers can be confusing if one isn't used to the x86 addressing modes. My advice is to read the instructions manual for
mov
and be sure to understand the difference betweenmov eax, 0x1234
andmov eax, DWORD [0x1234]
.If you need a pointer you can modify at runtime, you need some storage for the pointer value; it can't be an assemble-time constant or a label address.
In C, you'd use
int *ptr;
which would reserve space for a pointer-sized object either in static storage (at global scope) or in a register (or stack space) for local scope.