Приветствую, %username%.
Перед вами вторая статья из цикла о разработке ОС на Go + asm.
Изначально я планировал, что вторая статья будет про обработку прерываний, но Go накладывает свои корректировки — сейчас будет описанно простое выделение памяти и часть рантайма Go, которая нам пригодится, но будет переписанно. На самом деле это подготовка к третьей статье — куче и допиливанию рантайма.
Кода к этой статье на гитхабе не будет (забыл вовремя сделать коммит, а теперь банально лень специально восстанавливать, если кто пришлет пулл реквест — буду благодарен)
Слабонервным просьба закрыть статью — все, что может быть написанно на Go будет написанно на нем!
Помните в multiboot.s были функции заглушки для компилятора? Перенесите их в файл runtime.s (создайте его)
Step 0x00
Для начала давайте научимся выделять память — создадим файл memory.go
package memory //extern end var end uint32 //берется из link.ld var placement_address uint32 //Текущий адрес //extern __unsafe_get_addr func pointer2uint32(pointer *uint32) uint32 //Все как в прошлый раз, разве что тип аргумента изменился, но это не существенно func Init() { placement_address = pointer2uint32(&end) //получаем начальный адресс } func kmalloc(size uint32, align int, phys *uint32) uint32 { //выделение памяти if align == 1 && (placement_address&0xFFFFF000) != uint32(0) { //Если адрес не выровнен по границе - то выравниваем. placement_address &= 0xFFFFF000 placement_address += 0x1000 } if phys != nil { //если необходимо - возвращаем физический адрес *phys = placement_address } res := placement_address //возвращаем текущий адрес placement_address += size //изменяем текущий адрес return res } func Kmalloc(size uint32) uint32 { //Простое выделение памяти return kmalloc(size, 0, nil) }
Step 0x01
Приступим к рантайму. Создаем файл runtime.go
package runtime import ( "memory" ) //extern __unsafe_get_addr func pointer2byteSlice(ptr uint32) *[]byte //extern __unsafe_get_addr func pointer2uint32(ptr interface{}) uint32 func memset(buf_ptr uint32, value byte, count uint32) { //Примитивная реализация memset var buf *[]byte buf = pointer2byteSlice(buf_ptr) for i := uint32(0); i < count; i++ { (*buf)[i] = value } } func memcpy(dst, src uint32, size uint32) { //Так же примитивная реализация memcpy var dest, source *[]byte dest = pointer2byteSlice(dst) source = pointer2byteSlice(src) for i := uint32(0); i < size; i++ { (*dest)[i] = (*source)[i] } } func New(typeDescriptor uint32, size uint32) uint32 { //А вот и будущая new() //На данном этапе typeDescriptor не нужен buf_ptr := memory.Kmalloc(size) //Выделяем память memset(buf_ptr, 0, size) //забиваем выделенную память нулями return buf_ptr //Возращаем указатель }
Читатель, изучивший коментарии к преведущей статье задаст вопрос: «Как же так, new() это __go_new, а не go.runtime.new?»
Ответ в файле runtime.s (Помните я просил перенести функции из multiboot.s в него?)
;gccgo compability global __go_runtime_error global __go_register_gc_roots __go_register_gc_roots: __go_runtime_error: ret global __unsafe_get_addr ;convert uint32 to pointer __unsafe_get_addr: push ebp mov ebp, esp mov eax, [ebp+8] mov esp, ebp pop ebp ret extern go.runtime.New global __go_new global __go_new_nopointers __go_new: ;прокидываем go.runtime.New как __go_new __go_new_nopointers: call go.runtime.New ret
Теперь приведем файл kernel.go к следующему виду:
package kernel import ( "memory" "screen" ) func Load() { memory.Init() screen.Init() screen.Clear() str := new(string) *str = "Habrahabr" screen.PrintStr(string(*str)) }
После компиляции и запуска мы увидим надпись «Habrahabr».
ПРЕДУПРЕЖДЕНИЕ: На самом деле пока не стоит создавать вектора на слайсы, строки и другие типы переменной длинны иначе возможно наложение.
ссылка на оригинал статьи http://habrahabr.ru/post/259839/
Добавить комментарий