Училище за сглобяване: разработка на операционна система. Полетът на колибрито. На какво е способна една ОС, написана изцяло на асемблер Асемблиране и компилиране

Наскоро реших да науча асемблер, но не ми беше интересно да губя редове код. Мислех, че докато уча асемблер, ще овладея някаква предметна област. Така че изборът ми падна върху писането на буутлоудър. Резултатът от моите открития е тук в този блог.

Бих искал веднага да кажа, че обичам теорията, съчетана с практика, така че нека започнем.

Първо ще ви покажа как да създадете прост MBRза да можем да се насладим на резултата възможно най-скоро. Тъй като ставаме по-сложни с практически примери, ще дам теоретична информация.

Първо, нека направим буутлоудър за USB флаш устройство!

Внимание!!! Първата ни програма за асемблер ще работи както за флаш устройство, така и за други устройства като флопи диск или HDD. Впоследствие, за да работят правилно всички примери, ще дам редица пояснения относно работата на кода на различни устройства.

Ще пишем нататък Фазм, тъй като се счита за най-добрият компилатор за писане на зареждащи програми, което е MBR.Втората причина за избора на Fasm е, че прави компилирането на файлове много лесно. Без директиви командна линияи така нататък. глупости, които могат напълно да ви обезсърчат да научите асемблер и да постигнете целите си.Така че в началния етап ще ни трябват две програми и няколко ненужниФлашка с минимален размер. Изкопах 1Gb (формира се бързо и не е жалко, ако има нещо). След като нашият буутлоудър работи, флаш устройството вече няма да функционира нормално. Моят Windows 7 отказва да форматира флашката. Препоръчвам да използвате помощна програма, за да върнете живот на флаш устройството HP USB дискИнструмент за форматиране на съхранение ( HPUSBFW.EXE) или други помощни програми за форматиране на флаш устройства.

Ние ги инсталираме и хвърляме съответните преки пътища на работния плот или където желаете.

Подготовката е завършена, нека да преминем към действие

Отворете Fasmw.exe и напишете следното там. Ще начертаем минималния код, за да видим резултата. По-късно ще анализираме какво е надраскано тук. Ще дам коментарите си накратко.

FASM код: ============= boot.asm ==============

org 7C00h ; Нашите програмни адреси се изчисляват, като се вземе предвид тази директива

използване16; генерира се шестнадесетичен код

cli ;деактивиране на прекъсвания за промяна на адреси в сегментни регистри

mov брадва, 0

mov sp, 7C00h

sti ;разрешаване на прекъсвания (след промяна на адреси)

mov ax, 0003h ;задаване на видео режим за показване на линия на екрана

вътрешен 10ч

mov ax, 1301h; действителният изход на низа е функцията 13h int 10h (повече подробности по-късно)

mov bp, stroka ;адрес на изходния низ

mov dx, 0000h ;ред и колона, в които се показва текстът

mov cx, 15 ;брой знаци на изходния низ

mov bx, 000eh ;00-номер на видео страница (по-добре да не се пипа) 0e-атрибути на знаци (цвят, фон)

вътрешен 10ч

jmp $ ;tread water (зацикля програмата в този момент)

string db "Добре, MBR е зареден!"

по 510 - ($ - $$) db 0 ; запълване на пространството между предишния и следващия байт с нули

db 0x55 ,0xAA ;последните два байта

Компилирайте този код (Ctrl+F9) във fasm"e и запазете получения двоичен файл като boot.bin на някое удобно място. Преди да запишем нашия двоичен файл на флаш устройство, малко теория.

Когато включите флаш устройство в компютъра, абсолютно не е очевидно за BIOS системата, че искате да стартирате от флаш устройството, така че в настройките на BIOS трябва да изберете устройството, от което искате да стартирате. Така че ние избрахме за зареждане от USB (ще трябва да разберете как да направите това сами, тъй като BIOS интерфейсът има различни варианти... можете да го потърсите в Google BIOS настройкиза вашия дънна платка. Там няма нищо сложно, като правило).

Сега, когато BIOS знае, че искате да стартирате от флаш устройство, той трябва да се увери, че нулевият сектор на флаш устройството е стартиращ. За да направите това, BIOS сканира последните два байта от сектор нулаи ако те са равни на 0x55 0xAA, тогава само тогава ще бъде зареден в RAM. В противен случай BIOS просто ще заобиколи вашето флаш устройство. След като намери тези два магически байта, той зарежда сектор нула в RAM на адрес 0000:7С00h, след което забравя за флаш устройството и прехвърля управлението на този адрес. Сега цялата власт над компютъра принадлежи на вашия буутлоудър и той, действайки от RAM, може да зареди допълнителен код от флаш устройство. Сега ще видим как точно този сектор изглежда в програмата DMDE.

1. Поставете вашето флаш устройство в компютъра и се уверете, че не съдържа необходимата ви информация.

2. Отворете програмата DMDE. Прочетете всички следващи стъпки в снимките:

След като изгледате този комикс, ще имате умението да заредите своя MBR на флаш устройство. Ето как изглежда дългоочакваният резултат от нашия товарач:


Между другото, ако говорим за минималния код на буутлоудъра, той може да изглежда така:

Org 7C00h
jmp$
db 508 dup(0)
db 0x55.0xAA

Такъв буутлоудър, след като получи контрол, просто окачва компютъра, изпълнявайки една безсмислена команда jmp $ в цикъл. Наричам я тъпкана вода.

Публикувах видеоклип в YouTube, който може да ви помогне:

И накрая, няколко кратки факта за буутлоудъра:

1. Буутлоудърът, известен още като буутлоудър, известен също като MBR, има размер от 512 байта. Исторически,
че това условие трябва да бъде изпълнено, за да поддържа по-стари медии и устройства.
2. Буутлоудърът винаги се намира в нулевия сектор на флашка, флопи диск, харддиск, от гледна точка на програмата DMDE или други шестнадесетични редактори, които ви позволяват да работите с устройства. За да заредим двоичен файл (нашият boot.bin) на едно от изброените устройства, не е нужно да мислим за тяхната вътрешна физическа структура. Програмата DMDE просто знае как да чете секторите на тези устройства и ги показва в режим LBA (просто ги номерира от 0 до последния сектор). Можете да прочетете за LBA
3. Буутлоудърът винаги трябва да завършва с два байта 0x55 0xAA.
4. Буутлоудърът винаги се зарежда в паметта на адрес 0000:7С00h.
5. Операционната система започва с буутлоудъра.


Оригинал: AsmSchool: Направете операционна система
Автор: Майк Сондърс
Дата на публикуване: 15 април 2016 г
Превод: А. Панин
Дата на превод: 16 април 2016 г

Част 4: С уменията, които придобихте от четенето на предишните статии от тази серия, можете да започнете да разработвате своя собствена операционна система!

За какво е?

  • За да разберете как работят компилаторите.
  • За разбиране на инструкциите на процесора.
  • За да оптимизирате кода си за производителност.

В продължение на няколко месеца изминахме труден път, който започна с развитието прости програмина асемблерен език за Linux и завърши в последната статия от поредицата с разработването на самостоятелен код, който работи на персонален компютър без операционна система. Е, сега ще се опитаме да съберем цялата информация заедно и да създадем истинска операционна система. Да, ще следваме стъпките на Линус Торвалдс, но първо трябва да отговорим на следните въпроси: "Какво е операционна система? Кои нейни функции ще трябва да пресъздадем?"

В тази статия ще се съсредоточим само върху основните функции на операционната система: зареждане и изпълнение на програми. Сложните операционни системи изпълняват много повече функции, като управление и обработка на виртуална памет мрежови пакети, но правилното им внедряване изисква години непрекъсната работа, така че в тази статия ще разгледаме само основните функции, присъстващи във всяка операционна система. Миналия месец разработихме малка програма, която се побира в 512-байтовия сектор на флопи диска (първия му сектор), а сега ще го модифицираме малко, за да добавим функцията за зареждане на допълнителни данни от диска.

Разработка на Boot Loader

Можем да се опитаме да намалим размера на двоичния код на нашата операционна система колкото е възможно повече, за да го поберем в първия 512-байтов сектор на флопи диска, този, който се зарежда от BIOS, но в този случай няма да можем за реализиране на всякакви интересни функции. Следователно ще използваме тези 512 байта, за да съхраняваме двоичния код на прост системен зареждащ механизъм, който ще зареди двоичния код на ядрото на ОС в RAM и ще го изпълни. (След това ще разработим самото ядро ​​на ОС, което ще зареди двоичния код на други програми от диска и също ще го изпълни, но ще говорим за това малко по-късно.)

Можете да изтеглите изходния код за примерите, обсъдени в тази статия, на www.linuxvoice.com/code/lv015/asmschool.zip. И това е кодът на нашия системен зареждащ инструмент от файл, наречен boot.asm:

BITS 16 jmp къс старт; Преминаване към етикет, пропускане на описанието на диска nop; Добавка преди описанието на диска %include "bpb.asm" start: mov ax, 07C0h ; Зареждане на адрес mov ds, ax ; Сегмент от данни mov ax, 9000h ; Подготовка на стека mov ss, ax mov sp, 0FFFFh ; Стекът расте надолу! cld; Задаване на флага за посока mov si, kern_filename call load_file jmp 2000h:0000h ; Преход към двоичен код на ядрото на ОС, зареден от файла kern_filename db "MYKERNELBIN" %include "disk.asm" пъти 510-($-$$) db 0 ; Подпълване на двоичния код с нули до 510 байта dw 0AA55h ; Буфер за двоичен краен маркер на зареждащото устройство: ; Стартиране на буфер за съдържанието на диска

В този код първата инструкция на процесора е инструкцията jmp, която се намира след директивата BITS, която казва на асемблера на NASM, че се използва 16-битов режим. Както вероятно си спомняте от предишната статия от поредицата, изпълнението на 512-байтовия двоичен код, зареден от BIOS от диск, започва от самото начало, но трябва да преминем към етикет, за да пропуснем специален набор от данни. Очевидно миналия месец просто написахме кода в началото на диска (използвайки помощната програма dd) и оставихме останалата част от дисковото пространство празно.

Сега ще трябва да използваме дискета с подходяща файлова система MS-DOS (FAT12) и за да работим правилно с тази файлова система, трябва да добавим набор от специални данни близо до началото на сектора. Този набор се нарича BIOS Parameter Block (BPB) и съдържа данни като етикет на диска, брой сектори и т.н. Това не би трябвало да ни интересува на този етап, тъй като повече от една поредица от статии могат да бъдат посветени на такива теми, поради което сме поставили всички инструкции и данни, свързани с него, в отделен файл с изходен код, наречен bpb.asm.

Въз основа на горното, тази директива от нашия код е изключително важна:

% включва "bpb.asm"

Това е NASM директива, която позволява съдържанието на определен изходен файл да бъде включено в текущия изходен файл по време на асемблиране. По този начин можем да направим кода на буутлоудъра възможно най-кратък и разбираем, като поставим всички подробности за изпълнението на блока с параметри на BIOS в отделен файл. Блокът с параметри на BIOS трябва да се намира три байта след началото на сектора и тъй като инструкцията jmp заема само два байта, трябва да използваме инструкцията nop (името й означава "без операция" - това е инструкция, която прави нищо друго освен загуба на CPU цикли ), за да запълни оставащия байт.

Работа със стека

След това ще трябва да използваме инструкции, подобни на тези, обсъдени в последната статия, за да подготвим регистрите и стека, както и инструкцията cld (означава "ясна посока"), която ни позволява да зададем флага за посока за определени инструкции, като като инструкцията lodsb, която, когато бъде изпълнена, ще увеличи стойността в SI регистъра, вместо да я намали.

След това поставяме адреса на низа в SI регистъра и извикваме нашата функция load_file. Но помислете за минута - все още не сме разработили тази функция! Да, това е вярно, но неговата реализация може да бъде намерена в друг файл с изходен код, който включихме, наречен disk.asm.

Файловата система FAT12, използвана на флопи дискове, които са форматирани в MS-DOS, е една от най-простите налични файлови системи, но работата с неговото съдържание също изисква значително количество код. Подпрограмата load_file е дълга около 200 реда и няма да бъде показана в тази статия, тъй като разглеждаме процеса на разработване на операционна система, а не драйвер за конкретна файлова система, следователно не е много разумно да губите място в регистрирайте страници по този начин. Като цяло включихме файла с изходен код disk.asm почти преди края на текущия файл с изходен код и можем да забравим за него. (Ако все още се интересувате от структурата на файловата система FAT12, можете да прочетете отличния преглед на http://tinyurl.com/fat12spec и след това да разгледате файла с изходен код disk.asm - кодът, съдържащ се в него, е добре коментирано.)

И в двата случая рутинната програма load_file зарежда двоичния код от файла, посочен в регистъра SI, в сегмент 2000 при отместване 0, след което прескачаме в началото му за изпълнение. И това е всичко - ядрото на операционната система е заредено и системният буутлоудър е изпълнил задачата си!

Може би сте забелязали, че нашият код използва MYKERNELBIN вместо MYKERNEL.BIN като име на файла на ядрото на операционната система, което се вписва добре в схемата за именуване 8+3, използвана на флопи дискове в DOS. Всъщност файловата система FAT12 използва вътрешно представяне на имена на файлове и ние спестяваме място, като използваме име на файл, което гарантирано не изисква нашата рутинна load_file да внедри механизъм за търсене на знака точка и преобразуване на името на файла в вътрешно представяне на файловата система.

След реда с директивата за свързване на файла с изходен код disk.asm има два реда, предназначени да допълват двоичния код на системния зареждащ механизъм с нули до 512 байта и да включват крайния знак на неговия двоичен код (това беше обсъдено в предишната статия). И накрая, в самия край на кода е етикетът "buffer", който се използва от рутината load_file. По принцип рутината load_file се нуждае от свободно място в RAM, за да извърши някаква междинна работа, докато търси файл на диска, и имаме много свободно място след зареждането на зареждащия механизъм, така че поставяме буфера тук.

За да сглобите системата за зареждане на системата, използвайте следната команда:

Nasm -f bin -o boot.bin boot.asm

Сега трябва да създадем изображение на виртуална дискета във формат MS-DOS и да добавим двоичния код на нашия буутлоудър към първите 512 байта, като използваме следните команди:

Mkdosfs -C floppy.img 1440 dd conv=notrunc if=boot.bin of=floppy.img

На този етап процесът на разработване на системно зареждащо устройство може да се счита за завършен! Вече имаме стартиращо изображение на дискета, което ни позволява да заредим двоичния код на ядрото на операционната система от файл, наречен mykernel.bin, и да го изпълним. След това ни очаква по-интересна част от работата - разработката на самото ядро ​​на операционната система.

Ядрото на операционната система

Искаме ядрото на нашата операционна система да изпълнява много важни задачи: да показва поздравително съобщение, да приема въвеждане от потребителя, да определя дали въвеждането е поддържана команда и да изпълнява програми от диска, когато потребителят посочи техните имена. Това е кодът на ядрото на операционната система от файла mykernel.asm:

Mov ax, 2000h mov ds, ax mov es, ax цикъл: mov si, подкана lib_print_string mov si, user_input извикване lib_input_string cmp байт, 0 je цикъл cmp дума, "ls" je list_files mov ax, si mov cx, 32768 извикване lib_load_file jc load_fail повикване 32768 jmp цикъл load_fail: mov si, load_fail_msg повикване lib_print_string jmp цикъл list_files: mov si, file_list повикване lib_get_file_list повикване lib_print_string jmp цикъл подкана db 13, 10, "MyOS > ", 0 load_fail_msg db 13, 10 , „Не е намерено! ", 0 user_input пъти 256 db 0 file_list пъти 1024 db 0 %include "lib.asm"

Преди да разгледате кода, трябва да обърнете внимание на последния ред с директивата за включване на файла с изходен код lib.asm, който също се намира в архива asmschool.zip от нашия уебсайт. Това е библиотека от полезни процедури за работа с екрана, клавиатурата, низовете и дисковете, които можете също да използвате - в този случай ние включваме този файл с изходен код в самия край на основния файл с изходен код на ядрото на операционната система, за да за да направи последния възможно най-компактен и красив. Обърнете се към раздела Lib.asm Library Routines за Допълнителна информацияотносно всички налични подпрограми.

В първите три реда от кода на ядрото на операционната система ние попълваме сегментните регистри с данни, които да сочат към сегмент 2000, в който е зареден двоичният код. Това е важно за гарантирано правилна работаинструкции като lodsb, които трябва да четат данни от текущия сегмент, а не от който и да е друг. След това няма да извършваме никакви допълнителни операции върху сегментите; нашата операционна система ще работи с 64 KB RAM!

По-нататък в кода има етикет, съответстващ на началото на цикъла. Първо, използваме една от рутинните процедури от библиотеката lib.asm, а именно lib_print_string, за да отпечатаме поздрава. Байтове 13 и 10 преди поздравителния ред са екраниращи знаци. нова линия, благодарение на което поздравът няма да се показва веднага след изхода на която и да е програма, а винаги на нов ред.

След това използваме друга рутина от библиотеката lib.asm, наречена lib_input_string, която приема въвеждането от клавиатурата на потребителя и го съхранява в буфер, посочен в регистъра SI. В нашия случай буферът се декларира близо до края на кода на ядрото на операционната система, както следва:

User_input пъти 256 db 0

Тази декларация ви позволява да създадете буфер от 256 знака, пълни с нули - дължината му трябва да е достатъчна за съхраняване на команди за проста операционна система като нашата!

След това извършваме валидиране на въведеното от потребителя. Ако първият байт на буфера user_input е нула, тогава потребителят просто е натиснал клавиша Enter, без да въвежда никаква команда; Не забравяйте, че всички низове завършват с нулеви знаци. Така че в този случай трябва просто да отидем в началото на цикъла и да отпечатаме поздрава отново. Въпреки това, в случай че потребителят въведе каквато и да е команда, ще трябва първо да проверим дали е въвел командата ls. Досега можете да наблюдавате само сравнения на отделни байтове в нашите програми за асемблер, но не забравяйте, че е възможно също да сравнявате двубайтови стойности или машинни думи. В този код сравняваме първата машинна дума от буфера user_input с машинната дума, съответстваща на реда ls и, ако са идентични, преминаваме към кодовия блок по-долу. В рамките на този блок код използваме друга рутина от lib.asm, за да получим разделен със запетая списък с файлове на диска (който трябва да се съхранява в буфера file_list), да отпечатаме този списък на екрана и да се върнем обратно в цикъла, за да обработва въведеното от потребителя.

Изпълнение на програми на трети страни

Ако потребителят не въведе командата ls, предполагаме, че е въвел името на програмата от диска, така че има смисъл да се опита да я зареди. Нашата библиотека lib.asm съдържа имплементация на полезната рутина lib_load_file, която анализира дискови таблици на FAT12: взема указател към началото на ред с име на файл през регистъра AX, както и стойност на отместване за зареждане на двоичен код от програмен файл през CX регистъра. Ние вече използваме SI регистъра, за да съхраним указател към низа, съдържащ въведеното от потребителя, така че копираме този указател в AX регистъра и след това поставяме стойността 32768, която се използва като отместване за зареждане на двоичния код от програмния файл, в CX регистъра.

Но защо използваме тази конкретна стойност като отместване за зареждане на двоичен код от програмен файл? Е, това е само една от опциите за карта на паметта за нашата операционна система. Тъй като работим в един сегмент от 64 KB и нашият двоичен файл на ядрото се зарежда при отместване 0, трябва да използваме първите 32 KB памет за данни на ядрото и останалите 32 KB за зареждане на програмни данни. По този начин отместването 32768 е средата на нашия сегмент и ни позволява да осигурим достатъчно RAM както за ядрото на операционната система, така и за заредените програми.

След това процедурата lib_load_file изпълнява много важна операция: ако не може да намери файл с даденото име на диска или по някаква причина не може да го прочете от диска, тя просто излиза и задава специален флаг за пренасяне. Това е флаг за състояние на процесора, който се задава по време на изпълнение на някои математически операции и не трябва да ни интересува в момента, но в същото време можем да определим наличието на този флаг, за да вземем бързи решения. Ако рутинната програма lib_load_asm зададе флага за пренасяне, ние използваме инструкцията jc (скочи при пренасяне), за да преминем към блок от код, който отпечатва съобщението за грешка и се връща към началото на цикъла на въвеждане от потребителя.

В същия случай, ако флагът за прехвърляне не е зададен, можем да заключим, че подпрограмата lib_load_asm успешно е заредила двоичния код от програмния файл в RAM на адрес 32768. Всичко, от което се нуждаем в този случай, е да инициираме изпълнението на двоичния код код, зареден на този адрес, т.е. започнете да изпълнявате указаната от потребителя програма! И след като инструкцията ret бъде използвана в тази програма (за връщане към извикващия код), ние просто ще трябва да се върнем към цикъла за обработка на въведеното от потребителя. Така създадохме операционна система: тя се състои от най-простите механизми за разбор на команди и зареждане на програми, внедрени в около 40 реда асемблен код, макар и с голяма помощ от рутинни процедури от библиотеката lib.asm.

За да сглобите кода на ядрото на операционната система, използвайте следната команда:

Nasm -f bin -o mykernel.bin mykernel.asm

След това ще трябва по някакъв начин да добавим файла mykernel.bin към файла с изображението на флопи диска. Ако сте запознати с трика за монтиране на дискови изображения с помощта на loopback устройства, можете да получите достъп до съдържанието на дисковото изображение с помощта на floppy.img, но има по-лесен начин с помощта на GNU Mtools (www.gnu.org/software /mtools). Това е набор от програми за работа с флопи дискове, които използват MS-DOS/FAT12 файлови системи, достъпни от хранилища на пакети софтуервсички популярни Linux дистрибуции, така че всичко, което трябва да направите, е да използвате apt-get, yum, pacman или друга помощна програма, която използвате, за да инсталирате софтуерни пакети във вашата дистрибуция.

След като инсталирате подходящия софтуерен пакет, ще трябва да изпълните следната команда, за да добавите файла mykernel.bin към файла с изображението на диска floppy.img:

Mcopy -i floppy.img mykernel.bin::/

Обърнете внимание на забавните символи в края на командата: двоеточие, двоеточие и наклонена черта. Вече сме почти готови да стартираме нашата операционна система, но какъв е смисълът, ако няма приложения за нея? Нека коригираме това недоразумение, като разработим изключително просто приложение. Да, сега ще разработвате приложение за собствената си операционна система - само си представете колко ще се издигне авторитетът ви сред редиците на маниаците. Запишете следния код във файл с име test.asm:

Org 32768 mov ah, 0Eh mov al, "X" int 10h ret

Този код просто използва функцията на BIOS, за да отпечата "X" на екрана и след това връща контрола на кода, който го е извикал - в нашия случай този код е кодът на операционната система. Организационният ред, който започва изходния код на приложението, не е инструкция на процесора, а директива за асемблер на NASM, която му казва, че двоичният код ще бъде зареден в RAM при отместване 32768 и следователно всички отмествания трябва да бъдат преизчислени, за да отчетат това.

Този код също трябва да бъде асемблиран и полученият двоичен файл трябва да бъде добавен към файла с изображение на дискета:

Nasm -f bin -o test.bin test.asm mcopy -i floppy.img test.bin ::/

Сега поемете дълбоко дъх, пригответе се да съзерцавате несравнимите резултати от собствената си работа и стартирайте изображението на флопи диска с помощта на компютърен емулатор като Qemu или VirtualBox. Например, следната команда може да се използва за тази цел:

Qemu-system-i386 -fda floppy.img

Ето: системният буутлоудър boot.img, който интегрирахме в първия сектор на изображението на диска, зарежда ядрото на операционната система mykernel.bin, което показва приветстващо съобщение. Въведете командата ls, за да получите имената на два файла, разположени на диска (mykernel.bin и test.bin), след което въведете името на последния файл, за да го изпълните и да покажете X на екрана.

Готино е, нали? Сега можете да започнете да финализирате командна обвивкавашата операционна система, добавете реализации на нови команди и също добавете допълнителни програмни файлове към диска. Ако искате да стартирате тази операционна система на истински компютър, трябва да се обърнете към раздела „Стартиране на буутлоудъра на реална хардуерна платформа“ от предишната статия от поредицата - ще ви трябват точно същите команди. Следващия месец ще направим нашата операционна система по-мощна, като позволим на програмите за изтегляне да използват системни функции, като въведем концепция за споделяне на код, за да намалим дублирането на код. Голяма част от работата тепърва предстои.

lib.asm библиотечни процедури

Както споменахме по-рано, библиотеката lib.asm предоставя голям набор от полезни процедури за използване във вашите ядра операционна системаи индивидуални програми. Някои от тях използват инструкции и концепции, които все още не са засегнати в статиите от тази поредица, други (като дискови процедури) са тясно свързани с дизайна на файловите системи, но ако смятате, че сте компетентни по тези въпроси, можете прочетете ги сами с техните реализации и разберете принципа на работа. По-важно е обаче да разберете как да ги извикате от собствения си код:

  • lib_print_string - приема указател към низ с нулев край чрез SI регистъра и отпечатва низа на екрана.
  • lib_input_string - приема указател към буфер чрез SI регистъра и запълва този буфер със знаци, въведени от потребителя с помощта на клавиатурата. След като потребителят натисне клавиша Enter, редът в буфера завършва с нула и управлението се връща към извикващия програмен код.
  • lib_move_cursor - премества курсора на екрана до позиция с координати, предавани през регистрите DH (номер на ред) и DL (номер на колона).
  • lib_get_cursor_pos - тази подпрограма трябва да бъде извикана, за да се получат текущите номера на ред и колона, като се използват съответно DH и DL регистрите.
  • lib_string_uppercase - взема указател към началото на низ, завършващ с нула, използвайки регистъра AX и преобразува символите на низа в главни букви.
  • lib_string_length - взема указател към началото на низ с нулев край чрез регистъра AX и връща дължината му чрез регистъра AX.
  • lib_string_compare - приема указатели към началото на два низа с нулев край, използвайки регистрите SI и DI и сравнява тези низове. Задава флага за пренасяне, ако линиите са идентични (за използване на инструкция за прескачане в зависимост от флага за пренасяне jc) или изчиства този флаг, ако линиите са различни (за използване на инструкцията jnc).
  • lib_get_file_list - Взема указател към началото на буфер чрез SI регистъра и поставя в този буфер низ, завършващ с нула, съдържащ разделен със запетаи списък с имена на файлове от диска.
  • lib_load_file - взема указател към началото на ред, съдържащ името на файла, използвайки регистъра AX и зарежда съдържанието на файла при отместването, предадено през регистъра CX. Връща броя байтове, копирани в паметта (т.е. размера на файла), използвайки регистъра BX, или задава флага за пренасяне, ако файл с даденото име не бъде намерен.

Ще кажа веднага, не затваряйте статията с мислите „По дяволите, друг Попов“. Той просто има излъскан Ubuntu, докато аз имам всичко от нулата, включително ядрото и приложенията. И така, продължение под разреза.

OS група: Тук.
Първо ще ви дам една екранна снимка.

Няма повече от тях и сега нека поговорим по-подробно защо го пиша.

Беше топла априлска вечер, четвъртък. От детството си мечтаех да напиша ОС, когато изведнъж си помислих: „Сега знам предимствата и asm, защо да не сбъдна мечтата си?“ Търсих в Google сайтове по тази тема и намерих статия от Habr: „Как да започнете и да не спирате да пишете операционна система.“ Благодаря на неговия автор за връзката към OSDev Wiki по-долу. Отидох там и започнах работа. Имаше всички данни за минималната ОС в една статия. Започнах да създавам cross-gcc и binutils и след това пренаписах всичко оттам. Трябваше да видите радостта ми, когато видях надписа "Hello, kernel World!" Направо скочих от стола си и осъзнах, че няма да се откажа. Написах "конзола" (в кавички; нямах достъп до клавиатура), но след това реших да напиша прозоречна система. В крайна сметка се получи, но нямах достъп до клавиатурата. И тогава реших да измисля име, базирано на X Window System. Търсих в гугъл Y Window System - съществува. В резултат на това нарекох Z Window System 0.1, включена в OS365 pre-alpha 0.1. И да, никой не я видя освен мен. Тогава разбрах как да внедря поддръжка на клавиатура. Екранна снимка на първата версия, когато все още нямаше нищо, дори и прозоречна система:

Текстовият курсор дори не се премести, както можете да видите. Тогава написах двойка прости приложениябазиран на Z. А ето и изданието 1.0.0 алфа. Имаше много неща, дори системни менюта. А файлов мениджъри калкулаторът просто не работеше.

Бях директно тероризиран от приятел, който държи само на красотата (Митрофан, съжалявам). Той каза: „Измийте режима VBE 1024*768*32, измийте го, измийте го! Е, нека го изпием!“ Е, вече ми беше писнало да го слушам и все пак го нарязах. Относно изпълнението по-долу.

Направих всичко с моя буутлоудър, а именно GRUB. С негова помощ можете да настроите графичния режим без усложнения, като добавите няколко магически реда към заглавката Multiboot.

Задайте ALIGN, 1<<0 .set MEMINFO, 1<<1 .set GRAPH, 1<<2 .set FLAGS, ALIGN | MEMINFO | GRAPH .set MAGIC, 0x1BADB002 .set CHECKSUM, -(MAGIC + FLAGS) .align 4 .long MAGIC .long FLAGS .long CHECKSUM .long 0, 0, 0, 0, 0 .long 0 # 0 = set graphics mode .long 1024, 768, 32 # Width, height, depth
И след това от информационната структура на Multiboot вземам адреса на буфера на кадрите и разделителната способност на екрана и записвам пиксели там. VESA направи всичко много объркващо - RGB цветовете трябва да се въвеждат в обратен ред (не R G B, а B G R). Няколко дни не разбирах защо не се показват пикселите!? В крайна сметка разбрах, че съм забравил да променя стойностите на 16 цветни константи от 0...15 към техните RGB еквиваленти. В резултат на това го пуснах и в същото време намалих градиентния фон. След това направих конзола, 2 приложения и пуснах 1.2. О, да, почти забравих - можете да изтеглите операционната система от

Асемблер

Асемблер(от англ. assemble - сглобявам) - компилатор от асемблер в команди на машинен език.
Има асемблер за всяка процесорна архитектура и за всяка ОС или семейство ОС. Има и така наречените „кръстосани асемблери“, които ви позволяват да сглобявате програми за друга целева архитектура или друга ОС на машини с една архитектура (или в средата на една ОС) и да получавате изпълним код във формат, подходящ за изпълнение на целевата архитектура или в целевата среда OS.

x86 архитектура

Асемблери за DOS

Най-известните асемблери за операционната система DOS са Borland Turbo Assembler (TASM) и Microsoft Macro Assembler (MASM). Простият асемблер A86 също беше популярен по едно време.
Първоначално те поддържаха само 16-битови инструкции (до появата на процесора Intel 80386). По-късните версии на TASM и MASM поддържат както 32-битови инструкции, така и всички инструкции, въведени в по-модерни процесори, и специфични за архитектурата системи за инструкции (като например MMX, SSE, 3DNow! и др.).

Microsoft Windows

С появата на операционната система Microsoft Windows се появи разширение TASM, наречено TASM32, което направи възможно създаването на програми, които да работят в Windows среда. Последната известна версия на Tasm е 5.3, която поддържа MMX инструкции и в момента е включена в Turbo C++ Explorer. Но официално развитието на програмата е напълно спряно.
Microsoft поддържа продукт, наречен Microsoft Macro Assembler. Той продължава да се развива и до днес, като най-новите версии са включени в DDK. Но версията на програмата, насочена към създаване на програми за DOS, не се разработва. Освен това Стивън Хътчесън създаде програмен пакет MASM, наречен "MASM32".

GNU и GNU/Linux

Операционната система GNU включва компилатора gcc, който включва газовия асемблер (GNU Assembler), който използва AT&T синтаксис, за разлика от повечето други популярни асемблери, които използват Intel синтаксис.

Преносими асемблери

Има и проект за асемблер с отворен код, чиито версии са налични за различни операционни системи и който ви позволява да получите обектни файлове за тези системи. Този асемблер се нарича NASM (Netwide Assembler).
YASM е пренаписана версия на NASM под BSD лиценз (с някои изключения).
FASM (Flat Assembler) е млад асемблер под BSD лиценз, модифициран да забранява прелицензирането (включително под GNU GPL). Има версии за KolibriOS, GNU/Linux, MS-DOS и Microsoft Windows, използва синтаксис на Intel и поддържа AMD64 инструкции.

RISC архитектури


MCS-51
AVR
В момента има 2 компилатора, произведени от Atmel (AVRStudio 3 и AVRStudio4). Втората версия е опит да се коригира не много успешната първа. Асемблерът също е включен в WinAVR.
ARM
AVR32
MSP430
PowerPC

Сглобяване и компилиране

Процесът на транслиране на програма на асемблер в обектен код обикновено се нарича асемблиране. За разлика от компилирането, асемблирането е повече или по-малко недвусмислен и обратим процес. В асемблерния език всяка мнемоника съответства на една машинна инструкция, докато в езиците за програмиране от високо ниво всеки израз може да скрие голям брой различни инструкции. По принцип това разделение е доста произволно, така че понякога преводът на асемблерните програми се нарича още компилация.

Асемблер език

Асемблер език- вид език за програмиране на ниско ниво, който е формат за запис на машинни команди, който е удобен за човешкото възприятие. Често за краткост се нарича просто асемблер, което не е вярно.

Командите на асемблерния език съответстват едно към едно на командите на процесора и всъщност представляват удобна символна форма на запис (мнемоничен код) на команди и техните аргументи. Асемблерният език също предоставя основни софтуерни абстракции: свързване на програмни части и данни чрез етикети със символни имена (по време на асемблирането се изчислява адрес за всеки етикет, след което всяко появяване на етикета се заменя с този адрес) и директиви.
Директивите за сглобяване ви позволяват да включвате блокове от данни (описани изрично или прочетени от файл) в програма; повторете определен фрагмент определен брой пъти; съставете фрагмента според условието; задайте адреса за изпълнение на фрагмента, различен от адреса на местоположението в паметта [посочете!]; промяна на стойностите на етикета по време на компилация; използвайте макро дефиниции с параметри и т.н.
Всеки модел процесор по принцип има свой собствен набор от инструкции и съответен асемблерен език (или диалект).

Предимства и недостатъци

Предимства на асемблерния език

Минималният излишен код, тоест използването на по-малко инструкции и достъпи до паметта, позволява повишена скорост и намален размер на програмата.
Осигуряване на пълна съвместимост и максимално използване на възможностите на желаната платформа: използване на специални инструкции и технически характеристики на тази платформа.
При програмиране на асемблер стават достъпни специални възможности: директен достъп до хардуер, входно/изходни портове и специални процесорни регистри, както и възможност за писане на самопроменящ се код (т.е. метапрограмиране, без необходимост от софтуерен интерпретатор) .
Най-новите технологии за сигурност, въведени в операционните системи, не позволяват създаването на самопроменящ се код, тъй като те изключват възможността за едновременно изпълнение на инструкции и запис в една и съща област на паметта (W^X технология в BSD системи, DEP в Windows).

Недостатъци на асемблерния език

Големи количества код и голям брой допълнителни малки задачи, което води до факта, че кодът става много труден за четене и разбиране и следователно отстраняването на грешки и модификацията на програмата става по-трудно, както и трудността при внедряването на програмни парадигми и всякакви други конвенции. което води до усложняване на съвместното развитие.
По-малък брой налични библиотеки, тяхната ниска съвместимост една с друга.
Не е преносим към други платформи (с изключение на бинарно съвместими).

Приложение

Пряко следва от предимствата и недостатъците.
Тъй като големите програми на асемблер са изключително неудобни за писане, те се пишат на езици от високо ниво. В асемблер се пишат малки фрагменти или модули, за които следното е критично:
производителност (драйвери);
размер на кода (зареждащи сектори, софтуер за микроконтролери и процесори с ограничен ресурс, вируси, софтуерна защита);
специални възможности: работа директно с хардуер или машинен код, тоест зареждащи устройства на операционна система, драйвери, вируси, системи за сигурност.

Свързване на асемблерния код с други езици

Тъй като само фрагменти от програма най-често са написани на асемблер, те трябва да бъдат свързани с други части на други езици. Това се постига по 2 основни начина:
На етап компилация— вмъкване на асемблерни фрагменти (вграден асемблер) в програма с помощта на специални езикови директиви, включително процедури за писане на асемблер. Методът е удобен за прости трансформации на данни, но с него не може да се създаде пълноценен код за сглобяване с данни и подпрограми, включително подпрограми с много входове и изходи, които не се поддържат от езици на високо ниво.
На етапа на оформлението, или отделна компилация. За да взаимодействат сглобените модули, достатъчно е свързващите функции да поддържат необходимите конвенции за извикване и типове данни. Индивидуалните модули могат да бъдат написани на всеки език, включително асемблер.

Синтаксис

Няма общоприет стандарт за синтаксиса на асемблерните езици. Има обаче стандарти, към които се придържат повечето разработчици на асемблерни езици. Основните такива стандарти са синтаксисът на Intel и синтаксисът на AT&T.

Инструкции

Общият формат за записване на инструкции е еднакъв и за двата стандарта:

[етикет:] код на операция [операнди] [;коментар]

където опкодът е директната мнемоника на инструкциите към процесора. Към него могат да се добавят префикси (повторения, промени в типа на адресиране и др.).
Операндите могат да бъдат константи, имена на регистри, адреси в RAM и т.н. Разликите между стандартите на Intel и AT&T се отнасят главно до реда, в който са изброени операндите и техния синтаксис за различните методи на адресиране.
Използваните мнемоники обикновено са еднакви за всички процесори от една и съща архитектура или семейство архитектури (сред широко известните са мнемоники за Motorola, ARM, x86 процесори и контролери). Те са описани в спецификациите на процесора. Възможни изключения:
Ако асемблерът използва междуплатформен AT&T синтаксис (оригиналните мнемоники се преобразуват в AT&T синтаксис)
Ако първоначално имаше два стандарта за запис на мнемоника (командната система беше наследена от процесор от друг производител).
Например, процесорът Zilog Z80 наследи системата за инструкции Intel i8080, разшири я и промени мнемониката (и обозначенията на регистрите) по свой начин. Например промених mov на Intel на ld. Процесорите Motorola Fireball наследиха системата за инструкции Z80, намалявайки я донякъде. В същото време Motorola официално се върна към мнемониката на Intel. И в момента половината от асемблерите за Fireball работят с мнемоника на Intel, а половината с мнемоника на Zilog.

Директиви

В допълнение към инструкциите, програмата може да съдържа директиви: команди, които не се превеждат директно в машинни инструкции, но контролират работата на компилатора. Техният набор и синтаксис варират значително и зависят не от хардуерната платформа, а от използвания компилатор (генериране на диалекти на езици в рамките на едно и също семейство от архитектури). Като „джентълменски набор“ от директиви можем да подчертаем:
дефиниране на данни (константи и променливи)
управление на организацията на програмата в паметта и параметрите на изходния файл
настройка на режима на работа на компилатора
всички видове абстракции (т.е. елементи от езици от високо ниво) - от проектирането на процедури и функции (за опростяване на изпълнението на парадигмата на процедурното програмиране) до условни конструкции и цикли (за парадигмата на структурираното програмиране)
макроси

Примерна програма

Пример за програма Hello world за MS-DOS за x86 архитектура в диалекта TASM:

.MODEL TINY CODE СЕГМЕНТ ПРИЕМА CS:CODE, DS:CODE ORG 100h START: mov ah,9 mov dx,OFFSET Msg int 21h int 20h Msg DB "Hello World",13,10,"$" CODE ENDS END START

Произход и критика на термина "език за асемблиране"

Този тип език получава името си от името на преводача (компилатора) от тези езици - асемблер (английски асемблер). Името на последното се дължи на факта, че на първите компютри не е имало езици от по-високо ниво и единствената алтернатива за създаване на програми с помощта на асемблер беше програмирането директно в кодове.
Асемблерният език на руски често се нарича "асемблер" (и нещо свързано с него - "асемблер"), което според английския превод на думата е неправилно, но се вписва в правилата на руския език. Но самият асемблер (програмата) също се нарича просто „асемблер“, а не „компилатор на език за асемблиране“ и т.н.
Използването на термина "език за асемблиране" може също да доведе до погрешното схващане, че има един-единствен език на ниско ниво или поне стандарт за такива езици. При назоваване на езика, на който е написана конкретна програма, е препоръчително да се изясни за каква архитектура е предназначена и на какъв диалект от езика е написана.

Днес в нашия кабинет с любопитки има един любопитен пример - операционна система, написана на чист асемблер. Заедно с драйвери, графична обвивка, десетки предварително инсталирани програми и игри, той заема по-малко от един и половина мегабайта. Запознайте се с изключително бързата и предимно руска ОС „Колибри“.

Разработването на "Колибри" протича доста бързо до 2009 г. Птицата се научи да лети на различен хардуер, изискващ минимално първия Pentium и осем мегабайта RAM. Минималните системни изисквания за Hummingbird са:

  • CPU: Pentium, AMD 5x86 или Cyrix 5x86 без MMX с честота 100 MHz;
  • RAM: 8 MB;
  • видеокарта: VESA-съвместима с поддръжка на VGA режим (640 × 480 × 16).

Съвременната "Колибри" е редовно актуализирана "нощна компилация" на последната официална версия, издадена в края на 2009 г. Тествахме компилация 0.7.7.0+ от 20 август 2017 г.

ВНИМАНИЕ

В настройките по подразбиране KolibriOS няма достъп до дискове, които се виждат през BIOS. Помислете внимателно и направете резервно копие, преди да промените тази настройка.

Промените в нощните компилации, макар и малки, са се натрупали доста през годините. Актуализираният "Колибри" може да пише на FAT16–32 / ext2 - ext4 дялове и поддържа други популярни файлови системи (NTFS, XFS, ISO-9660) в режим на четене. Той добави поддръжка за USB и мрежови карти и добави TCP/IP стек и аудио кодеци. Като цяло вече можете да правите нещо в него, а не просто да погледнете веднъж ултра лека операционна система с GUI и да се впечатлите от скоростта на стартиране.



Подобно на предишните версии, най-новата "Колибри" е написана на плосък асемблер (FASM) и заема една дискета - 1,44 MB. Благодарение на това той може да бъде поставен изцяло в някаква специализирана памет. Например, занаятчиите написаха KolibriOS директно във Flash BIOS. По време на работа може да се намира изцяло в кеша на някои процесори. Само си представете: цялата операционна система, заедно с програмите и драйверите, е кеширана!

ИНФО

При посещение на сайта kolibrios.org браузърът може да ви предупреди за опасността. Причината, очевидно, са асемблерните програми в дистрибуцията. VirusTotal вече определя сайта като напълно безопасен.

"Колибри" може лесно да се зареди от дискета, твърд диск, флашка, Live CD или във виртуална машина. За да емулирате, просто посочете типа на ОС „друго“, разпределете едно процесорно ядро ​​и малко RAM за него. Не е необходимо да свързвате устройството, а ако имате рутер с DHCP, „Hummingbird“ моментално ще се свърже с интернет и локална мрежа. Веднага след изтеглянето ще видите съответно известие.


Един от проблемите е, че HTTPS протоколът не се поддържа от браузъра, вграден в Kolibri. Поради това не беше възможно да разгледате сайта в него, както и да отворите страниците на Google, Yandex, Wikipedia, Sberbank... всъщност няма обичаен адрес. Всички отдавна са преминали към защитен протокол. Единственият сайт с чист HTTP от старата школа, на който попаднах, беше „Руският правителствен портал“, но и той не изглеждаше най-добре в текстов браузър.



Настройките за външен вид в Hummingbird се подобриха през годините, но все още са далеч от идеалните. Списък с поддържаните видео режими се показва на екрана за зареждане на Hummingbird, когато натиснете клавиша a.



Списъкът с налични опции е малък и необходимата разделителна способност може да не е там. Ако имате видеокарта с графичен процесор AMD (ATI), тогава можете веднага да добавите потребителски настройки. За да направите това, трябва да предадете параметъра -m на товарача ATIKMS х х , Например:

/RD/1/DRIVERS/ATIKMS -m1280x800x60 -1

Тук /RD/1/DRIVERS/ATIKMS е пътят до буутлоудъра (RD - RAM диск).

Когато системата работи, избраният видео режим може да се види с командата vmode и (теоретично) да се превключи ръчно. Ако „Hummingbird“ работи във виртуална машина, този прозорец ще остане празен, но с чисто зареждане могат да се добавят видео драйвери на Intel от i915 до Skylake включително.

Изненадващо, KolibriOS може да побере много игри. Сред тях има логически и аркадни игри, етикет, змия, танкове (не, не WoT) - цял „Център за игри“! Дори Doom и Quake бяха пренесени в Kolibri.



Друго важно нещо беше четецът FB2READ. Работи коректно с кирилица и има настройки за показване на текст.



Препоръчвам да съхранявате всички потребителски файлове на флашка, но тя трябва да бъде свързана през USB 2.0 порт. Нашето USB 3.0 флаш устройство (в USB 2.0 порт) с капацитет 16 GB с файлова система NTFS беше идентифицирано веднага. Ако трябва да пишете файлове, тогава трябва да свържете флаш устройство с FAT32 дял.



Комплектът за разпространение на Kolibri включва три файлови мениджъра, помощни програми за преглед на изображения и документи, аудио и видео плейъри и други потребителски приложения. Основният му фокус обаче е върху развитието на асемблерния език.



Вграденият текстов редактор има подчертаване на ASM синтаксиса и дори ви позволява незабавно да стартирате въведени програми.



Сред инструментите за разработка има компилаторът Oberon-07/11 за i386 Windows, Linux и KolibriOS, както и емулатори от ниско ниво: E80 - ZX Spectrum емулатор, FCE Ultra - един от най-добрите NES емулатори, DOSBox v.0.74 и други. Всички те бяха специално пренесени в Колибри.

Ако оставите KolibriOS за няколко минути, скрийнсейвърът ще започне. На екрана ще се появят редове с код, в които можете да видите препратка към MenuetOS.

Продължението е достъпно само за членове

Вариант 1. Присъединете се към общността на сайта, за да прочетете всички материали на сайта

Членството в общността в рамките на посочения период ще ви даде достъп до ВСИЧКИ хакерски материали, ще увеличи личната ви кумулативна отстъпка и ще ви позволи да натрупате професионален рейтинг на Xakep Score!


Връх