JamesM and everyone here is right. I would like to add to what is already stated though:
1) You indeed do need to write everything yourself, including malloc, free, memset and anything else you would want to use (unless you take someone else's code and drop it in your OS). Your very first form of memory allocation is usually an integer, starting at value 'end_of_your_kernel' that gets returned and then gets 'size_of_memory_you_wish_to_allocate' added to it. However, since you will eventually run out of memory, you will need something that manages blocks of memory and allocations internally, this is what is commonly referred to as
the heap (see
JamesM's tutorials or the
Writing a memory manager article on the wiki for an explanation).
2) As James said, constructors and destructors of objects in C++ are already called inside a normal function, BUT this does not include global variables (as stated on the wiki). Global variables can be created, but they will not be initialized, so in a bare C++ OS with no actual support yet, the following will happen:
Code: Select all
/* This variable will probably not be 5000 when you enter your entry point. */
int MyGlobalVar = 5000;
/* Random function. */
void foo()
{
/* This variable is locally declared and allocated on the stack, as JamesM said. It should have value '2000' as expected. */
int SomeVar = 2000;
}
int main()
{
foo();
}
The global variable will most likely not have the expected value, it WILL exist however, so a first solution to your problem could be something similar to the following:
Code: Select all
int MyGlobalVar;
void InitializeGlobalVars()
{
MyGlobalVar = 2000;
/* ... */
}
int main()
{
InitializeGlobalVars();
}
However, this is redundant, and forces you to initialize every global variable similarly in that function. The second (and in my opinion, the best) solution is to write a loop that does all the initialization. The wiki page '
C PlusPlus bare bones' explains how to do this in the 'loader.s' section. You can loop the constructors in either your C++ code or Assembly (Assembly is probably the best choice). The thing about this solution is: you add the code once, you never look at it again (unless it's causing problems, of course).
There's also discussion about whether it's needed to loop the destructors as well. Some say you should, others share the opinion "why would I call destructors of global objects in my OS if my OS is shutting down anyway?". Here's my constructor code, it might serve as an example if you need it:
Code: Select all
; Note that these are declared in the linker script with the same names.
[EXTERN LdConstrStart]
[EXTERN LdConstrEnd]
ExecConstr:
push ebx ; Preserve EBX contents.
mov ebx, LdConstrStart ; Load the first constructor.
jmp .Compare ; Jump to the compare label.
.Body: ; This is the body, it calls the constructor and increments the 'pointer'.
call [ebx] ; Call the constructor.
add ebx, 4 ; Move to the next one.
.Compare: ; This checks if we've reached the end of the constructor 'list'.
cmp ebx, LdConstrEnd ; Check if the constructor inside ebx is the last constructor.
jb .Body ; If it isn't, go back to Body.
pop ebx ; Return EBX to its old state.
ret ; Return, we're done here.
Hope this 'summary' was of any help to you (others feel free to correct my mistakes).
When the chance of succeeding is 99%, there is still a 50% chance of that success happening.