You can create simple binaries using the gcc linker using the linker script. the key is the OUTPUT_FORMAT directive (binary):
//======================================== FILE: linker.ld //======================================== OUTPUT_FORMAT(binary) SECTIONS { .text : { *(.text) } .data : { *(.data) } .bss : { *(.bss) } } //========================================
I called the linker in the makefile as follows (whereas linker.ld is the linker script):
I compiled the object code with
to get rid of standard libraries that don't work in 16-bit mode.
for the MBR loader for handshake and loader, I used the following assembly code loaderMain.S gcc (loaderMain.o should be the first file passed to the linker, which is located at offset 0x0000, as you can see above). I used the -code16gcc directive to generate 16-bit code. However, the generated code will probably not work on older x86 machines, as I used incompatible ones (% esp, $ ebp, leave, etc.) that are available only for newer models.
//======================================== FILE: loaderEntry.S //======================================== .text .code16gcc // the entry point at 0x9000:0x0000 // this is where I did a far call to by the MBR .globl loaderMain // loader C entry function name declaration push %cs // initialize data segments with same value as code segment pop %ax // (I managed only tiny model so far ...) movw %ax, %ds movw %ax, %es movw %ax, %fs movw %ax, %gs movw %ax, %ss // initialize stack segment with same value as code segment movl $0xffff, %esp // initialize stack pointers with 0xffff (usage of extended (dword) offsets does not work, so we're stuck in tiny model) movl %esp, %ebp call loaderMain // call C entry function cli // halt the machine for the case the main function dares to return hlt //========================================
assembly code calls the character that was defined in the CMain.c language file loader. in order to generate code compatible with 16-bit code, you must declare the use of the 16-bit command installed before the first line of code in each C file you use. This can only be done with the AFAIK built-in command:
asm(".code16gcc\n"); // use 16bit real mode code set /* ... some C code .. */ // ... and here is the C entry code ... // void loaderMain() { uint cmdlen = 0; bool terminate = false; print(NL); print(NL); print("*** EOS LOADER has taken over control. ***\r\n\r\n"); print("Enter commands on the command line below.\r\n"); print("Command are executed by pressing the <ENTER> key.\r\n"); print("The command \'help\' shows a list of all EOS LOADER commands.\r\n"); print("HAVE FUN!\r\n"); print(NL); while (!terminate) { print("EOS:>"); cmdlen = readLine(); buffer[cmdlen] = '\0'; print(NL); terminate = command(); } shutdown(); }
So far I have managed to write simple C code - so far I have failed with C ++ code, and I have managed to create only a small memory model (this means that CS, SS, DS and ES are all the same). gcc uses only offsets as pointer addresses, so it seems difficult to overcome the timy memory model problem without additional assembler code. (Although I heard that some people dealt with this issue in gcc)
The calling convention is that the last argument is first pushed onto the stack, and it seems that all values โโare dword aligned. An example assembly code that can be invoked in .code16gcc C code is located below:
btw The declaration of the C function header is as follows:
Hope this was helpful somehow. Greetings.