Училиште за асемблерски јазици: развој на оперативен систем. Летот на колибри. Она што оперативниот систем целосно напишан во асемблер може да го склопи и компајлира

Неодамна решив да научам асемблер, но не ме интересираше да трошам линии на код. Мислев дека додека учам асемблер ќе совладам некоја предметна област. Така, мојот избор падна на пишување подигнувач. Резултатот од моите наоди е овде на овој блог.

Сакам веднаш да кажам дека ја сакам теоријата во комбинација со практиката, па да почнеме.

Прво ќе ви покажам како да креирате едноставна MBRза да можеме што побрзо да уживаме во резултатот. Како што стануваме посложени со практични примери, ќе дадам теоретски информации.

Ајде прво да направиме подигнувач на USB флеш драјв!

Внимание!!! Нашата прва асемблер програма ќе работи и за флеш драјв и за други уреди како што се флопи диск или HDD. Последователно, за да можат сите примери да работат правилно, ќе дадам голем број појаснувања во врска со работата на кодот на различни уреди.

Ќе пишеме Фасм, бидејќи се смета за најдобар компајлер за пишување натоварувачи, што е MBR.Втората причина за изборот на Fasm е тоа што во голема мера го поедноставува процесот на компајлирање датотеки. Нема директиви командна линијаи така натаму. глупости кои можат целосно да го обесхрабрат учењето асемблерот и да ги постигнете вашите цели. Значи, во почетната фаза ни требаат две програми и некои непотребнимал флеш диск. Ископав 1Gb (брзо форматирано, и не е штета, ако ништо друго). По работата на нашиот подигнувач, флеш-уредот ќе престане да функционира нормално. мојот Windows 7 одбива да го форматира флеш-уредот. Ве советувам да го вратите флеш-уредот во живот со помошна алаткаАлатка за форматирање на USB диск за складирање на HP ( HPUSBFW.EXE) или други алатки за форматирање флеш драјвови.

Инсталирајте ги и фрлете ги соодветните кратенки на работната површина или каде и да сакате.

Подготовката е завршена, да преминеме на акција

Отворете Fasmw.exe и напишете го следново таму. Ќе го скицираме минималниот минимум код за да го видиме резултатот. Подоцна ќе анализираме што е накаљакано овде. Накратко ќе коментирам.

FASM код: ============== подигање.асм ================

org 7C00h ; адресите на нашата програма се пресметуваат според оваа директива

употреба16; се генерира хексадецимален код

cli, оневозможи прекини за промена на адресите во регистрите на сегменти

подвижна секира, 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 $ ;дршка на место (ја завртува програмата во овој момент)

линија db "Во ред, MBR е вчитан!"

пати 510 - ($ - $$) db 0 ;пополнување со нули јазот помеѓу претходниот бајт и последниот

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

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

Кога сте го приклучиле USB флеш-уредот на компјутерот, апсолутно не е очигледно за системот BIOS дека сакате да се подигне од USB флеш-уредот, затоа, во поставките на BIOS-от, треба да го изберете уредот од кој сакате да се подигне Затоа, избравме да се подигне од USB (ќе треба сами да сфатите како да го направите тоа, бидејќи интерфејсот на bios има различни варијации ... можете да гуглате Поставки на BIOS-отза вашиот матична плоча. Нема ништо комплицирано, по правило).

Сега кога BIOS-от знае дека сакате да се подигне од флеш-уредот, мора да се увери дека нултиот сектор на флеш-уредот е подигнуван. За да го направите ова, изгледа BIOS-от последните два бајти нулта сектори, ако тие се еднакви на 0x55 0xAA, тогаш само тогаш ќе се вчита во RAM меморија. Во спротивно, BIOS-от едноставно ќе помине покрај вашиот флеш-уред. Откако ги пронајде овие два магични бајти, го вчитува нултиот сектор во RAM меморијата на адресата 0000: 7C00h, а потоа заборава на флеш-уредот и ја пренесува контролата на оваа адреса. Сега целата моќност на компјутерот му припаѓа на вашиот подигнувач и тој, дејствувајќи веќе од RAM меморијата, може да вчита дополнителен код од USB флеш-уредот. Сега ќе видиме како изгледа токму овој сектор во програмата DMDE.

1. Вметнете го флеш-уредот во компјутерот и уверете се дека не ги содржи потребните информации.

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

Откако ќе го погледнете овој стрип, ќе имате вештина да го преземете вашиот MBR на флеш-уред. А вака изгледа долгоочекуваниот резултат на нашиот подигнувач:


Патем, ако зборуваме за минималниот код на натоварувачот, тогаш може да изгледа вака:

Орган 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:7C00h.
5. Подигнувачот го стартува оперативниот систем.


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

Дел 4: Со вештините што ги научивте од претходните написи во серијата, можете да започнете да развивате сопствен оперативен систем!

За што е?

  • Да се ​​разбере како работат компајлерите.
  • Да ги разбере инструкциите на централната единица за обработка.
  • За да го оптимизирате вашиот код во однос на перформансите.

За неколку месеци минавме низ тежок пат, кој започна со развојот едноставни програмиво асемблерски јазик за Linux и заврши во последната статија од серијата со развој на самостоен код што работи на персонален компјутер без оперативен систем. Па, сега ќе се обидеме заедно да ги собереме сите информации и да создадеме вистински оперативен систем. Да, ќе ги следиме стапките на Линус Торвалдс, но прво вреди да се одговори на следниве прашања: "Што е оперативен систем? Која од неговите функции ќе треба да ја рекреираме?".

Во оваа статија ќе се фокусираме само на главните функции на оперативниот систем: вчитување и извршување програми. Комплексните оперативни системи извршуваат многу повеќе функции, како што се управување со виртуелна меморија и обработка мрежни пакети, но нивната правилна имплементација бара долгогодишна континуирана работа, па затоа во оваа статија ќе ги разгледаме само главните функции кои се присутни во секој оперативен систем. Минатиот месец развивме мала програма која се вклопува во сектор од 512 бајти на флопи диск (неговиот прв сектор), а сега малку ќе ја измениме за да ја додадеме функцијата за вчитување дополнителни податоци од дискот.

Развој на подигнувач

Можеме да се обидеме да го задржиме нашиот оперативен систем бинарен колку што е можно помал за да го поставиме во првиот сектор од 512 бајти на флопи дискот, оној што е вчитан од BIOS-от, но во овој случај нема да можеме да го имплементираме какви било интересни функции. Затоа, ќе ги искористиме овие 512 бајти за да го поставиме бинарниот код на едноставен системски натоварувач кој ќе го вчита бинарниот код на кернелот на ОС во RAM и ќе го изврши. (После тоа, ќе го развиеме самиот кернел на ОС, кој ќе го вчита бинарниот код на другите програми од дискот и исто така ќе го изврши, но за ова ќе разговараме малку подоцна.)

Можете да го преземете изворниот код за примерите дискутирани во оваа статија на www.linuxvoice.com/code/lv015/asmschool.zip. И ова е кодот за нашиот подигнувач од датотека наречена boot.asm:

BITS 16 jmp краток почеток ; Скокни до етикетата опис на дискот за прескокнување nop ; Додавање пред опис на дискот %вклучи почеток на „bpb.asm“: mov ax, 07C0h ; Вчитај адреса mov ds, ax ; Податочен сегмент mov ax, 9000h ; Подгответе стек mov ss, ax mov sp, 0FFFFh ; Магацинот расте надолу! cld ; Постави насока знаменце mov si, kern_filename повик load_file jmp 2000h:0000h ; Скокни до бинарното кернелот на ОС вчитано од датотеката kern_filename db „MYKERNELBIN“ %вклучи „disk.asm“ пати 510-($-$$) db 0 ; Нулта полнење на бинарен код до 510 бајти dw 0AA55h; Бафер за крајна ознака на бинарен код на подигнувачот: ; Започнете го баферот за содржината на дискот

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

Сега ќе треба да користиме флопи диск со соодветен датотечен систем MS-DOS (FAT12), а за да работиме правилно со овој датотечен систем, треба да додадеме сет на специјални податоци во близина на почетокот на секторот. Овој сет се нарекува „Блок на параметри на BIOS-от“ (BPB) и содржи податоци како што се етикетата на дискот, број на сектори итн. Тоа не треба да не интересира во оваа фаза, бидејќи таквите теми може да се посветат на повеќе од една серија написи, поради што ги сместивме сите инструкции и податоци поврзани со него во посебна датотека со изворен код наречен bpb.asm.

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

%вклучи „bpb.asm“

Ова е директива NASM која дозволува содржината на наведената изворна датотека да се вклучи во тековната изворна датотека за време на склопувањето. Така, ќе можеме да го направиме кодот на нашиот системски натоварувач што е можно пократок и разбирлив со преместување на сите детали за имплементацијата на параметарскиот блок BIOS-от во посебна датотека. Блокот на параметри на BIOS-от мора да биде лоциран три бајти по почетокот на секторот, и бидејќи инструкцијата jmp зафаќа само два бајта, мора да ја користиме инструкцијата nop (неговото име значи „нема операција“ - ова е инструкција што не прави ништо но отпадни циклуси на процесорот ) за пополнување на преостанатиот бајт.

Работа со магацинот

Следно, ќе треба да користиме инструкции слични на оние што беа дискутирани во претходната статија за да ги подготвиме регистрите и стекот, како и инструкцијата за 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 подигање.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 loop: mov si, prompt call lib_print_string mov si, user_input call lib_input_string cmp byte , 0 je loop cmp word , "ls" je list_files mov ax, si mov3_load_cx8, jc load_fail повик 32768 jmp јамка load_fail: mov si, load_fail_msg повик lib_print_string jmp loop list_files: mov si, file_list повик lib_get_file_list повик lib_print_string jmp јамка промпт db "_ "13 "130M, 3, 10, „Не е пронајдено! ", 0 user_input пати 256 db 0 file_list пати 1024 db 0 %вклучи "lib.asm"

Пред да го погледнете кодот, обрнете внимание на последната линија со директивата за вклучување на датотеката со изворниот код lib.asm, која исто така се наоѓа во архивата asmschool.zip од нашата веб-страница. Ова е библиотека со корисни потпрограми за работа со екранот, тастатурата, линиите и дисковите кои исто така можете да ги користите - во овој случај ја вклучуваме оваа датотека со изворен код на самиот крај од главната датотека со изворниот код на јадрото на оперативниот систем по ред да го направиме второто што е можно покомпактно и убаво . Погледнете во „рутините на библиотеката lib.asm“ за дополнителни информацииза сите достапни рутини.

Во првите три линии од кодот на јадрото на оперативниот систем, ги пополнуваме регистрите на сегменти со податоци за да укажеме на сегментот 2000 во кој е вчитан бинарниот код. Ова е важно за гарантирано правилна работаинструкции како lodsb , кои треба да читаат податоци од тековниот сегмент, а не од кој било друг. После тоа, нема да вршиме никакви дополнителни операции на сегментите; нашиот оперативен систем ќе работи со 64 KB RAM!

Понатаму во кодот има ознака што одговара на почетокот на циклусот. Прво, користиме една од рутините од библиотеката lib.asm, имено lib_print_string , за да ја испечатиме честитката. Бајтите 13 и 10 пред линијата здраво се скокаат до нова линија, благодарение на што поздравот нема да се прикажува веднаш по излезот на која било програма, туку секогаш на нова линија.

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

Време на влез на корисник 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 (jump if carry) за да скокнеме до блокот код што ја печати пораката за грешка и се враќа на почетокот на циклусот за внесување на корисникот.

Во истиот случај, ако знамето за пренос не е поставено, можеме да заклучиме дека потпрограмата lib_load_asm успешно го вчита бинарниот код од програмската датотека во RAM меморијата на адреса 32768. Сè што ни треба во овој случај е да го иницираме извршувањето на бинарниот код вчитано на оваа адреса, односно започнете со извршување на програмата наведена од корисникот! И откако ќе се користи инструкцијата за ret во оваа програма (за да се вратиме на кодот за повикување), само ќе треба да се вратиме во циклусот за внесување на корисникот. Така, создадовме оперативен систем: тој се состои од наједноставните механизми за парсирање на команди и вчитување програми, имплементирани во околу 40 линии на асемблерски код, иако со голема помош од потпрограмите од библиотеката lib.asm.

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

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

После тоа, ќе мора некако да ја додадеме датотеката mykernel.bin во датотеката со слика на флопи диск. Ако сте запознаени со трикот за монтирање слики на дискот со уреди со повратна врска, можете да пристапите до содржината на сликата на дискот floppy.img користејќи ја, но постои полесен начин, а тоа е користење на GNU Mtools (www.gnu.org /софтвер /mtools). Ова е збир на програми за работа со флопи дискови кои користат датотечни системи MS-DOS/FAT12, достапни од складиштата на софтверски пакети на сите популарни Линукс дистрибуции, така што сè што треба да направите е да користите apt-get, yum, pacman или која било алатка што ја користите за да инсталирате софтверски пакети на вашата дистрибуција.

Откако ќе го инсталирате соодветниот софтверски пакет, за да ја додадете датотеката mykernel.bin во датотеката со слика на дискот, ќе треба да ја извршите следнава команда:

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 флопи.img

Voila: подигнувачот 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, а јас имам се од нула, вклучително и кернелот и апликациите. Значи, продолжување под сечењето.

Група ОС: Еве.
Прво, ќе ви фрлам една слика од екранот.

Ги нема повеќе, а сега подетално зошто го пишувам.

Беше топла априлска вечер, четврток. Уште од детството сонував да напишам оперативен систем, кога одеднаш помислив: „Сега ги знам добрите и добрите страни, зошто да не го остварам мојот сон?“. Ги гуглав сајтовите на оваа тема и најдов статија од Хабр: „Како да започнете и да не престанете да пишувате ОС“. Му благодариме на неговиот автор за врската OSDev Вики подолу. Отидов таму и почнав да работам. Имаше во една статија сите податоци за минималниот ОС. Почнав да градам cross-gcc и binutils, а потоа препишав сè од таму. Требаше да ја видите мојата радост кога го видов натписот "Здраво, кернел свет!" Скокнав веднаш од столот и сфатив - нема да се откажам. Напишав „конзола“ (во наводници, немав пристап до тастатура), но потоа решив да напишам прозорски систем. Како резултат на тоа, работеше, но немав пристап до тастатурата. И тогаш решив да смислам име засновано на системот на прозорци X. Googled Y Window System - тоа е. Како резултат на тоа, тој го нарече Z Window System 0.1, кој е вклучен во OS365 pre-alpha 0.1. И да, никој не го виде освен мене. Потоа сфатив како да имплементирам поддршка за тастатура. Слика од екранот на првата верзија, кога немаше ништо, дури ни прозорски систем:

Не го помести курсорот на текстот, како што можете да видите. Потоа напишав неколку едноставни апликацииврз основа на Z. И тука е изданието 1.0.0 алфа. Имаше многу работи, дури и системското мени. А менаџер на датотекиа калкулаторот едноставно не работеше.

Директно ме тероризираше еден пријател кој се грижи само за убавината (Митрофан, извини). Тој рече „Измиен режим на VBE 1024 * 768 * 32, измиен, измиен! Па, тие се опијаниле! Па, веќе бев уморен да го слушам, а сепак го измив. Повеќе за имплементацијата подолу.

Направив сè со мојот подигнувач, имено GRUB "ohm. Со него, можете да го поставите графичкиот режим без компликации со додавање на неколку магични линии во заглавието 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
И тогаш ја земам адресата на framebuffer и резолуцијата на екранот од структурата на информации Multiboot и пишувам пиксели таму. VESA направи сè многу збунето - RGB боите мора да се внесат во обратен редослед (не R G B, туку B G R). Не разбрав неколку дена - зошто не се прикажуваат пикселите!? На крајот, сфатив дека заборавив да ги сменам вредностите на 16 константи на бои од 0...15 на нивните RGB еквиваленти. Како резултат на тоа, го ослободив, во исто време ја измив градиентната позадина. Потоа направив конзола, 2 апликации и пуштив 1.2. О, да, речиси заборавив - можете да го преземете оперативниот систем на

асемблер

асемблер(од англискиот assemble - to assemble) - компајлер од асемблерски јазик до команди на машински јазик.
За секоја архитектура на процесорот и за секое семејство ОС или ОС, постои Асемблер. Постојат и таканаречени „вкрстени асемблери“, кои им овозможуваат на машините со една архитектура (или во околината на еден ОС) да составуваат програми за друга целна архитектура или друг ОС и да добијат извршна шифра во формат погоден за извршување на целната архитектура или во целната средина.ОС.

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

Асемблери за ДОС

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

Microsoft Windows

Со доаѓањето на оперативниот систем Мајкрософт Виндоус, се појави екстензија TASM наречена TASM32, што овозможи да се создадат програми за извршување во околината на Windows. Најновата позната верзија на Tasm е 5.3, која поддржува MMX инструкции и моментално е вклучена во Turbo C++ Explorer. Но, официјално развојот на програмата е целосно запрен.
Мајкрософт одржува производ наречен Microsoft Macro Assembler. Продолжува да се развива до ден-денес, со најновите верзии вклучени во DDK. Но, верзијата на програмата насочена кон создавање програми за ДОС не се развива. Дополнително, Стивен Хачесон создаде програмски пакет MASM наречен „MASM32“.

GNU и GNU/Linux

Оперативниот систем GNU го вклучува компајлерот gcc, кој го вклучува асемблерот на гас (GNU Assembler) со помош на синтаксата AT&T, за разлика од повеќето други популарни асемблери кои користат синтакса на Интел.

Преносни склопувачи

Исто така, постои отворен проект за асемблери, чии верзии се достапни за различни оперативни системи и кој ви овозможува да добиете објектни датотеки за овие системи. Овој асемблер се нарекува NASM (Netwide Assembler).
YASM е препишана верзија на NASM лиценцирана од нула (со некои исклучоци).
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.
АРМ
AVR32
MSP430
PowerPC

Склопување и компилација

Процесот на преведување на програма за асемблерски јазик во објектен код се нарекува склопување. За разлика од компилацијата, склопувањето е повеќе или помалку недвосмислен и реверзибилен процес. Во асемблерскиот јазик, секој мнемоник одговара на една машинска инструкција, додека кај програмските јазици на високо ниво зад секој израз може да се кријат голем број различни инструкции. Во принцип, оваа поделба е прилично произволна, па понекогаш преводот на асемблер програми се нарекува и компилација.

асемблерски јазик

асемблерски јазик- вид на програмски јазик на ниско ниво, кој е формат за снимање инструкции на машината што е погоден за човечка перцепција. Често, за краткост, едноставно се нарекува асемблерот, што не е точно.

Командите на асемблерски јазик одговараат еден до еден на командите на процесорот и, всушност, претставуваат пригодна симболична форма на нотација (мнемонички код) на командите и нивните аргументи. Јазикот на асемблер обезбедува и основни софтверски апстракции: поврзување на делови од програмата и податоци преку етикети со симболични имиња (за време на склопувањето се пресметува адреса за секоја ознака, по што секоја појава на етикетата се заменува со оваа адреса) и директиви.
Директивите за склопување ви дозволуваат да вклучите блокови на податоци (опишани експлицитно или прочитани од датотека) во програмата; повторете одреден фрагмент одреден број пати; состави го фрагментот според условот; поставете ја адресата за извршување на фрагментот да биде различна од адресата на локацијата на меморијата[наведете!]; промена на вредностите на етикетата за време на компилацијата; користете макро дефиниции со параметри итн.
Секој модел на процесор, во принцип, има свој сет на инструкции и соодветниот асемблерски јазик (или дијалект).

Предности и недостатоци

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

Минималниот износ на вишок код, односно користењето на помалку инструкции и пристапи до меморијата, ви овозможува да ја зголемите брзината и да ја намалите големината на програмата.
Обезбедување целосна компатибилност и максимална употреба на можностите на саканата платформа: употреба на специјални упатства и технички карактеристики на оваа платформа.
Кога програмирате во асемблер, стануваат достапни специјални функции: директен пристап до хардвер, I/O порти и специјални регистри на процесорот, како и можност за пишување код кој самостојно се менува (т.е. метапрограмирање и без потреба од софтверски преведувач) .
Најновите безбедносни технологии имплементирани во оперативните системи не дозволуваат правење код кој само-модифицира, бидејќи ја исклучуваат истовремено можноста за извршување инструкции и пишување во иста мемориска област (технологија W^X во BSD системите, DEP во Windows).

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

Големи количини на код и голем број дополнителни мали задачи, што доведува до фактот дека кодот станува многу тежок за читање и разбирање, и затоа станува потешко да се дебагира и рафинира програмата, како и тешкотијата за спроведување на програмирањето парадигми и сите други конвенции. што доведува до сложеност на развојот на соработката.
Помалку достапни библиотеки, нивната ниска компатибилност една со друга.
Не е пренослив на други платформи (освен бинарни компатибилни).

Апликација

Директно следи од предностите и недостатоците.
Бидејќи е крајно незгодно да се пишуваат големи програми на асемблерски јазик, тие се напишани на јазици на високо ниво. Во асемблер, тие пишуваат мали фрагменти или модули за кои се критични:
перформанси (возачи);
големина на кодот (сектори за подигање, софтвер за микроконтролери и процесори со ограничени ресурси, вируси, софтверска заштита);
посебни карактеристики: работа директно со хардверски или машински код, односно натоварувачи на оперативен систем, драјвери, вируси, системи за заштита.

Поврзување на асемблерот со други јазици

Бидејќи само фрагменти од програмата најчесто се пишуваат во асемблер, тие мора да се поврзат со останатите делови на други јазици. Ова се постигнува на 2 главни начини:
Во времето на компајлирање- вметнување фрагменти од асемблер во програмата (анг. инлајн асемблер) со посебни јазични директиви, вклучително и процедури за пишување на асемблер јазик. Методот е погоден за едноставни трансформации на податоци, но целосниот асемблер код, со податоци и потпрограми, вклучувајќи потпрограми со многу влезови и излези кои не се поддржани од јазици на високо ниво, не може да се направи со негово користење.
Во фаза на изградба, или посебна компилација. За да комуницираат модулите за поврзување, доволно е функциите за поврзување да ги поддржуваат потребните конвенции за повикување и типови на податоци. Посебните модули можат да бидат напишани на кој било јазик, вклучувајќи го и асемблерот.

Синтакса

Не постои општо прифатен стандард за синтаксата на асемблерските јазици. Сепак, постојат стандарди до кои се придржуваат повеќето развивачи на асемблерски јазици. Главните такви стандарди се синтаксата на Интел и синтаксата AT&T.

Инструкции

Општиот формат за инструкции за пишување е ист за двата стандарди:

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

каде што оптичкиот код е директно мнемоник на инструкцијата до процесорот. Може да се додадат префикси (повторувања, промени во типот на адреси итн.).
Операндите можат да бидат константи, имиња на регистри, адреси на RAM, итн. Разликите помеѓу стандардите на Intel и AT&T се однесуваат главно на редоследот по кој се наведени операндите и нивната синтакса за различни методи на адресирање.
Користените мнемоници обично се исти за сите процесори од иста архитектура или семејство на архитектури (меѓу широко познатите мнемоници се Motorola, ARM, x86 процесори и контролери). Тие се опишани во спецификацијата на процесорот. Можни исклучоци:
Ако асемблерот користи AT&T синтакса на повеќе платформи (оригиналните мнемоници се претвораат во AT&T синтакса)
Ако првично постоеле два стандарди за пишување мнемоника (системот за инструкции бил наследен од процесорот на друг производител).
На пример, процесорот Zilog Z80 го наследи комплетот инструкции Intel i8080, го прошири и ги смени мнемониците (и ознаките на регистрите) на свој начин. На пример, го сменив движењето на Интел во ld. Процесорите на Motorola Fireball го наследија комплетот со инструкции Z80, намалувајќи го малку. Сепак, Motorola официјално се врати на Intel mnemonics. И во моментов, половина од асемблерите на Fireball работат со мнемоника на Intel, а половина со мнемоника на Zilog.

директиви

Покрај инструкциите, програмата може да содржи директиви: команди кои не се преведени директно во машински инструкции, туку ја контролираат работата на компајлерот. Нивниот сет и синтаксата значително се разликуваат и не зависат од хардверската платформа, туку од користениот компајлер (што доведува до дијалекти на јазици во исто семејство архитектури). Како „џентлменски сет“ на директиви, можеме да разликуваме:
дефинирање на податоци (константи и променливи)
управување со организацијата на програмата во меморијата и параметрите на излезната датотека
поставување на режимот на компајлерот
сите видови апстракции (т.е. елементи на јазиците на високо ниво) - од дизајнирање процедури и функции (за поедноставување на имплементацијата на парадигмата за процедурално програмирање) до условни структури и јамки (за парадигмата за структурирано програмирање)
макроа

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

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

.МОДЕЛ МАЛКИ СЕГМЕНТ НА ​​КОД ПРЕТЗЕЛИ CS:CODE, DS:CODE ORG 100h СТАРТ: mov ah,9 mov dx,OFFSET Порака int 21h int 20h Порака DB "Здраво свето",13,10,"$" КОД КРАЈ КРАЈ

Потекло и критика на терминот „асемблерски јазик“

Овој тип на јазик го доби своето име од името на преведувачот (компајлер) од овие јазици - асемблер (англиски асемблер - асемблер). Името на второто се должи на фактот дека на првите компјутери немаше јазици од повисоко ниво, а единствената алтернатива за создавање програми со помош на асемблер беше програмирањето директно во кодови.
Јазикот на склопување на руски често се нарекува „асемблер“ (и нешто поврзано со него - „асемблер“), што, според англискиот превод на зборот, е неточно, но се вклопува во правилата на рускиот јазик. Меѓутоа, самиот асемблер (програма) се нарекува и едноставно „асемблер“, а не „компајлер на јазикот на асемблер“ итн.
Употребата на терминот „асемблерски јазик“ исто така може да доведе до заблуда дека постои единствен јазик на ниско ниво, или барем стандард за такви јазици. При именување на јазикот на кој е напишана одредена програма, пожелно е да се определи за која архитектура е наменета и на кој дијалект на јазикот е напишана.

Денес, во нашата Kunstkamera, љубопитен пример е оперативен систем напишан во чист асемблер. Заедно со драјвери, графичка школка, десетици претходно инсталирани програми и игри, потребни се помалку од еден и пол мегабајти. Запознајте го исклучително брзиот и претежно руски оперативен систем Колибри.

Развојот на колибри се одвиваше прилично брзо до 2009 година. Птицата научи да лета на различен хардвер, барајќи минимум од првиот Pentium и осум мегабајти RAM меморија. Минималните системски барања за колибри се:

  • Процесор: 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 ја дефинира страницата како целосно безбедна.

„Колибри“ лесно се вчитува од флопи диск, хард диск, флеш драјв, ЦД во живо или во виртуелна машина. За емулација, доволно е да го наведете типот на ОС „друго“, да одвоите едно процесорско јадро и малку RAM меморија на него. Не е неопходно да се поврзе диск, а ако има рутер со DHCP, Hummingbird веднаш ќе се поврзе на Интернет и локална мрежа. Веднаш по преземањето, ќе видите известување.


Еден проблем - протоколот HTTPS не е поддржан од вградениот прелистувач Hummingbird. Затоа, не беше можно да се погледне страницата во неа, како и да се отворат страниците на Google, Yandex, Wikipedia, Sberbank ... всушност, немаше вообичаена адреса. Сите се префрлија на безбеден протокол одамна. Единствената локација со чист HTTP од старата школа на која наидов беше „порталот на Владата на Русија“, но не изгледаше најдобро во текстуален прелистувач.



Поставките за изгледот во Hummingbird се подобрија со текот на годините, но сè уште се далеку од идеални. Списокот на поддржани режими на видео се прикажува на екранот за подигање Hummingbird кога ќе го притиснете копчето a буквата.



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

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

Овде /RD/1/DRIVERS/ATIKMS е патеката до подигнувачот (RD - RAM Disk).

Кога системот работи, избраниот режим на видео може да се гледа со командата vmode и (теоретски) да се префрли рачно. Ако Hummingbird работи во виртуелна машина, тогаш овој прозорец ќе остане празен, но со чист подигање, видео драјверите на Intel може да се додадат од i915 до Skylake инклузивно.

Изненадувачки, еден куп игри се вклопуваат во KolibriOS. Меѓу нив има логични и аркадни игри, ознаки, змија, тенкови (не, не WoT) - цел „Центар за игри“! Дури и Doom и Quake беа пренесени на Hummingbird.



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



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



Дистрибуцијата Hummingbird вклучува три менаџери на датотеки, алатки за прегледување слики и документи, аудио и видео плеери и други кориснички апликации. Сепак, фокусот е на развојот на асемблерски јазик.



Вградениот уредувач на текст има истакнување на синтаксата ASM, па дури и ви овозможува веднаш да ги извршите напишаните програми.



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

Ако го оставите KolibriOS неколку минути, заштитникот на екранот ќе започне. На екранот ќе работат линии на код, во кои можете да видите референца за MenuetOS.

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

Опција 1. Придружете се на заедницата „сајт“ за да ги прочитате сите материјали на страницата

Членството во заедницата во наведениот период ќе ви овозможи пристап до СИТЕ хакерски материјали, ќе го зголеми вашиот личен кумулативен попуст и ќе ви овозможи да акумулирате професионална оценка за Xakep Score!


Врв