Day 04 - Take the Advantage of C++!!!

We have Assembly code, we have C++ code, emm~~, Assembly code interacting with C++ code. Assembly code is more suitable to deal with registers or hardware level business, C++ is more programming friendly when we want to implement somewhat high level logics. During the development of OS, from time to time, we have to realize some low level functions which are not naturally supported by C++, saying dealing with interruptions. But, implementing everything using Assembly may be too troublesome. In today’s work, we will call C++ functions in Assembly, and call Assembly functions in C++.

Assembly Function

We first write an assembly function named “void assemblyprint(void)” that prints “C++ calls assembly function success!!”. The code reads

msg:
    db "C++ calls assembly function success!!", 0

; some other codes


global fin              ; declare a function name called "fin"
fin:
    cli
    hlt

global assemblyprint    ; declare a function name called "assemblyprint"
assemblyprint:
    mov esi,msg
    mov ebx,0xb8000
    add ebx, 80*2*2         ; 80 chars is one line, one char is 2 bytes. spare two lines
.loop2:
    lodsb
    or al,al
    jz fin
    or eax,0x0F00
    mov word [ebx], ax
    add ebx,2
    jmp .loop2

C++ Function

We now create a new file named kmain.cpp. Since we don’t have access to standard libraries such as iostream , we have to implement all functions ourselves. This time, the C++ code reads

// __Z7myprintPKcis
void myprint(const char* msg, int len, short color){
    short* vga = (short*)0xb8000; // vga memory is at 0xb8000. It's a constant
    // for vga, it doesn't know how to change line,
    // we have to spare 80 chars (one line is 80 char) to avoid cover the assembly printout
    short textoffset = 80;
    for (int i = 0; i<len;++i)
        vga[i + textoffset] = color | msg[i];
}

extern "C" void assemblyprint(void);
extern "C" void fin(void);


int main(){
    const short color = 0x0F00; // white
    const char* hello = "Hello cpp world!";

    myprint(hello, 16, color);

    assemblyprint();
    fin();
    return 0;
}

Knowledge points:

  1. Name Mangling

C++ compiler by default will use name mangling to rename functions with new names to enable “function override”, except “main” function. For example, myprint will be renamed to be “__Z7myprintPKcis”. In order to disable the “name mangling” function, we add extern "C" prefix.

  1. Function Declaration

extern "C" void assemblyprint(void); and extern "C" void fin(void); are declarations of functions. These two lines tell the compiler that “Please feel safe to call these two functions. I realized both of them somewhere else.”

Assembly Call C++ Functions

In assembly code, we need to add

; The following is to call C++
cppcode:
    mov esp, kernel_stack_top
    extern main
    call main                       ; the actual name in c++ obj file. If name mangling is used, "main" may need to be changed.

section .bss        ; Mark the following to be .bss section. Will be checked by linker script
align 4             ; aligns the current location to a specified boundary by padding 0.
kernel_stack_bottom: equ $
    resb 16384 ; reserve 16 KB for kernel stack from current place (marked by $)
kernel_stack_top:

We also need to tell linker to follow a template to properly link assembly functions to C++ code. More detailed tutorial lies at the top of the linker.ld. In the near future, we use the following template

ENTRY(boot)
OUTPUT_FORMAT("binary")
SECTIONS {
    . = 0x7c00;
    .text :
    {
        *(.boot)
        *(.text)
    }

    .rodata :
    {
        *(.rodata)
    }

    .data :
    {
        *(.data)
    }

    .bss :
    {
        *(.bss)
    }
}

Result

../../../../_images/result3.png