Agora, finalmente podemos colocar a mão na massa!
Iremos precisar criar nossa estrutura de arquivos para realizar a tarefa. Seguindo a mesma ordem hierárquica do projeto ErdOS, iremos obter:
mkdir bin boot libc
$ touch start.asm main.cpp link.ld libc/io.hpp libc/io.cpp
Para a comodidade, disponibilizo o disco de boot já criado com GRUB no repositório do ErdOS. Mas, caso queira criar o seu, sem problema algum.
cd boot
$ wget https://github.com/gabrielbiga/ErdOS/raw/master/boot/grub.img
Com nossa estrutura pronta, já iremos poder colocar o código em seus respectivos arquivos.
Arquivo "start.asm" (lançador do SO, escrito em
Assembly):
[BITS 32]
global start
start:
mov esp, _pilha_do_sistema ; Seleciona nova area na pilha do sistema
jmp subir_kernel ; Pula para o bloco onde e efetuada a chamada do kernel (main.cpp)
ALIGN 4 ; Obrigatoriamente esta parte deve estar alinhada a 4 bytes
;
; O bloco multiboot e responsavel por expor informacoes importantes para o linker
;
multiboot:
; Macros para o GRUB
MULTIBOOT_PAGE_ALIGN equ 1<<0
MULTIBOOT_MEMORY_INFO equ 1<<1
MULTIBOOT_AOUT_KLUDGE equ 1<<16
MULTIBOOT_HEADER_MAGIC equ 0x1BADB002
MULTIBOOT_HEADER_FLAGS equ MULTIBOOT_PAGE_ALIGN | MULTIBOOT_MEMORY_INFO | MULTIBOOT_AOUT_KLUDGE
MULTIBOOT_CHECKSUM equ -(MULTIBOOT_HEADER_MAGIC + MULTIBOOT_HEADER_FLAGS)
EXTERN code, bss, end
; Expondo os cabecalhos Multiboot do GRUB, a assinatura do boot
; Caso queira ter mais informacoes vide: http://www.gnu.org/software/grub/manual/multiboot/multiboot.html
dd MULTIBOOT_HEADER_MAGIC
dd MULTIBOOT_HEADER_FLAGS
dd MULTIBOOT_CHECKSUM
; Expondo enderecos fisicos para o linker (link.ld)
dd multiboot
dd code
dd bss
dd end
dd start
;
; O bloco subir_kernel e um loop infinito e tem a funcao de chamar
; a funcao main() do nosso main.cpp
;
subir_kernel:
extern main
call main
jmp $
;
; A section BSS tem a funcao de alocar o tamanho de nossa pilha
;
SECTION .bss
resb 8192 ; Reserva e apenas reserva 8KB de memoria para a pilha
_pilha_do_sistema:
Arquivo "main.cpp" (arquivo principal, escrito em
C++):
#include "libc/io.hpp"
using namespace std;
int main() {
//Limpa nossa tela
limpar_tela();
//Escreve uma string na tela, no segundo parametro e colocada
//a cor desejada, onde 0x??. O primeiro ? representa a cor de fundo
//e o segundo ? representa a cor da fonte. 0 para preto.
escrever("Bem vindo ao ErdOS", 0x02);
//Loop infinito para o SO nunca parar
for(;;);
}
Arquivo "link.ld" (parâmetros de configuração do linker, arquivo de parâmetros):
OUTPUT_FORMAT("binary")
ENTRY(start)
/* Endereco fisico onde o Kernel sera alocado */
phys = 0x00100000;
/* Exposicoes ao ld */
SECTIONS
{
.text phys : AT(phys) {
code = .;
*(.text)
*(.rodata)
. = ALIGN(4096);
}
.data : AT(phys + (data - code))
{
data = .;
*(.data)
. = ALIGN(4096);
}
.bss : AT(phys + (bss - code))
{
bss = .;
*(.bss)
. = ALIGN(4096);
}
end = .;
}
Arquivo "libc/io.hpp" (definição do namespace "std", header C++):
#ifndef IO_HPP
#define IO_HPP
namespace std {
void escrever(char*, int);
void limpar_tela(void);
}
#endif
Arquivo "libc/io.cpp" (minúsculo driver de vídeo, escrito em C++):
#include "io.hpp"
//Define o endereco do buffer
#define BUFFER 0xb8000
/*
* std::escrever
* Escreve uma string (array de caracteres) na tela.
*/
void std::escrever(char* mensagem, int cor)
{
char* mem = (BUFFER);
while(*mensagem != 0)
{
*mem = *mensagem;
mem++;
mensagem++;
*mem = (char*) cor;
mem++;
}
}
/*
* std::limpar_tela
* Limpa a tela.
*/
void std::limpar_tela(void)
{
char* mem = (char*)(BUFFER);
while(*mem != 0)
{
*mem = 0;
mem++;
}
}