Reputation: 6547
I am trying to make my own operating system from scratch and am making my own boot loader. I have a function to print a string onto the screen.
Here is some code that I have:
ORG 0x7C00
BITS 16
mov si, msg
call Print
cli
hlt
Print:
lodsb
cmp al, 0
je Done
mov ah, 0Eh
mov bh, 0
int 10h
jmp Print
Done:
ret
msg db 'Hello World!', 0
times 510-($-$$) db 0
dw 0xAA55
This is then compiled with the following command:
nasm -f bin bootloader.asm -o bootloader.bin
The question is, how would I be able to access the print function within C? I know I have to use the extern keyword, but how would I compile this into a binary format file?
Upvotes: 3
Views: 1497
Reputation:
Basically you have to run gcc
with -ffreestanding
(don't link) and then link using ld
with the flags -static
, -nostdlib
.
Creating bootloader in C is not exactly good idea. I'd recommend you to get copy of GRUB and work on top of it. OSDEV wiki has explained this incredibly well.
To sum things up, whenever you'll try to create bootloader in C, use these to compile it:
$ gcc -m16 -c -g -Os -march=i686 -ffreestanding -Wall -Werror -I. -o bootloader.o bootloader.c
$ ld -static -T linker.ld -nostdlib --nmagic -o bootloader.elf bootloader.o
$ objcopy -O binary bootloader.elf bootloader.bin
Second thing, you can't use extern! You didn't set up stack, so C code will probably bail out pretty quickly. C compiler doesn't know in which format do you pass parameters to it, because your function doesn't follow any of usual conventions. Possible linker script:
ENTRY(main);
SECTIONS
{
. = 0x7C00;
.text : AT(0x7C00)
{
_text = .;
*(.text);
_text_end = .;
}
.data :
{
_data = .;
*(.bss);
*(.bss*);
*(.data);
*(.rodata*);
*(COMMON)
_data_end = .;
}
.sig : AT(0x7DFE)
{
SHORT(0xaa55);
}
/DISCARD/ :
{
*(.note*);
*(.iplt*);
*(.igot*);
*(.rel*);
*(.comment);
}
}
Also, GCC is by default emitting 32-bit code - you need to force it to generate 16-bit code using __asm__(".code16gcc\n")
or, as suggested in comments, pass -m16
parameter to compilers' commandline.
You can rewrite your function to C (to make it complain any of calling conventions) like so:
void print(const unsigned char * s){
while(*s){
__asm__ __volatile__ ("int $0x10" : : "a"(0x0E00 | *s), "b"(7));
s++;
}
}
And of course, right after .code16gcc
, you'd have to jump directly to your bootloader start: __asm__ ("jmpl $0, $main\n");
Upvotes: 4