Page 1 of 1

C + Asm combination for an OS [x86]

Posted: Sun Jul 22, 2007 6:08 pm
by CompDever
I am attempting to create my OS with a combination of assembler and c, i have a good stand alone assembler bootloader but when i use this tutorial's method of combining, my text no longer displays...

The Assembly

Code: Select all

[BITS 16]
[global main]
; Main program
main:		; Label for the start of the main program

 mov ax,0x0000	; Setup the Data Segment register
		; Location of data is DS:Offset
 mov ds,ax	; This can not be loaded directly it has to be in two steps.
		; 'mov ds, 0x0000' will NOT work due to limitations on the CPU
 mov si, Intro
 call PutStr
 call PutNewLine
 mov si, HelloWorld	; Load the string into position for the procedure.
 call PutStr	; Call/start the procedure
 call PutNewLine
 call PutNewLine
 call PutNewLine
 call PutNewLine
 call PutNewLine
 mov si, Conclusion
 call PutStr

 jmp $	; Never ending loop

; Procedures
PutStr:		; Procedure label/start
 ; Set up the registers for the interrupt call
 mov ah,0x0E	; The function to display a chacter (teletype)
 mov bh,0x00	; Page number
 mov bl,0x07	; Normal text attribute

.nextchar	; Internal label (needed to loop round for the next character)
 lodsb		; I think of this as LOaD String Block 
		; (Not sure if thats the real meaning though)
		; Loads [SI] into AL and increases SI by one
 ; Check for end of string '0' 
 or al,al	; Sets the zero flag if al = 0 
		; (OR outputs 0's where there is a zero bit in the register)
 jz .return	; If the zero flag has been set go to the end of the procedure.
		; Zero flag gets set when an instruction returns 0 as the answer.
 int 0x10	; Run the BIOS video interrupt 
 jmp .nextchar	; Loop back round to the top
.return		; Label at the end to jump to when complete
 ret		; Return to main program

PutNewLine:
 mov si, newLine
 call PutStr
ret

;ExitSequence:
;inc cx
;cmp cx, 750
;je Quit
;jmp ExitSequence


; Data
Intro db ' OSTester: An Operating System Test',13,10,0
newLine db 13,10,0
HelloWorld db ' The Current Message Is As Follows: "Hello, World!"',13,10,0
Conclusion db ' Turn off the computer and remove the floppy disk to return to the normal OS',0

;Quit:
;do nothing

times 512-($-$$)-2 db 0
dw 0xAA55
this merely displays a few lines of text on the screen, works well stand-alone

The C Code:

Code: Select all

int main()
{
  return 0;
}

int k_main()
{
  int num;
  char ch;

  char *text_video = (char*)0xB8000;
  char attrib = 0x07;
  char *str="Kernel Loaded";

  while(*str!=0)
  {
    *text_video = *str;
    *text_video++;
    *text_video = attrib;
    *text_video++;
    *str++;
  }
  return;
}

void clear_screen(char clear_to, char attrib)
{
  char *text_video = (char*)0xB8000;
  char *str="Kernel Loaded";
  int pos=0;

  while(pos<(80*25*2))
  {
    *text_video = clear_to;
    *text_video++;
    *text_video = attrib;
    *str++;
    pos++;
  }
}
This code should be self-explanatory


The Batch File:

Code: Select all

@echo off
del "C:\asm\ostest.bin"
del "C:\asm\ostest.img"
del "C:\asm\kernel.o"

echo Compiling assembly
echo          nasm
C:\asm\nasm.exe -E C:\asm\errors.txt C:\asm\*.asm -f aout -o C:\asm\ostest.bin
echo          gcc
C:\djgpp\bin\gcc.exe C:\asm\*.c
echo          ld
C:\djgpp\bin\ld.exe C:\asm\link.ld -o C:\asm\kernel.o C:\asm\ostest.bin
echo Compile Attempt Finished
if exist "C:\asm\ostest.bin" (GOTO DoPartCopy)
GOTO exita

:DoPartCopy
copy /b C:\asm\ostest.bin+C:\asm\kernel.o C:\asm\ostest.img
call :InsertImgOnDisk "C:\asm\ostest.img"
GOTO exita

:InsertImgOnDisk
echo C:\asm\PartCopy.exe %1 0 %~z1 -f0
C:\asm\PartCopy.exe %1 0 %~z1 -f0
GOTO exitc

:exitb
del "C:\asm\kernel.o"
del "C:\asm\ostest.bin"
del "C:\asm\ostest.img"
echo .
echo Installation Attempt Failed
pause

:exita
del "C:\asm\kernel.o"
del "C:\asm\ostest.bin"
del "C:\asm\errors.txt"
del "C:\asm\ostest.img"
echo .
echo Installation Attempt Succeded

:exitc
The batch file deletes old files, then it compiles the assembly with NASM using the aout format [even though it is written to .bin] then it compiles the .c files in my asm directory then it links them with ld using this linker script

The Linker Script:

Code: Select all

OUTPUT_FORMAT("binary")
ENTRY(main)
SECTIONS
{
  .text  0xFF800000 : {
    *(.text)
  }
  .data  : {
    *(.data)
  }
  .bss  :
  { 					
    *(.bss)
  }
}
The Linker file, not much to explain

I know this is a LONG post, sorry, but this allows you to now my circumstances. I would like to know why it displays jibberish on the screen as opposed to any message? Stand-alone the asm works fine, but when they are combined it does not function even though it never uses the c code

Posted: Sun Jul 22, 2007 7:54 pm
by AndrewAPrice
There are a few things I am unclear of:
- Are you trying to use C in your bootloader? I is possible, but it's generally a bad idea since high-level languages generate a lot of code. Your BIOS will only load the first 512 bytes of the boot disk into memory, so most people stick with assembly in your boot loader.
- If you want to continue.. I noticed you have two main functions (one assembly, one c). You can rename to C one to something like c_main instead of just main. Then in Assembler use:

Code: Select all

[extern c_main]
jmp c_main
or

Code: Select all

[extern _c_main]
jmp _c_main
Whether you have to call your C functions using an underscore in assembly is up to your assembler, your linker, and the file format you're using.

Posted: Mon Jul 23, 2007 5:01 am
by CompDever
No i'm not trying to use it in my bootloader just reference it from within to start the kernel. And i essentially knew what you said but my code generates jibbberish when combined with the C code even when i am not using the C code.

Posted: Mon Jul 23, 2007 5:26 am
by AJ
Hi,

I know that lots of people have this kind of issue with Bran's Tutorial because there is not a *(.rodata) section in the link script. Does it help if you add one?

Cheers,
Adam

Posted: Mon Jul 23, 2007 5:40 am
by CompDever
Ok, People, I am just starting out in OS Development, i started this a couple of weeks ago. I learned Visual Basic in a month, C# in a week, and C++ and 2 - 3 weeks, and i guess C was learned with c++. Now that i am into OSDev i need examples to learn how to write my own OSes. In all the programming i've learned before (completely on my own) i learned by finding examples that were small enough that i could read it quickly and make sense of the code. The problem i am having with OS Development is that there are no 'small samples' there are either huge open source OSes or there are tiny pieces of code that don't do enough. I would like a 'short tutorial' on how to use both C and Asm in one OS with NASM as the assembler... Can anybody help me?

Posted: Mon Jul 23, 2007 6:36 am
by AJ

Posted: Mon Jul 23, 2007 8:02 am
by AJ
Big addition to my short answer above!
CompDever wrote:Ok, People, I am just starting out in OS Development, i started this a couple of weeks ago. I learned Visual Basic in a month, C# in a week, and C++ and 2 - 3 weeks, and i guess C was learned with c++.
What, thoroughly? You can switch from an OOP way of thinking to a non-OOP way of thinking after that length of time? How about coding the fastest possible way of solving a problem in any of your languages? How about the smallest memory footprint routine?
CompDever wrote: In all the programming i've learned before (completely on my own) i learned by finding examples that were small enough that i could read it quickly and make sense of the code.
That is an extremely valuable way of learning. However, I would back it up with some theory reading and background reading on your chosen toolchain.
CompDever wrote: The problem i am having with OS Development is that there are no 'small samples' there are either huge open source OSes or there are tiny pieces of code that don't do enough. I would like a 'short tutorial' on how to use both C and Asm in one OS with NASM as the assembler... Can anybody help me?
The link above should give you a fairly short tutorial, but it is certainly not exhaustive (your kernel will not be able to multitask, has very little in the way of drivers and can accept keyboard input but not interpret it). There is also no high level memory management.

The main reason you can find concise examples for userland code is simple. Look at the following:

Code: Select all

#include <stdio.h>
int main(int argc, char **argv)
{
   printf("Hello World.\n");
}
You would rightly expect that to work in most console environments [looksforobviousmistakes!]. If you want to do the same in a kernel environment, you have to write (or port) the functions included in stdio.h yourself. This is a matter of writing a kernel which can take a system call interrupt, correctly decipher the device to send the character string to, send it to the device via an interface (and probably driver) that you have defined yourself and so on.

For that you need a lot of code. Don't expect to complete your OS in a matter of weeks (..or months...or years..). And expect the code examples you find to either require support code, or be extremely long and convoluted.

Cheers,
Adam

Posted: Mon Jul 23, 2007 9:37 am
by Dex
You could try "OS Development for Dummies" see here: http://alexfru.chat.ru/epm.html#los4d

Re: C + Asm combination for an OS [x86]

Posted: Wed Jul 25, 2007 2:21 am
by urxae
CompDever wrote:I am attempting to create my OS with a combination of assembler and c, i have a good stand alone assembler bootloader but when i use this tutorial's method of combining, my text no longer displays...

The Assembly
[snip]
this merely displays a few lines of text on the screen, works well stand-alone

Code: Select all

OUTPUT_FORMAT("binary")
ENTRY(main)
SECTIONS
{
  .text  0xFF800000 : {
    *(.text)
  }
  .data  : {
    *(.data)
  }
  .bss  :
  { 					
    *(.bss)
  }
}
The Linker file, not much to explain

I know this is a LONG post, sorry, but this allows you to now my circumstances. I would like to know why it displays jibberish on the screen as opposed to any message? Stand-alone the asm works fine, but when they are combined it does not function even though it never uses the c code
Let me guess: You're not using that linker script when compiling the bootsector standalone?
That linker script tells the code to expect to be loaded at address 0xFF800000, which isn't where the bootsector will be loaded. This causes it to look for your strings in the wrong place, resulting in gibberish instead of your text.
You could try this:
  • Put "SECTION boot-text" near the top of your assembly code.
  • Put this in front of the .text section in your linker script:

    Code: Select all

      boot-text  0x7C00 : {
        *(.text)
      }
This should put your bootsector into a separate section that's linked for the address it'll be loaded at. At least, I think it should...

Posted: Wed Jul 25, 2007 2:57 am
by jnc100
All you're doing to your C code is compiling it. You cannot expect from just doing that that the first byte of the object file is the start of the first opcode of k_main. You need to use a binary format that defines where in the executable the entry point is. If you wish to use a flat binary, it probably makes sense to have your entry point at the start of the file. The normal way to do this is to create a small assembly stub at the start of your kernel (not bootloader) which jumps to your c function.

e.g.
bootloader (boot.asm)

Code: Select all

[ORG 0x0]
set up system (cli, a20 gate, remap pic etc)
load next sector to predefined address, e.g. 0x9000
enable pmode, set up segment selectors
jump to 0x9000
times 510-($-$$) db 0
dw 0xAA55 
kernel stub (head.asm)

Code: Select all

global k_main

[BITS 32]
mov esp, 0x90000
call k_main
jmp $
kernel c code (main.c)

Code: Select all

int puts(const char *c);

void k_main(void) {
puts("Hi from the kernel!");
}

int puts(const char *c)
{
// code to display a string
}
Then you could make the image with:

Code: Select all

nasm -f bin -o boot.bin boot.asm
nasm -f elf -o head.o head.asm
gcc -c -o main.o main.c
ld --oformat binary -Ttext 0x9000 -o kernel.bin head.o main.o
cat boot.bin kernel.bin > image
This is just off the top of my head, I haven't tested it. Once you get something like that working, you can try using a different kernel file format, like elf, which doesn't require the entry point to be the first byte in the file. Also, GRUB can load elf files directly.

Regards,
John