Форум программистов
 

Восстановите пароль или Зарегистрируйтесь на форуме, о проблемах и с заказом рекламы пишите сюда - alarforum@yandex.ru, проверяйте папку спам!

Вернуться   Форум программистов > Низкоуровневое программирование > Assembler - Ассемблер (FASM, MASM, WASM, NASM, GoASM, Gas, RosAsm, HLA) и не рекомендуем TASM
Регистрация

Восстановить пароль
Повторная активизация e-mail

Купить рекламу на форуме - 42 тыс руб за месяц

Ответ
 
Опции темы Поиск в этой теме
Старый 19.08.2008, 15:05   #1
BOBAH13
Android Developer
Старожил Подтвердите свой е-майл
 
Аватар для BOBAH13
 
Регистрация: 19.02.2007
Сообщений: 3,708
Подмигивание Разработка собственной ОС

Всем привет. Как бы это не звучало смешно, НО стоит призадуматся, а почему бы нет ? Я сам лично тоже изучаю, поэтому абсолютно на все вопросы дать ответы не смогу но буду стараться. Излагать буду не грамоздко, а как возможно компактнее.

Причины которые сподвигли меня на это:
1. Разобраться как все устроено в ПК.
2. Довести до ума мои знания Asm и C.
3. Существует ряд статей в сети на эту тему, но все они не дают полного понимания того, что просиходит в коде.
4. Просто это интересно

По сути последнии и самые популярные ОС, всем известные Windows и Linux основаны на идеях еще 20го века
И так главная идея всей этой задумки, это просто: "Для изучения и понимания". А в будущем будет видно что из этого выйдет. Начало будет обычным, а дальше будем думать уже вместе.

И так к делу:

1. Софт
Для разработки нашей ОС понадобится ( выбран софт, который я использую, можете выбрать какой вам угоден ):
1. Виртуальная машина для теста Bochs
2. Ассемблер Nasm
3. Для создания кода на языке С понадобится MinGW
4. Так же информация о Asm, C, Железе, Прерываниях BIOS HelpPC

2. Теория
Так мы начинающие в этом деле то будем начинать с самого малого. Этапы запуска нашей ОС это
1. Загрузка ( в нашем случае будем грузиться с флоппика, в будущем будет видно )
2. Проверка и инициализация оборудования
3. Загрузка всех необходимых данных для нашей ОС и запуск «диалога» с пользователем.

3. Загрузчик
И так забываем о файловых системах, и вообще о понятии «файл». Флоппик ( дискета ) делится на сектора. Каждый сектор это 512 байт. При запуске ПК BIOS инициализирует все оборудование, проверяет его на ошибки. Дальше читает дискету, если она будет, то читаются первые 512 байт ( т.е. первый сектор ) по адресу 0000:07c0 и иму передается управление. Именно эти 512 байт и будут «первичным» загрузчиком. Почему первичным т.к. именно в этих 512 байт мы должны будем вместить код для передачи управления коду который будет распалогаться далее на дискете после первого сектора. Т.к. мы только загружаемся у нас есть BIOS и не более, поэтому начнем с Nasm.

Последний раз редактировалось BOBAH13; 19.08.2008 в 16:01.
BOBAH13 вне форума Ответить с цитированием
Старый 19.08.2008, 15:06   #2
BOBAH13
Android Developer
Старожил Подтвердите свой е-майл
 
Аватар для BOBAH13
 
Регистрация: 19.02.2007
Сообщений: 3,708
По умолчанию

boot.asm
Код:
[BITS 16]	;Говорим что 16 битный код
[ORG 0]	;Начало с 0000:0000

jmp	entry ;Пропускаем данные и передаем управление коду по метке entry, а так же для корректировки на 0000:07c0

	cyls_read			db 10	;Кол-во цилиндров для чтения
	max_errors			db 5	;Максимальное кол-во ошибок

	;Сообщения для информирования пользователя
	msg_loading 		db "Loading", 0
	msg_loading_proc 		db ".", 0
	msg_reading_error 	db "Error reading from floppy. Errcode:", 0
	msg_giving_up 		db "Too many errors, giving up", 0x0a, 0x0d, "Reboot your system, please", 0
	msg_crlf 			db 0x0a, 0x0d ;Всем знакомые 2 байты, для переноса строки и каретки.

entry:
	cli				;Запрещаем прерывания BIOS
	mov	ax, 0x07c0		;Указываем адрес куда нас BIOS загрузил
	mov	ds, ax
	mov 	ax, 0x9000		;Адрес сегмента
	mov 	es, ax
	xor 	si, si		;Копируем с нуля
	xor 	di, di
	sti				;Разрешаем прерывания BIOS

	mov 	cx, 128		;Говорим перенести 128 двойных слов ( 128 * 2 * 2 байт ) по адресу 9000:0000
	rep 	movsd

	jmp 	0x9000:start	;Передаем управление скопированному коду


start:
	mov	si, msg_loading	;Вывод сообщения
	call	print

	mov 	ax, cs		;Новые значения в сегментные регистры
	mov 	ds, ax
	mov	ss, ax

	mov 	di, 1			;Начнем копирование с дискеты в 0290:0000 с первого цилиндра
	mov 	ax, 0x290
	xor 	bx, bx

.loop:
	mov	si, msg_loading_proc	;Выводим точку :)
	call	print

	mov 	cx, 0x50
	mov 	es, cx

	push 	di
		
	shr 	di, 1
	setc 	dh
	mov 	cx, di
	xchg 	cl, ch

	pop	di
	cmp 	di, cyls_read		;Все ли цилиндры прочитаны
	je 	.quit

	call 	read_cylinder		;Читаем дальше цилиндр	0050:0000 - 0050:2400

	pusha
	push 	ds

	mov 	cx, 0x50			;Копируем данный блок дальше в 0290:0000
	mov 	ds, cx
	mov 	es, ax
	xor 	di, di
	xor 	si, si
	mov 	cx, 0x2400
	rep 	movsb

	pop 	ds
	popa

	inc 	di				;Увеличиваем значения
	add 	ax, 0x240
	jmp 	short .loop			;Продолжаем читать

.quit:
	mov 	ax, 0x50			;Скопированы все цилиндры, прочитаем нулевой цилиндр
	mov 	es, ax
	mov 	bx, 0
	mov 	ch, 0
	mov 	dh, 0
	call 	read_cylinder

	jmp 	0x0000:0x0700		;Все теперь передаем управление коду "вторичного" загрузчика

read_cylinder:				;Функция чтения цилиндра прерывание BIOS 0x13
	mov 	[.errors_counter], byte 0
	pusha

.start:
	mov 	ah, 0x02
	mov 	al, 18
	mov 	cl, 1

	int 	0x13
	jc 	.read_error

	popa
	ret



.errors_counter: db 0			;Кол-во ошибок
.read_error:
	inc 	byte [.errors_counter]
	mov 	si, msg_reading_error
	call 	print
	call 	printh
	mov 	si, msg_crlf
	call 	print

	cmp 	byte [.errors_counter], max_errors
	jl 	.start

	mov 	si, msg_giving_up		;Получили много ошибок, выведим сообщение и повесим систему
	call 	print
	hlt
	jmp	$



hex_table:	db "0123456789ABCDEF"
printh:					;Выведим хекс значение
	pusha
	xor 	bx, bx
	mov 	bl, ah
	and 	bl, 11110000b
	shr 	bl, 4
	mov 	al, [hex_table+bx]
	call 	printc

	mov 	bl, ah
	and 	bl, 00001111b
	mov 	al, [hex_table+bx]
	call 	printc

	popa
	ret


printc:					;Выведим один символ al
	pusha
	mov 	ah, 0x0E
	int 	0x10
	popa
	ret

print:					;Выведим строку si
	pusha
.loop:
	lodsb
	or 	al, al
	jz 	.quit
	mov 	ah, 0x0e
	mov 	bx, 0x7
	int 	0x10
	jmp 	.loop
.quit:
	popa
	ret


TIMES 510 - ($-$$) db 0			;Ставим нулевые байты до 510го байта
dw 0xaa55					;Ставим сигнатуру, говорим BIOS, что это загрузочный сектор

incbin   'setup.o'

Последний раз редактировалось BOBAH13; 19.08.2008 в 15:23.
BOBAH13 вне форума Ответить с цитированием
Старый 19.08.2008, 15:23   #3
BOBAH13
Android Developer
Старожил Подтвердите свой е-майл
 
Аватар для BOBAH13
 
Регистрация: 19.02.2007
Сообщений: 3,708
По умолчанию

Теперь наша задача скопоновать код по адресу 0000:0700, это будет наш "вторичный загрузчик".
В этот раз наша задача состоит в:
1. Включить линию А20, это даст возможность использовать на полную память ПК
2. Загрузить GDT - таблица дескрипторов
3. Перейти в защищенный режим процессора. Есть еще реальный режим, в чем его недостатки можете прочитать в сети безовсяких проблем .
4. Скопировать ядро и передать ему управление.
5. Пишем уже на С .

setup.asm
Код:
[BITS 16]
[ORG 0x0700]		;и так в "первичном" загрузчике давали управление именно по этому адресу

	cli			;инициализируем сегментные регистры
	mov 	ax, 0
	mov 	ds, ax
	mov 	es, ax
	mov 	ss, ax
	mov 	sp, 0x700
	sti

	; Установим базовый вектор контроллера прерываний в 0x20
	mov 	al, 00010001b 
	out 	0x20, al
	mov 	al, 0x20 
	out 	0x21, al 
	mov 	al, 00000100b 
	out 	0x21, al
	mov 	al, 00000001b	
	out 	0x21, al

	cli

	call	enableA20		;находится в A20.asm про него ниже

	; Загрузка регистра GDTR:
	xor	ax, ax
	mov	ds, ax

	lgdt 	[gd_reg]

	; Установка бита PE регистра CR0
	mov 	eax, cr0
	or 	al, 1
	mov 	cr0, eax
	
	; Переходим в защищенный режим, в 32-бита
	jmp 	0x08:protected

%include 'A20.asm'

[BITS 32]
protected:
	mov 	ax, 0x10		;инициализируем сегментные регистры
	mov 	ds, ax
	mov 	es, ax
	mov 	ss, ax
	mov	fs, ax
	mov	gs, ax
	sti

	; Копируем ядро по адресу 0x200000
	mov 	esi, kernel
	mov 	edi, 0x200000
	mov 	ecx, (kernel_size / 4) + 1 ;Размер ядра
	rep 	movsd

	jmp	0x200000 ; Передаем управление ядру

gdt:
gdt_null:
	dd	0, 0		; Нулевой дескриптор
gdt_code:
	dw 	0xFFFF	; Сегмент кода
	dw 	0
	db 	0
	db 	10011010b
	db 	11001111b
	db 	0
gdt_data:
	dw 	0xFFFF	; Сегмент данных
	dw 	0
	db 	0
	db 	10010010b
	db 	11001111b
	db 	0
gdt_end:

; Значение, которое мы загрузим в GDTR:	
gd_reg:
	dw 	gdt_end - gdt - 1
	dd 	gdt

kernel:
	incbin 'kernel.bin'
kernel_size equ $-kernel

; Если надо, то расскоментируйте и получите полный образ дискеты
; Floppy size = 1474560
;TIMES 1474560 - 512 - ($-$$) db 0

Последний раз редактировалось BOBAH13; 19.08.2008 в 15:29.
BOBAH13 вне форума Ответить с цитированием
Старый 19.08.2008, 15:30   #4
BOBAH13
Android Developer
Старожил Подтвердите свой е-майл
 
Аватар для BOBAH13
 
Регистрация: 19.02.2007
Сообщений: 3,708
По умолчанию

A20.asm я выудил с одного сайта, уже даже и не помню где именно, но писал его умный человек вот код:
Код:
;;
;; enableA20.s (adapted from Visopsys OS-loader)
;;
;; Copyright (c) 2000, J. Andrew McLaughlin
;; You're free to use this code in any manner you like, as 
;; long as this notice is included (and you give credit where
;; it is due), and as long as you understand and accept that
;; it comes with NO WARRANTY OF ANY KIND. 
;; Contact me at jamesamc@yahoo.com about any bugs or problems.
;;

enableA20:
;; Эта процедура включит A20 через контроллер клавиатуры
;; Аргументы: нет.  Возвращает: 0 в EAX в случае успеха,
;; -1 при ошибке.  Написано для использования в 16-битном коде
;; (32-битные участки помечены как 32-BIT)

pusha

;; Убедимся, что прерывания отключены
cli

;; В CX будет счетчик (мы предпримем до 5 попыток включения A20)
mov CX, 5

.startAttempt1:
;; Подождем пока контроллер готов принимать команды
.commandWait1:
xor AX, AX
in AL, 64h
bt AX, 1
jc .commandWait1

;; Скажем контроллеру, что мы хотим прочитать текущий статус
;; (команда D0h)
mov AL, 0D0h
out 64h, AL

;; Подождем пока контроллер готов
.dataWait1:
xor AX, AX
in AL, 64h
bt AX, 0
jnc .dataWait1

;; Прочитаем текущий статус из порта 60h
xor AX, AX
in AL, 60h

;; сохраним текущее значение (E)AX
push AX; 16-BIT
;; push EAX; 32-BIT

;; Подождем пока контроллер готов
.commandWait2:
in AL, 64h
bt AX, 1
jc .commandWait2

;; Скажем контроллер, что мы снова хотим записать байт статуса
mov AL, 0D1h
out 64h, AL

;; Подождем пока контроллер готов
.commandWait3:
xor AX, AX
in AL, 64h
bt AX, 1
jc .commandWait3

;; Запишем новое значение в порт 60h.  Напоминаю, что предыдущее
;; мы сохранили в стеке
pop AX; 16-BIT
;; pop EAX; 32-BIT

;; Установим бит включения A20
or AL, 00000010b
out 60h, AL

;; И, наконец, прочитаем байт статуса, чтобы убедиться
;; что A20 был включен

;; Подождем пока контроллер готов
.commandWait4:
xor AX, AX
in AL, 64h
bt AX, 1
jc .commandWait4

;; Команда D0h
mov AL, 0D0h
out 64h, AL

;; Подождем пока контроллер готов
.dataWait2:
xor AX, AX
in AL, 64h
bt AX, 0
jnc .dataWait2

;; Прочитаем статус из порта 60h
xor AX, AX
in AL, 60h

;; Шлюз A20 включен?
bt AX, 1

;; Проверим результат.  Если флаг CF установлен, то A20 включен.
jc .success

;; Повторить операцию?  Если счетчик в ECX
;; не дошел до нуля, мы должны повторить
loop .startAttempt1


;; Мда, попытка включить A20 обычным способом провалилась.
;; Теперь мы попробуем запасной метод (на многих чипсетах он
;; не поддерживается, но на некоторых он единственно возможный


;; Счетчик попыток
mov CX, 5

.startAttempt2:
;; Подождем пока контроллер готов
.commandWait6:
xor AX, AX
in AL, 64h
bt AX, 1
jc .commandWait6

;; Скажем контроллеру, что мы хотим включить A20
mov AL, 0DFh
out 64h, AL

;; Прочитаем статус A20, чтобы убедиться, что A20 включен

;; Подождем пока контроллер готов
.commandWait7:
xor AX, AX
in AL, 64h
bt AX, 1
jc .commandWait7

;; Команда D0h.
mov AL, 0D0h
out 64h, AL

;; Подождем пока контроллер готов
.dataWait3:
xor AX, AX
in AL, 64h
bt AX, 0
jnc .dataWait3

;; Прочитаем байт статуса из порта 60h
xor AX, AX
in AL, 60h

;; A20 включен?
bt AX, 1

;; Проверим CF. Если он установлен, то A20 включен.
;; Предупредим, что пришлось использовать альтернативный метод
jc .warn

;; Повторим операцию, если CX не дошел до нуля
loop .startAttempt2


;; Мда, у нас ничего не получилось. Здесь можно поместить
;; сообщение об ошибке
jmp .fail


.warn:
;; Здесь можно поместить сообщение о том, что пришлось
;; использовать альтернативный метод включения A20

.success:
sti
popa
xor EAX, EAX
ret

.fail:
sti
popa
mov EAX, -1
ret
BOBAH13 вне форума Ответить с цитированием
Старый 19.08.2008, 15:37   #5
BOBAH13
Android Developer
Старожил Подтвердите свой е-майл
 
Аватар для BOBAH13
 
Регистрация: 19.02.2007
Сообщений: 3,708
По умолчанию

И вот уже мы перешли непосредственно к ядру нашей ОС. И как разработчики-профи должны писать уже на С.
Для нормальной компановки нашего образа дискеты создадим один .c файл, как точка входа в язык С.

entry.c
Код:
void kernel_main(); // функция ядра

void kernel_entry() {
   kernel_main(); // передадим ей управление
}
kernel.c
Код:
void kernel_main() {
   while(1); // повесим в ожидании чуда.
}
4. Сборка образа
И так чтобы собрать выше я указал нужный нам софт, если все скачали и поставили то приступим. Все что у нас уже есть так это boot.asm, setup.asm, A20.asm, entry.c, kenrle.c. Чтобы собрать создадим MyOS_build.bat такого содержания
Код:
@gcc -ffreestanding -c -o entry.o entry.c
@gcc -ffreestanding -c -o kernel.o kernel.c
@ld -Ttext 0x200000 -o kernel.bin entry.o kernel.o

@objcopy kernel.bin -O binary

@nasmw -fbin -o setup.o setup.asm
@nasmw -fbin -o MyOS.img boot.asm

@pause
В итоге окно консоли должно быть пустым и повяится файл MyOS.img - это и есть образ дискеты который даем Bochs'у . Как запустить данный образ? Создадим еще один файл MyOSstart.bxrc вот такого вот содержания:
Код:
romimage: file=$BXSHARE/BIOS-bochs-latest

megs: 512

vgaromimage: file= $BXSHARE/VGABIOS-lgpl-latest

floppya: 1_44= "MyOS.img" , status=inserted

boot: floppy

mouse: enabled=0
Все пробуем запустить Если Вы проявите интерес то можно будет продолжить. Честно говоря я пока не сильно далеко по сути ушел от этого этапа, но терминал, рискну сказать полноценный, уже есть.

Последний раз редактировалось BOBAH13; 19.08.2008 в 15:50.
BOBAH13 вне форума Ответить с цитированием
Старый 19.08.2008, 23:06   #6
drRobert
Пользователь
 
Аватар для drRobert
 
Регистрация: 07.08.2008
Сообщений: 33
По умолчанию

1. ^_^ Написание ос - дело трудное, а в одну харю и вовсе бесперспективное. Занимался этим вопросом, сваял подобие ядра со всеми приблудами - защищенный режим, станичная адрессация, менеджер памяти (страничный аллокатор+менеджер кучи), подобие интерфеса для драйверов, IPC (на основе механизма передачи сообщений), работа с ext2. По железу - флоп, клава....
писал на nasm-e и dev-c++ (типа gcc+Visual Studio.. между нами девочками, VS просто отдыхает)
эмулятор - qemu manager-4.0

2. Загрузчик - дело последнее, как бы странно это ни звучало. На первых порах сгодится подобие, писанное левой задней ногой. Можно даже без файловой системы - лишь бы грузило конкретный участок кода в конкретный участок памяти. ИМХО в первую очередь надо написать мини-микроядро (понимайте как хотите =)) ) и интерфейсы к нему, а после этого уже становится возможным распределение задач между программистами.

3. Чтож.. я готов принять участие, но к таким вещам надо подходить как проектировщик, а не быдлокодер. (прошу не обижаться - сам был таким, когда хватался за это)
"... Раньше мы жили завтра, а теперь и сегодня - вчера
Вместо Роллингов - хакеры, вместо Битлов - юзера..."
(с) Б.Г.
drRobert вне форума Ответить с цитированием
Старый 20.08.2008, 08:14   #7
BOBAH13
Android Developer
Старожил Подтвердите свой е-майл
 
Аватар для BOBAH13
 
Регистрация: 19.02.2007
Сообщений: 3,708
По умолчанию

Спасибо Но ведь я и писал что только учусь в эту сторону. На счет клавы уже все работает и текст выводим ну printf уже есть Дело встало т.к. требуется вызывать BIOS прерывания из под защищенного режима. Поможете с этим ? Вскоре могу продолжить выкладывать сорсы.

Я думаю строить ядро как наноядро. Будет класс как контейнер включать в себя другие классы. Т.е. Класс Ядро а от него идут уже другие, ну и конечно динамическое подключение модулей ядра. т.е. все API будет ввиде иерархии классов. Думаю это довольно таки не плохо, а то сообщения и дискрипторы уже надоели.

Последний раз редактировалось BOBAH13; 20.08.2008 в 08:45.
BOBAH13 вне форума Ответить с цитированием
Старый 20.08.2008, 21:53   #8
drRobert
Пользователь
 
Аватар для drRobert
 
Регистрация: 07.08.2008
Сообщений: 33
По умолчанию

1. BIOS-прерывания в защищенном режиме недоступны, за исключением BIOS32-прерываний. Cобственно я ими не пользовался.. Для чего, если не секрет, они понадобились?

2. Классы, это конечно здорово.. Только ведь IPC (межпроцессная коммуникация) на чем-то должна основываться, будь то каналы, шаренная память или пресловутые сообщение. У себя я их и реализовывал - при нажатии на клавишу в системную очередь отправлялось сообщение MSG_KEYDOWN. А обработчик, будь то консоль или ещё что, волен делать в ответ на сообщение что угодно (выводить на экран, пищать динамиком и т.д. - не суть важно).
"... Раньше мы жили завтра, а теперь и сегодня - вчера
Вместо Роллингов - хакеры, вместо Битлов - юзера..."
(с) Б.Г.

Последний раз редактировалось drRobert; 20.08.2008 в 22:03.
drRobert вне форума Ответить с цитированием
Старый 21.08.2008, 09:26   #9
BOBAH13
Android Developer
Старожил Подтвердите свой е-майл
 
Аватар для BOBAH13
 
Регистрация: 19.02.2007
Сообщений: 3,708
По умолчанию

Ну например в Си в dos.h описан int86 вызов прерывания. Например тот же видеорежим установить ( VESA ). Ну конечно сообщения это привычно. Ну ведь полюбому придется обращаться к BIOS прерываниям, поэтому и хочу реализовать это. А вот как.. пока неизвестно
BOBAH13 вне форума Ответить с цитированием
Старый 21.08.2008, 17:16   #10
drRobert
Пользователь
 
Аватар для drRobert
 
Регистрация: 07.08.2008
Сообщений: 33
По умолчанию

Поехали по порядку.

В защищенном режиме вектора 0х0-0х1f резервированы для обработки исключений, т.е. обращение к BIOS-прерыванию 0х10 (работа с экраном) приведёт к исключению. Можно конечно определить собственные обработчики программных прерываний, но это удар - по скорости.

VESA я включал до перехода в PM (для PM есть VESA32-расширение, я с ним особо не разбирался - без надобности было). После перехода работал только с портами ввода\вывода. (что для клавы, что для флоппа). На чем конкретно стопор? Если "чисто прерываний бивис хочу" - не мучайся
"... Раньше мы жили завтра, а теперь и сегодня - вчера
Вместо Роллингов - хакеры, вместо Битлов - юзера..."
(с) Б.Г.

Последний раз редактировалось drRobert; 21.08.2008 в 17:25.
drRobert вне форума Ответить с цитированием
Ответ


Купить рекламу на форуме - 42 тыс руб за месяц

Опции темы Поиск в этой теме
Поиск в этой теме:

Расширенный поиск


Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
Объявление собственной глобальной процедуры Gorin Общие вопросы Delphi 11 06.11.2007 17:49
РАЗРАБОТКА DLL Leo Общие вопросы Delphi 2 26.10.2007 21:35
Разработка протокола OrdJONY Общие вопросы по программированию, компьютерный форум 2 04.10.2007 13:21