Hello World, або Мені ОС більше не потрібна, можу написати свою.
Кожен справжній програмер хоч раз у житті хоче спробувати написати свою ОСьку, але рідко хто доходить навіть до етапу написання загрузчика з диска/флешки/дискетки. Я можу похвалитись тим, що дійшов до емуляції дискетки і виводу рядка на екран віртуальної машини! Якщо хочете повторити мій “подвиг”, починайте читати даний туторіал (повторю, тут описана не ОС, а загрузчик – просто виконання невеликої порції коду на процесорі без всяких там гіндовсів та гінуксів)
Й так, що необхідно для роботи?
Намагатимусь описати крос-платформенні продукти.
- Для роботи необхідно знання принципів машинного коду – тобто, список базових інструкцій (MOV INT OR CALL), як користуватись мітками і тп. Хоча, щоб просто запустити код – таких знань непотрібно.
- Ассемблер – fASM. Крос-платформовий. Wiki-RUS,Wiki-ENG. (можете використати щось інше, але за результат уже не ручаюсь)
- Віртуальна машина – BOCHS. Крос-платформова. Wiki-RUS,Wiki-ENG (можете використати щось інше, але за результат уже не ручаюсь)
- Опціонально дизасемблер – під гінукс не знаю хорошого, але під гінду є чудесний IDA Pro.
Власне трохи теорії.
Коли комп включається, біос знаходить boot-drive, завантажує звідти перші 512 байт в пам”ять на адресу 0x7C00 і дає процесору команду jmp 0x7C00. Тобто, теоретично достатньо записати асемблерну програму в перші 512 байт boot-sectorа дискетки/диска/флешки і вона почне працювати.
Нюанси. Бут-сектор повинен мати розмір 512 байт, не більше не менше. Тобто, якщо ваша програма більша – то треба спочатку написати підпрограмку завантаження в пам”ять основної програми і передати вказівник IP на нову адресу.
Бут-сектор закінчується так: 0x55AA. Якщо цього не зробити – БІОС відмовиться працювати далі. Принциповий він….
Вся програма повинна бути записана в бінарних інструкціях процесора – заголовки і call ExitProcess тут не потрібні.
Власне і вся теорія )
hello.asm
; I won't use Ukranian, because of different encoding hells ;OK, this is simple BOOT program. All it does - puts a char on screen org 7C00h ; tell compiler the start adress of loaded program ; allways 0x7C00 for booting programs use16 ; Simplify all ;++++++++++++++++++++ ;-- MAIN ------- ;++++++++++++++++++++ main: ; Main program label mov ax,0x0000 ; Setup the Data Segment register ; Location of data is DS:Offset mov ds,ax ; This can not be loaded directly it has to be in two steps. ; 'mov ds, 0x0000' will NOT work due to limitations on the CPU mov si, HelloWorld ; Load the string into position for the procedure. call PutStr ; Call/start the procedure jmp $ ; jmp to current instruction, means single instr forever loop ;-------------------- ;-- PutChar ------- ;-------------------- PutChar: ; Label to call procedure mov ah,0x0E ; Special function: put char on text screen mov bh,0x00 ; Page number (Ignore for now) mov bl,0x07 ; Text attr: 07 = White text, black background. mov al,65 ; ASCII character code: char "A" int 0x10 ; Call the BIOS video interrupt. ret ; Return to main program ;-------------------- ;-- PutStr ------- ;-------------------- PutStr: ; Procedure label/start ; Set up the registers for the interrupt call mov ah,0x0E ; Special function: put char on text screen mov bh,0x00 ; Page number (Ignore for now) mov bl,0x07 ;Text attr: 07 = White text, black background. nextchar: ; Internal label (needed to loop round for the next character) lodsb ; I think of this as LOaD String Block ; (Not sure if thats the real meaning though) ; Loads [SI] into AL and increases SI by one ; Check for end of string '0' or al,al ; Sets the zero flag if al = 0 ; (OR outputs 0's where there is a zero bit in the register) jz return ; If the zero flag has been set go to the end of the procedure. ; Zero flag gets set when an instruction returns 0 as the answer. int 0x10 ; Run the BIOS video interrupt jmp nextchar ; Loop back round tothe top return: ; Label at the end to jump to when complete ret ; Return to main program ;--------------------------- ;-- Different data ------- ;--------------------------- HelloWorld db 'Pruvit, danbst, tu tse zrobuv!',0 ; 0 means end character times (512-2-($-7C00h)) db 0 ; OK, here we fill the rest of boot sector with 0 ; boot sector MUST be 512 bytes long.... db 055H,0AAH ; ... and end with 0x55AA signature
image.bin
ОК, тепер у нас є файлик з кодом. Зкомпілемо його в бінарний файлик. (тут і далі використовую batch-скрипти гінди, проблем з переводом їх на гінукс надіюсь не буде )))
c:\fasm\fasm.exe hello.asm image.bin
BOCHS config.txt
Щоб БОХ правильно працював задамо йому конфігурації. Створюємо файлик config.txt в папці БОХу (насправді розширення ролі не грає, це просто для зручності гіндоус-юзерів):
floppya: 1_44= C:\fasm\EXAMPLES\Load\image.bin, status=inserted floppyb: 1_44= a:, status=ejected romimage: file=BIOS-bochs-latest megs: 32 boot: floppy vga_update_interval: 50000 keyboard_serial_delay: 250 cpu: count=1, ips=500000 mouse: enabled=0 private_colormap: enabled=0 i440fxsupport: enabled=0 clock: time0=0 log: - panic: action=ask error: action=report info: action=report debug: action=ignore
Замітка. Цей конфіг робочий для БОХу 2.4.2. Для інших версій деякі опції можуть відрізнятись. І ще – поміняйте адресу до файлу image.bin в першому рядку.
run
Для полегшення запуску вірт. машини з даним конфігом я створив батник run.bat:
bochs -q -f config.txt
Все ) Після запуску run.bat я бачу таку картинку:
Останній важливий крок
Тепер треба записати image.bin на якийсь носій і спробувати завантажитись з нього. Я це поки що робити не буду, оскільки не знаю як правильно замінювати бут-сектор флешки.
Update
Останній важливий крок
Оскільки ми переконались у тому, що наша boot-програмка працює, лишилось перевірити її на справжній платформі. У мене нема флоппі-дисководу, на диск записувати напряжно, тому я вибрав в якості носія свою флешку.
Качаєм програмку (для гінди) – MKBT
Запускаємо її
mkbt -c -x image.bin I:
image.bin – ваш бінарний образ, створений fASMом
I: – ваш флеш-драйв
Дивіться не напутайте з буквою флеш-драйва – а то при кожному включенні компа будете дивитись вашу “hello world” програмку…
Після цього при завантаженні компа затисніть F12 (або що там у вас) і виберіть флешку. У мене вона називалась USB-HDD0.
Ось тепер все!
Гінукс юзери (хоча, і не тільки вони) можуть проглянути код загрузчика GRUB stage1, оскільки в ньому знаходиться багато цікавої інформації про зчитування stage2 загрузчика з диска.
; Nice English anyway.
Спробую якось перед Різдвом повторити. Якщо мій HP доживе. Я правда вже раз пробував:
, але нічого не вийшло.
З флешкою ідея класна. Ще б добре було просунутись до завантаження grub, і вияснити як пишеться ядро.
Лінус до речі, теж добрався до системного програмування на третьому курсі. On January 2, 1991 he purchased an Intel 80386-based IBM PC[10] and spent a month playing the game Prince of Persia before receiving his MINIX copy which in turn enabled him to begin his work on Linux.[4] Хоча йому тоді було вже 32.
І прикинь, вчора був день радіофізики. Я зайшов до них, а в них там виставка старої техніки. І я бачив живий саморобний ZX Spectrum, який по ідеї мав би грузити програмки з магнітофона. За ненаявністю магнітофона грузився з ноута, на якому грали waw-файли. Сама ОС – якийсь там DOS, клавіатура на 40 кнопок, яка працює в 5 режимах (так і не зміг добратись до Бейсіка, бо написи стерті). Але найкрутіше – він завантажується за секунду, бо ОС і Бейсік вшиті в ПЗУ.
bunyk
26 Грудня, 2009 at 19:25
>>>живий саморобний ZX Spectrum
ги, історія )
>>>Ще б добре було просунутись до завантаження grub, і вияснити як пишеться ядро.
третє моє посилання веде на сайт “Туторіали по написанню своїх ОС”, і в числі публікацій там розбирається також і ГРУБ
>>>…як пишеться ядро.
хмм.. мені зараз цікавіша робота з консоллю/графікою/клавою на найнайнайнижчому портовому рівні. Ядро я навіть не збираюсь писати – мультизадачності, розділення пам”яті, підтримка девайсів. Ну нах таке щастя.
danbst
26 Грудня, 2009 at 20:49
Ще б добре було просунутись до завантаження grub
під ..гінуксом нема нічого простіше:
# mkfs.ext2 /dev/sdb
# mount /dev/sdb /mnt/flashstick
# grub-install --root-directory=/mnt/flashstick /dev/sdb
# qemu -hda /dev/sdb
ulidtko
27 Грудня, 2009 at 17:49
На флешку я бы просто записал, скажем:
dd if=image.bin of=/dev/sdX bs=512 count=1
И, по идее, все бы заработало. Обратить флешку потом можно было бы форматированием.
Видишь, как всё получается просто — знай немного магии (0×7C00, 0×55AA) и остальное в твоих руках.
Алекс
27 Грудня, 2009 at 10:21
Ага, тепер зрозуміло.
Зчитати MBR
dd if=/dev/hda of=image.bin bs=512 count=1
Записати MBR
dd if=image.bin of=/dev/hda bs=512 count=1
Тут гінукс й справді гнучкіший. Тільки нарахунок просто – це якщо тільки виводити ХеллоВорлд на екран.
danbst
27 Грудня, 2009 at 10:29
еее… а про таблицю розділів ви не забули, панове? Найпоширеніший її вид — досівський MBR — зберігає дані про чотири primary partitions десь якраз всередині першого сектору. Звичайно, можна скористатися й іншими схемами розбиття диску (наприклад, GPT перспективна), можна створити й власну схему
, з блекдж. Але стандарти є стандарти.ulidtko
27 Грудня, 2009 at 17:37
мабуть, в даному випадку не говориться про зберігання всіх партицій та даних на носії. Особисто мені було цікаво відмовитись від абстракцій “файл” і “файлова система” та працювати напряму з секторами. А раптом я придумаю якусь структуру кращу за існуючі? =)))
*через кілька днів у мене пройде ця манія і я стану знову тверезо дивитись на речі*
danbst
27 Грудня, 2009 at 19:23
ну, і невеличкий загальний коментар. Мати навички роботи з дизасемблером важливо для загального комп’ютерного розвитку; тим не менше, це не необхідний скіл при написанні своєї ОС.
І так, IDA Pro я теж рекомендую.
ulidtko
27 Грудня, 2009 at 17:55
Уміння написати дизасм на асмі – хороша навичка при відлагодженні своєї ОС. Якраз тут навички для роботи з дизасмом знадобляться )))) Хоча так, скіл це не основний.
danbst
27 Грудня, 2009 at 19:17
Якщо є проблеми з запуском віртуальної машини в Гінуксі, то можете а) погуглити б) почитати даний лог переписки з джаббера
bunyk (00:45:40 10/01/2010)
а в мене навіть твій загрузчик не запустився
bunyk (01:03:43 10/01/2010)
========================================================================
Bochs x86 Emulator 2.4.1
Build from CVS snapshot on June 7, 2009
========================================================================
00000000000i[ ] LTDL_LIBRARY_PATH not set. using compile time default ‘/usr/lib/bochs/plugins’
00000000000i[ ] BXSHARE not set. using compile time default ‘/usr/share/bochs’
00000000000i[ ] reading configuration from bochs.conf
00000000000i[ ] lt_dlhandle is (nil)
00000000000p[ ] >>PANIC<< dlopen failed for module 'x': file not found
========================================================================
Event type: PANIC
Device: [ ]
Message: dlopen failed for module 'x': file not found
A PANIC has occurred. Do you want to:
cont – continue execution
alwayscont – continue execution, and don't ask again.
This affects only PANIC events from device [ ]
die – stop execution now
abort – dump core
debug – hand control to gdb
Choose one of the actions above: [die]
danbst (01:16:33 10/01/2010)
n Wed, Jul 28, 2004 at 04:39:16PM +0200, Johann Spies wrote:
> I am also trying out bochs. After creating a bximage (flat file) and
> copying the line indicated by bximage to /etc/bochs-init/bochsrc the
> following happened when I tried bochs:
[…]
> Please choose one: [2] 5
> 00000000000i[ ] lt_dlhandle is (nil)
> 00000000000p[ ] >>PANIC< >>found
Install the package bochs-x or change your bochsrc until it stops
attempting to use x as the display_library. bochs does not like certain
combinations of config_interface and display_library and will try to use
the display_library ‘x’ if it doesn’t like the combination.
Also, you should tell bochs where to look for its config file, even
though you are using /etc/bochs-init/bochsrc as bochs appears to not
find it, but bochs does find ~/.bochsrc.
danbst (01:17:18 10/01/2010)
Install the package bochs-x
bunyk (01:17:42 10/01/2010)
а під x вони мають на увазі графічну систему unix?
danbst (01:17:46 10/01/2010)
так
bunyk (01:22:42 10/01/2010)
ооо
bunyk (01:22:47 10/01/2010)
Device: [MEM0 ]
Message: ROM: couldn’t open ROM image file ‘BIOS-bochs-latest’.
danbst (01:23:01 10/01/2010)
чудесно, це вже набагато краще )
danbst (01:27:55 10/01/2010)
romimage: file=”~/Documents/bochs-2.3.7/bios/BIOS-bochs-legacy”, address=0xf0000
vgaromimage: file=”/home/mayank/Documents/bochs-2.3.7/bios/VGABIOS-lgpl-latest”
vga: extension=vbe
ось, знайшов якісь рядки з чийогось робочого конфігу
bunyk (01:33:27 10/01/2010)
значить це вставити кудись в конфіг, замінивши файли на наступні?
bunyk@bunyk-laptop:/usr/share/bochs$ ls
BIOS-bochs-latest BIOS-bochs-legacy BIOS-qemu-latest keymaps VGABIOS-lgpl-latest
danbst (01:33:39 10/01/2010)
так
bunyk (01:39:46 10/01/2010)
Pruvit, danbst, tu tse zrobuv!
danbst (01:39:59 10/01/2010)
не варто було мене хвалити
danbst
10 Січня, 2010 at 09:52
Ну, тут занадто довго розписано.
А повний алгоритм простіший:
1. Ставимо пакет bochs
2. Ставимо пакет bochs-x без якого будуть проблеми
3. Качаємо http://flatassembler.net/download.php
4. Створюємо якусь папку типу myos, розпаковуємо туди ассемблер.
5. Створюємо (Ctrl+C) код загрузчика наприклад в файлі myos/myos.asm
6. створюємо там же конфігурацію bochs наприклад bochs.conf.
пишемо там з відповідними замінами назв та адрес:
7. Щоб багато не морочитись з консоллю створюємо файл наприклад run:
Я прописав там абсолютні адреси, бо якось захотілось запустити ~/myos/run з одної далекої-далекої директорії, а воно сказало що не бачить деяких файлів.
І якщо ви не забули chmod +x run, тоді:
8. ./run Поїхали!
8,05. Ну, і на любителя Vim:
:map :w :!./run
bunyk
10 Січня, 2010 at 12:28