Захотелось мне пописать что-то ненормальное. Выбор пал на ОС, в конце-концов каждый программист должен написать свою ОС, пусть хотя бы учебную.
Как некоторым известно, я очень люблю язык Go ну, и решил попробовать написать на нем. Что из этого получилось — под хабракатом.
Писать свой загрузчик я не буду, не для того умные люди придумывали спеку multiboot.
Для начала напишем код первоначальной загрузки (файл multiboot.s)
MBOOT_PAGE_ALIGN equ 1<<0
MBOOT_MEM_INFO equ 1<<1
MBOOT_HEADER_MAGIC equ 0x1BADB002
MBOOT_HEADER_FLAGS equ MBOOT_PAGE_ALIGN | MBOOT_MEM_INFO
MBOOT_CHECKSUM equ -(MBOOT_HEADER_MAGIC + MBOOT_HEADER_FLAGS)
[BITS 32]
[GLOBAL mboot]
[EXTERN code]
[EXTERN bss]
[EXTERN end]
mboot:
dd MBOOT_HEADER_MAGIC
dd MBOOT_HEADER_FLAGS
dd MBOOT_CHECKSUM
dd mboot
dd code
dd bss
dd end
dd start
[GLOBAL start]
extern go.kernel.Load ;Указываем на то, что у нас есть внешняя функция на Go
start:
push ebx
cli
call go.kernel.Load ;Вызываем внешнюю функцию, которая содержит основной код ядра
jmp $
теперь создадим файл kernel.go следующего содержания:
package kernel
function Load(){
//Как видим наше ядро пока ничего не делает
}
Создадим файл link.ld
ENTRY(start)
SECTIONS
{
.text 0x100000 :
{
code = .; _code = .; __code = .;
*(.text)
. = ALIGN(4096);
}
.data :
{
data = .; _data = .; __data = .;
*(.data)
*(.rodata)
. = ALIGN(4096);
}
.bss :
{
bss = .; _bss = .; __bss = .;
*(.bss)
. = ALIGN(4096);
}
end = .; _end = .; __end = .;
}
и Makefile
SOURCES=multiboot.o kernel.go.o
GOFLAGS= -nostdlib -nostdinc -fno-stack-protector -fno-split-stack -static -m32 -g -I.
GO=gccgo
ASFLAGS= -felf
NASM= nasm $(ASFLAGS)
OBJCOPY=objcopy
LDFLAGS=-T link.ld -m elf_i386
all: $(SOURCES) link
clean:
rm *.o kernel
link:
ld $(LDFLAGS) -o kernel $(SOURCES)
%.go.o: %.go
$(GO) $(GOFLAGS) -o $@ -c $<
%.o: %.s
$(NASM) $<
Теперь, выполнив make в дирректории проекта вы получите на выходе файл kernel, который можно загрузить с помощью qemu:
qemu-system-i386 -kernel ./kernel
Ядро успешно загрузится и ничего не будет делать )
Настало время поздороваться с миром.
Для начала добавим в multiboot.s следующие строки:
global __go_runtime_error
global __go_register_gc_roots
global __unsafe_get_addr
__unsafe_get_addr:
push ebp
mov ebp, esp
mov eax, [ebp+8]
mov esp, ebp
pop ebp
ret
__go_register_gc_roots:
__go_runtime_error:
ret
функция __unsafe_get_addr нужна для того, что бы мы могли конвертировать uint32 в указатели внутри Go
Другие две функции — просто затычки для компилятора
Теперь создадим файл screen.go
package screen
var (
frameBuffer *[totalMax]uint16 //Старшие 8 бит - символ, младшие - его атрибуты
cursorX, cursorY uint8
)
const (
frameBufferAddr = 0xB8000
maxX = 80
maxY = 25
totalMax = maxX * maxY
whiteOnBlack = 0x07
)
//Ниже мы создаем обертку для Go над нашей функцией __unsafe_get_addr
//extern __unsafe_get_addr
func getAddr(addr uint32) *[totalMax]uint16
func Init() {
cursorX = 0
cursorY = 0
frameBuffer = getAddr(frameBufferAddr) //Получаем доступ к видеобуферу
}
//Очистка экрана, просто заполняем весь видеобуфер нулями
func Clear() {
for i := 0; i < totalMax; i++ {
frameBuffer[i] = 0
}
cursorX = 0
cursorY = 0
}
//Меняем позицию курсора
func SetCursor(x, y uint8) {
cursorX = x
cursorY = y
}
//Скроллим экран если он заполнен
func scroll() {
if cursorY >= maxY {
for i := 0; i < 24*maxX; i++ {
frameBuffer[i] = frameBuffer[i+80] //Смещаем все строки на одну вверх
}
for i := 24 * 80; i < totalMax; i++ {
//Очищаем нижнюю строку
frameBuffer[i] = 0x20 | (((0 << 4) | (15 & 0x0F)) << 8)
frameBuffer[i] = 0
}
cursorY = 24
cursorX = 0
}
}
//Вывод символов
func putChar(c byte) {
switch c {
case 0x08: //backspace
if cursorX > 0 {
cursorX--
}
case 0x09: //tab
cursorX = (cursorX + 8) & (8 - 1)
case '\r': //return
cursorX = 0
case '\n': //new line
cursorX = 0
cursorY++
default:
if c >= 0x20 { //Все печатные символы
frameBuffer[cursorY*80+cursorX] = uint16(c) | (((0 << 4) | (15 & 0x0F)) << 8)
cursorX++
}
}
if cursorX >= 80 { //Если надо перемещаем курсор
cursorX = 0
cursorY++
}
scroll()
}
//Выводим строку
func PrintStr(s string) {
for i := 0; i < len(s); i++ {
putChar(s[i])
}
}
Теперь надо подключить наш модуль screen к ядру — в kernel.go добавляем import «screen», там же, в функци Load() пишем:
screen.Init()
screen.Clear()
screen.PrintStr("Hello Habrahar!")
Теперь надо указать компилятору как все это дело собирать нам понадобится добавить в Makefile следующие строки:
%.gox: %.go.o
$(OBJCOPY) -j .go_export $< $@
И там же, в переменную SOURCES между multiboot.o и kernel.go.o добавить screen.go.o и screen.gox
После проведения всех манипуляций вызываем команду make и запускаем qemu с нашим ядром. Дожидаемся загрузки и радуемся
P.S. Прошу простить меня за опечатки, если они есть. Обязуюсь исправить.
P.P.S. На днях код будет выложен на github
Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.
This entry passed through the Full-Text RSS service - if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.
Комментариев нет:
Отправить комментарий