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:
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.
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)
}
}