სემანტიკური ბირთვი - როგორ შევადგინოთ ის სწორად? არასაჭირო "გასაღებების" ამოღება

ბირთვის შემუშავება სამართლიანად განიხილება არც თუ ისე იოლი საქმე, მაგრამ ყველას შეუძლია დაწეროს მარტივი ბირთვი. იმისათვის, რომ განიცადოთ ბირთვის ჰაკერების მაგია, თქვენ უბრალოდ უნდა დაიცვათ გარკვეული კონვენციები და დაეუფლოთ ასამბლერის ენას. ამ სტატიაში ჩვენ გაჩვენებთ, თუ როგორ უნდა გავაკეთოთ ეს.

ეს არის არჯუნ სრიჰარანის ორი სტატიის თარგმანი:

გამარჯობა მსოფლიო! მოდით დავწეროთ ბირთვი, რომელიც ჩაიტვირთება GRUB-ის მეშვეობით x86-თან თავსებად სისტემებზე. ჩვენი პირველი ბირთვი აჩვენებს შეტყობინებას ეკრანზე და იქ გაჩერდება.

როგორ ჩაიტვირთება x86 მანქანები

სანამ ვიფიქრებთ იმაზე, თუ როგორ დავწეროთ ბირთვი, მოდით შევხედოთ როგორ ჩაიტვირთება კომპიუტერი და გადასცემს კონტროლს ბირთვს. x86 პროცესორის რეგისტრების უმეტესობას აქვს კონკრეტული მნიშვნელობები ჩატვირთვის შემდეგ. ინსტრუქციის მაჩვენებლის რეესტრი (EIP) შეიცავს ინსტრუქციის მისამართს, რომელიც შესრულდება პროცესორის მიერ. მისი მყარი კოდირებული მნიშვნელობა არის 0xFFFFFFFF0. ანუ, x86 პროცესორი ყოველთვის დაიწყებს შესრულებას ფიზიკური მისამართიდან 0xFFFFFFF0. ეს არის 32-ბიტიანი მისამართების სივრცის ბოლო 16 ბაიტი. ამ მისამართს ეწოდება გადატვირთვის ვექტორი.

ჩიპსეტში მოთავსებულ მეხსიერების ბარათში ნათქვამია, რომ მისამართი 0xFFFFFFF0 ეხება BIOS-ის კონკრეტულ ნაწილს და არა RAM-ს. თუმცა, BIOS აკოპირებს თავის თავს RAM-ში მეტის მისაღებად სწრაფი წვდომა- ამ პროცესს ეწოდება "დაჩრდილვა", შექმნა ჩრდილოვანი ასლი. ასე რომ, მისამართი 0xFFFFFFF0 შეიცავს მხოლოდ ინსტრუქციას მეხსიერების იმ მდებარეობაზე გადასვლის შესახებ, სადაც BIOS-მა თავად დააკოპირა.

ასე რომ, BIOS იწყებს შესრულებას. პირველ რიგში, ის ეძებს მოწყობილობებს, საიდანაც მას შეუძლია ჩატვირთვა პარამეტრებში მითითებული თანმიმდევრობით. ის ამოწმებს მედიას „ჯადოსნურ ნომერზე“, რომელიც განასხვავებს ჩამტვირთველ დისკებს ჩვეულებრივიდან: თუ პირველ სექტორში ბაიტი 511 და 512 არის 0xAA55, მაშინ დისკი ჩამტვირთავია.

როგორც კი BIOS აღმოაჩენს ჩატვირთვის მოწყობილობა, ის დააკოპირებს პირველი სექტორის შიგთავსს RAM-ში, დაწყებული მისამართიდან 0x7C00, შემდეგ გადაიტანს შესრულებას ამ მისამართზე და დაიწყებს მის მიერ ახლახან ჩატვირთული კოდის შესრულებას. ამ კოდს Bootloader ეწოდება.

ჩამტვირთველი ატვირთავს ბირთვს ფიზიკურ მისამართზე 0x100000. ეს არის ის, რასაც ყველაზე პოპულარული x86 ბირთვი იყენებს.

x86-თან თავსებადი ყველა პროცესორი იწყება პრიმიტიული 16-ბიტიანი რეჟიმით, რომელსაც ეწოდება "რეალური რეჟიმი". GRUB ჩამტვირთველი ცვლის პროცესორს 32-ბიტიან დაცულ რეჟიმზე CR0 რეგისტრის ქვედა ბიტის ერთზე დაყენებით. ამრიგად, ბირთვი იწყებს ჩატვირთვას 32-ბიტიან დაცულ რეჟიმში.

გაითვალისწინეთ, რომ GRUB, Linux-ის ბირთვების შემთხვევაში, ირჩევს შესაბამის ჩატვირთვის პროტოკოლს და იტვირთავს ბირთვს რეალური რეჟიმი. Linux-ის ბირთვები ავტომატურად გადადიან დაცულ რეჟიმში.

რა გვჭირდება

  • x86 თავსებადი კომპიუტერი (ცხადია)
  • Linux
  • NASM ასამბლეერი,
  • ld (GNU Linker),
  • GRUB.

თქვენ შეგიძლიათ იპოვოთ წყაროს კოდი იმისა, რაც უნდა მივიღოთ აქ.

ასამბლეის ენის შესასვლელი წერტილი

ჩვენ, რა თქმა უნდა, გვსურს დავწეროთ ყველაფერი C-ზე, მაგრამ ბოლომდე ვერ ავიცილებთ თავიდან ასამბლერის გამოყენებას. ჩვენ დავწერთ პატარა ფაილს x86 ასამბლერში, რომელიც გახდება ჩვენი ბირთვის საწყისი წერტილი. ასამბლეის კოდი მხოლოდ გამოძახებს გარე ფუნქციას, რომელსაც ჩავწერთ C-ში და შემდეგ შევაჩერებთ პროგრამის შესრულებას.

როგორ გავხადოთ ასამბლეის კოდი ჩვენი ბირთვის ამოსავალ წერტილად? ჩვენ ვიყენებთ ლინკერის სკრიპტს, რომელიც აკავშირებს ობიექტის ფაილებს და ქმნის საბოლოო შესრულებადი ფაილიბირთვები (ქვემოთ უფრო დეტალურად აგიხსნით). ამ სკრიპტში ჩვენ პირდაპირ მივუთითებთ, რომ გვინდა ჩვენი ორობითი გადმოტვირთვა მისამართზე 0x100000. ეს არის მისამართი, როგორც უკვე დავწერე, რომელზედაც ჩამტვირთველი ელის ბირთვში შესვლის წერტილს.

აქ არის ასამბლერის კოდი.

ბირთვი.ასმ

ბიტი 32

განყოფილება. ტექსტი

გლობალური დასაწყისი

გარე კმ

დაწყება:

mov esp, stack_space

დარეკეთ kmain

განყოფილება. bss

რეზბ 8192

stack_space:

პირველი ბიტი 32 ინსტრუქცია არ არის x86 ასამბლერი, არამედ NASM დირექტივა, რომელიც ეუბნება მას, რომ შექმნას კოდი პროცესორის 32-ბიტიან რეჟიმში მუშაობისთვის. ეს არ არის აუცილებელი ჩვენი მაგალითისთვის, მაგრამ კარგი პრაქტიკაა ამის ცალსახად მითითება.

მეორე სტრიქონი იწყებს ტექსტის განყოფილებას, რომელიც ასევე ცნობილია როგორც კოდის განყოფილება. მთელი ჩვენი კოდი აქ წავა.

გლობალური არის NASM-ის კიდევ ერთი დირექტივა, რომელიც აცხადებს ჩვენს კოდში არსებულ სიმბოლოებს გლობალურად. ეს საშუალებას მისცემს ლინკერს მოძებნოს დაწყების სიმბოლო, რომელიც ჩვენი შესვლის წერტილია.

kmain არის ფუნქცია, რომელიც განისაზღვრება ჩვენს kernel.c ფაილში. extern აცხადებს, რომ ფუნქცია გამოცხადებულია სხვაგან.

შემდეგ მოდის start ფუნქცია, რომელიც უწოდებს kmain-ს და აჩერებს პროცესორს hlt ინსტრუქციით. შეფერხებებმა შეიძლება გააღვიძოს პროცესორი hlt-ის შემდეგ, ამიტომ პირველ რიგში გამორთეთ შეფერხებები cli (გაასუფთავეთ შეფერხებები) ინსტრუქციით.

იდეალურ შემთხვევაში, ჩვენ უნდა გამოვყოთ მეხსიერების გარკვეული რაოდენობა სტეკისთვის და მივუთითოთ სტეკის მაჩვენებელი (esp). როგორც ჩანს, GRUB ამას აკეთებს ჩვენთვის და ამ ეტაპზე სტეკის მაჩვენებელი უკვე დაყენებულია. თუმცა, ყოველი შემთხვევისთვის, მოდით გამოვყოთ გარკვეული მეხსიერება BSS განყოფილებაში და მივუთითოთ სტეკის მაჩვენებელი მის დასაწყისზე. ჩვენ ვიყენებთ resb ინსტრუქციას - ის ინახავს მეხსიერებას მითითებულ ბაიტებში. ამის შემდეგ რჩება ნიშანი, რომელიც მიუთითებს მეხსიერების რეზერვირებული ნაწილის ზღვარზე. kmain-ის გამოძახებამდე, სტეკის მაჩვენებელი (esp) მიმართულია ამ ზონაში mov ინსტრუქციით.

ბირთვი C

kernel.asm ფაილში ჩვენ დავურეკეთ kmain() ფუნქციას. ასე რომ, C კოდში, შესრულება დაიწყება იქიდან.

ბირთვი.გ

void kmain (ბათილი)

const char * str = "ჩემი პირველი ბირთვი" ;

char * vidptr = (char * ) 0xb8000 ;

ხელმოუწერელი int i = 0 ;

ხელმოუწერელი int j = 0 ;

ხოლო (ჯ< 80 * 25 * 2 ) {

vidptr[j] = " ";

vidptr[j+1] = 0x07;

j = j + 2 ;

j = 0;

while (str [ j ] != "\0" ) (

vidptr[i] = str[j];

vidptr[i+1] = 0x07;

i = i + 2;

დაბრუნება ;

ჩვენი ბირთვი ყველაფერს გააკეთებს არის ეკრანის გასუფთავება და ჩემი პირველი ბირთვის ხაზის ამობეჭდვა.

პირველი, ჩვენ ვქმნით vidptr მაჩვენებელს, რომელიც მიუთითებს მისამართზე 0xb8000. დაცულ რეჟიმში, ეს არის ვიდეო მეხსიერების დასაწყისი. ტექსტური ეკრანის მეხსიერება უბრალოდ მისამართების სივრცის ნაწილია. მეხსიერების განყოფილება გამოყოფილია ეკრანის I/O-სთვის, რომელიც იწყება მისამართიდან 0xb8000 მასში მოთავსებულია 80 ASCII სიმბოლოსგან შემდგარი 25 ხაზი.

ტექსტის მეხსიერებაში თითოეული სიმბოლო წარმოდგენილია 16 ბიტით (2 ბაიტი), ვიდრე 8 ბიტით (1 ბაიტი), რომელსაც ჩვენ შეჩვეული ვართ. პირველი ბაიტი არის სიმბოლოს ASCII კოდი, ხოლო მეორე ბაიტი არის ატრიბუტი-ბაიტი. ეს არის სიმბოლოების ფორმატის განმარტება, მისი ფერის ჩათვლით.

სიმბოლოს მწვანე შავზე გამოსატანად, ჩვენ უნდა ჩავდოთ s ვიდეო მეხსიერების პირველ ბაიტში და მნიშვნელობა 0x02 მეორე ბაიტში. 0 აქ ნიშნავს შავ ფონს და 2 ნიშნავს მწვანე ფერს. გამოვიყენებთ ღია ნაცრისფერ ფერს, მისი კოდია 0x07.

პირველ ციკლში ხოლო პროგრამაავსებს 80 სიმბოლოსგან შემდგარი 25 სტრიქონს თითოეული ცარიელი სიმბოლოებით 0x07 ატრიბუტით. ეს გაასუფთავებს ეკრანს.

მეორეში ხოლო მარყუჟი null-შეწყვეტილი სტრიქონის სიმბოლოები, ჩემი პირველი ბირთვი, იწერება ვიდეო მეხსიერებაში და თითოეული სიმბოლო იღებს ატრიბუტ-ბაიტს, რომელიც ტოლია 0x07. ეს უნდა გამოვიდეს სტრიქონი.

განლაგება

ახლა ჩვენ უნდა შევადგინოთ kernel.asm ობიექტურ ფაილში NASM-ის გამოყენებით და შემდეგ გამოვიყენოთ GCC kernel.c სხვა ობიექტის ფაილში შესადგენისთვის. ჩვენი ამოცანაა დავაკავშიროთ ეს ობიექტები ჩატვირთვისთვის შესაფერის შესრულებად ბირთვში. ამისთვის დაგვჭირდება ლინკერის (ld) სკრიპტის დაწერა, რომელსაც არგუმენტად გადავცემთ.

ბმული.ld

OUTPUT_FORMAT (elf32 - i386)

ENTRY (დაწყება)

სექციები

0x100000;

ტექსტი: (*(.ტექსტი))

მონაცემები: (*(.data))

Bss: (*(. bss))

აქ ჩვენ პირველად დავაყენეთ ჩვენი შესრულებადი ფაილის ფორმატი (OUTPUT_FORMAT) 32-ბიტიან ELF-ზე (შესრულებადი და დამაკავშირებელი ფორმატი), სტანდარტული ორობითი ფორმატი Unix-ზე დაფუძნებული სისტემებისთვის x86 არქიტექტურისთვის.

ENTRY იღებს ერთ არგუმენტს. იგი განსაზღვრავს სიმბოლოს სახელს, რომელიც იქნება შესრულებადი ფაილის შესვლის წერტილი.

SECTIONS ჩვენთვის ყველაზე მნიშვნელოვანი ნაწილია. აქ ჩვენ განვსაზღვრავთ ჩვენი შესრულებადი ფაილის განლაგებას. ჩვენ შეგვიძლია განვსაზღვროთ, თუ როგორ გაერთიანდება სხვადასხვა სექციები და სად განთავსდება თითოეული მათგანი.

ხვეული ბრეკეტებში, რომლებიც მიჰყვება SECTIONS გამოსახულებას, წერტილი მიუთითებს მდებარეობის მრიცხველზე. ის ავტომატურად ინიციალიზდება 0x0-მდე SECTIONS ბლოკის დასაწყისში, მაგრამ შეიძლება შეიცვალოს ახალი მნიშვნელობის მინიჭებით.

ადრე დავწერე, რომ ბირთვის კოდი უნდა დაიწყოს მისამართიდან 0x100000. ამიტომ პოზიციის მრიცხველს ვანიჭებთ მნიშვნელობას 0x100000.

შეხედეთ ხაზს.ტექსტი: ( *(.ტექსტი) ) . ვარსკვლავი აქ განსაზღვრავს ნიღაბს, რომელიც შეიძლება შეესაბამებოდეს ფაილის ნებისმიერ სახელს. შესაბამისად, გამოთქმა *(.text) ნიშნავს ყველა შეყვანილ .ტექსტის განყოფილებას ყველა შეყვანის ფაილში.

შედეგად, ლინკერი აერთიანებს ყველა ობიექტის ფაილის ტექსტურ განყოფილებას შესრულებადი ფაილის ტექსტურ განყოფილებაში და განათავსებს მას პოზიციის მრიცხველში მითითებულ მისამართზე. ჩვენი შესრულებადი კოდის განყოფილება დაიწყება მისამართიდან 0x100000.

მას შემდეგ, რაც ლინკერი აწარმოებს ტექსტის განყოფილებას, პოზიციის მრიცხველი იქნება 0x1000000 პლუს ტექსტის განყოფილების ზომა. ანალოგიურად, მონაცემთა და bss სექციები გაერთიანდება და განთავსდება პოზიციის მრიცხველის მიერ მითითებულ მისამართზე.

GRUB და multiboot

ახლა ყველა ჩვენი ფაილი მზად არის ბირთვის ასაშენებლად. მაგრამ რადგან ჩვენ ჩავტვირთავთ ბირთვს GRUB-ის გამოყენებით, დარჩა კიდევ ერთი ნაბიჯი.

არსებობს სტანდარტი სხვადასხვა x86 ბირთვების ჩატვირთვისთვის ჩატვირთვის გამოყენებით. ამას ჰქვია "multiboot სპეციფიკაცია". GRUB ჩატვირთავს მხოლოდ ბირთვებს, რომლებიც შეესაბამება მას.

ამ სპეციფიკაციის მიხედვით, ბირთვი შეიძლება შეიცავდეს სათაურს (Multiboot header) პირველ 8 კილობაიტში. ეს სათაური უნდა შეიცავდეს სამ ველს:

  • მაგია- შეიცავს "ჯადოსნურ" ნომერს 0x1BADB002, რომლითაც ხდება სათაურის იდენტიფიცირება;
  • დროშები- ეს ველი ჩვენთვის არ არის მნიშვნელოვანი, შეგიძლიათ ნული დატოვოთ;
  • საკონტროლო ჯამი- საკონტროლო ჯამი, უნდა მისცეს ნულს, თუ დაემატება მაგიური და დროშების ველებს.

ჩვენი kernel.asm ფაილი ახლა ასე გამოიყურება.

ბირთვი.ასმ

ბიტი 32

განყოფილება. ტექსტი

; მულტი ჩატვირთვის სპეციფიკა

გასწორება 4

dd 0x1BADB002; მაგია

dd 0x00 ; დროშები

dd - (0x1BADB002 + 0x00) ; საკონტროლო ჯამი

გლობალური დასაწყისი

გარე კმ

დაწყება:

mov esp, stack_space

დარეკეთ kmain

განყოფილება. bss

რეზბ 8192

stack_space:

dd ინსტრუქცია განსაზღვრავს 4 ბაიტიან ორმაგ სიტყვას.

ბირთვის აწყობა

ასე რომ, ყველაფერი მზად არის, რომ შევქმნათ ობიექტის ფაილი kernel.asm-დან და kernel.c-დან და დავაკავშიროთ ისინი ჩვენი სკრიპტის გამოყენებით. ჩვენ ვწერთ კონსოლში:

-c პარამეტრი მიუთითებს, რომ ფაილის დაკავშირება არ არის საჭირო კომპილაციის შემდეგ. ჩვენ თვითონ გავაკეთებთ ამას:

$ ld - m elf_i386 - T ბმული. ld - o kernel kasm . o kc. ო

ეს ბრძანება გაუშვებს ლინკერს ჩვენი სკრიპტით და წარმოქმნის შესრულებადს სახელად kernel.

ბირთვის გატეხვა საუკეთესოდ ხდება ვირტუალურ გარემოში. ბირთვის გასაშვებად QEMU-ში GRUB-ის ნაცვლად, გამოიყენეთ ბრძანება qemu-system-i386 - ბირთვის ბირთვი.

GRUB-ის დაყენება და ბირთვის გაშვება

GRUB მოითხოვს, რომ ბირთვის ფაილის სახელი მიჰყვეს ბირთვს-<версия>. მოდით გადავარქვათ სახელი ფაილს - დავარქმევ ჩემს ბირთვს-701.

ახლა ჩვენ ვათავსებთ ბირთვს /boot დირექტორიაში. ამას დასჭირდება სუპერმომხმარებლის პრივილეგიები.

თქვენ უნდა დაამატოთ მსგავსი რამ GRUB კონფიგურაციის ფაილში grub.cfg:

ბირთვის დაწერა კლავიატურისა და ეკრანის მხარდაჭერით

ჩვენ დავასრულეთ მუშაობა მინიმალურ ბირთვზე, რომელიც ჩაიტვირთება GRUB-ის მეშვეობით, მუშაობს დაცულ რეჟიმში და ეკრანზე ბეჭდავს ერთ ხაზს. დროა გააფართოვოთ და დაამატოთ კლავიატურის დრაივერი, რომელიც წაიკითხავს სიმბოლოებს კლავიატურიდან და აჩვენებს მათ ეკრანზე.

სრული წყაროს კოდი შეგიძლიათ იპოვოთ ავტორის საცავში GitHub-ზე.

ჩვენ დავუკავშირდებით I/O მოწყობილობებს I/O პორტების საშუალებით. არსებითად, ისინი მხოლოდ მისამართებია I/O ავტობუსში. არსებობს სპეციალური პროცესორის ინსტრუქციები კითხვისა და ჩაწერის ოპერაციებისთვის.

პორტებთან მუშაობა: კითხვა და გამომავალი

read_port:

mov edx, [esp + 4]

in al, dx

write_port:

mov edx, [esp + 4]

mov al, [esp + 4 + 4]

გარეთ dx, al

I/O პორტებზე წვდომა ხდება x86 კომპლექტში შეტანილი და გასვლის ინსტრუქციების გამოყენებით.

read_port-ში პორტის ნომერი გადაეცემა არგუმენტად. როდესაც შემდგენელი იძახებს ფუნქციას, ის უბიძგებს ყველა არგუმენტს სტეკზე. არგუმენტი კოპირებულია edx რეესტრში სტეკის მაჩვენებლის გამოყენებით. dx რეგისტრი არის edx რეგისტრის ქვედა 16 ბიტი. ინსტრუქცია აქ კითხულობს პორტის ნომერს, რომელიც მოცემულია dx-ში და აყენებს შედეგს al-ში. al რეგისტრი არის eax რეგისტრის ქვედა 8 ბიტი. შესაძლოა კოლეჯიდან გახსოვთ, რომ ფუნქციების მიერ დაბრუნებული მნიშვნელობები გადაეცემა eax რეესტრში. ასე რომ read_port საშუალებას გვაძლევს წავიკითხოთ I/O პორტებიდან.

write_port ფუნქცია მუშაობს ანალოგიურად. ჩვენ ვიღებთ ორ არგუმენტს: პორტის ნომერი და მონაცემები, რომლებიც დაიწერება. გამოსვლის ინსტრუქცია წერს მონაცემებს პორტში.

წყვეტს

ახლა, სანამ დრაივერის დაწერას დავუბრუნდებით, უნდა გავიგოთ, როგორ იცის პროცესორმა, რომ ერთ-ერთმა მოწყობილობამ შეასრულა ოპერაცია.

უმარტივესი გამოსავალია მოწყობილობების გამოკითხვა - მუდმივად შეამოწმეთ მათი სტატუსი წრეში. ეს აშკარა მიზეზების გამო არაეფექტური და არაპრაქტიკულია. ასე რომ, ეს არის ის, სადაც შეფერხებები მოქმედებს. შეფერხება არის სიგნალი, რომელიც გადაეგზავნება პროცესორს მოწყობილობის ან პროგრამის მიერ, რომელიც მიუთითებს, რომ მოხდა მოვლენა. შეფერხებების გამოყენებით, ჩვენ შეგვიძლია თავიდან ავიცილოთ მოწყობილობების გამოკითხვის აუცილებლობა და ვუპასუხოთ მხოლოდ ჩვენთვის საინტერესო მოვლენებს.

ჩიპი სახელწოდებით პროგრამირებადი შეფერხების კონტროლერი (PIC) პასუხისმგებელია x86 არქიტექტურის შეფერხებებზე. ის ამუშავებს ტექნიკის შეფერხებებს და მარშრუტებს და აქცევს მათ შესაბამის სისტემის შეფერხებებად.

როდესაც მომხმარებელი რაღაცას აკეთებს მოწყობილობასთან, პულსი, რომელსაც ეწოდება შეფერხების მოთხოვნა (IRQ) ეგზავნება PIC ჩიპს. PIC თარგმნის მიღებულ შეწყვეტას სისტემის შეფერხებადა უგზავნის შეტყობინებას პროცესორს, რომ დროა შეწყვიტოს ის, რასაც აკეთებს. შეფერხების შემდგომი მართვა ბირთვის ამოცანაა.

PIC-ის გარეშე, სისტემაში არსებული ყველა მოწყობილობის გამოკითხვა მოგვიწევდა, რათა გვენახა, მოხდა თუ არა მოვლენა რომელიმე მათგანთან.

მოდით შევხედოთ როგორ მუშაობს ეს კლავიატურაზე. კლავიატურა კიდია 0x60 და 0x64 პორტებზე. პორტი 0x60 აგზავნის მონაცემებს (ღილაკზე დაჭერისას), ხოლო პორტი 0x64 აგზავნის სტატუსს. თუმცა, ჩვენ უნდა ვიცოდეთ ზუსტად როდის წავიკითხოთ ეს პორტები.

შეფერხებები აქ სასარგებლოა. ღილაკზე დაჭერისას კლავიატურა აგზავნის PIC სიგნალს IRQ1 შეფერხების ხაზის მეშვეობით. PIC ინახავს ოფსეტური მნიშვნელობას, რომელიც შენახულია მისი ინიციალიზაციის დროს. ის ამატებს შეყვანის ხაზის ნომერს ამ შიგთავსში შეფერხების ვექტორის შესაქმნელად. შემდეგ პროცესორი ეძებს მონაცემთა სტრუქტურას, რომელსაც ეწოდება შეფერხების აღმწერი ცხრილი (IDT), რათა შეფერხების დამმუშავებელს მისცეს მისი ნომრის შესაბამისი მისამართი.

ამ მისამართის კოდი შესრულებულია და ამუშავებს შეფერხებას.

დააყენეთ IDT

IDT_entry (

ხელმოუწერელი მოკლე int offset_lowerbits ;

ხელმოუწერელი მოკლე int სელექტორი;

ხელმოუწერელი სიმბოლო ნული;

ხელმოუწერელი char type_attr ;

ხელმოუწერელი მოკლე int offset_higherbits;

struct IDT_entry IDT [IDT_SIZE];

void idt_init(void)

ხელმოუწერელი გრძელი კლავიატურა_მისამართი;

ხელმოუწერელი გრძელი idt_address ;

ხელმოუწერელი გრძელი idt_ptr [2];

კლავიატურა_მისამართი = (ხელმოუწერელი გრძელი) კლავიატურა_მმართველი;

IDT[0x21]. offset_lowerbits = კლავიატურის_მისამართი & 0xffff;

IDT[0x21]. სელექტორი = 0x08 ; /* KERNEL_CODE_SEGMENT_OFFSET */

IDT[0x21]. ნული = 0;

IDT[0x21]. type_attr = 0x8e ; /* INTERRUPT_GATE */

IDT[0x21]. offset_higherbits = (კლავიატურის_მისამართი & 0xffff0000 ) >> 16 ;

ჩაწერის_პორტი (0x20, 0x11);

ჩაწერის_პორტი (0xA0, 0x11);

ჩაწერის_პორტი (0x21, 0x20);

ჩაწერის_პორტი (0xA1, 0x28);

ჩაწერის_პორტი (0x21, 0x00);

ჩაწერის_პორტი (0xA1, 0x00);

ჩაწერის_პორტი (0x21, 0x01);

ჩაწერის_პორტი (0xA1, 0x01);

ჩაწერის_პორტი (0x21, 0xff);

ჩაწერის_პორტი (0xA1, 0xff);

idt_address = (ხელმოუწერელი გრძელი) IDT;

idt_ptr[0] = (ზომა (სტრუქტურ IDT_შესვლა) * IDT_SIZE) + ((idt_address& 0xffff) << 16 ) ;

idt_ptr[ 1 ] = idt_address>> 16 ;

load_idt(idt_ptr) ;

}

IDT არის IDT_entry სტრუქტურების მასივი. ჩვენ მოგვიანებით განვიხილავთ კლავიატურის შეფერხების დაკავშირებას დამმუშავებელთან, მაგრამ ახლა ვნახოთ, როგორ მუშაობს PIC.

თანამედროვე x86 სისტემებს აქვთ ორი PIC ჩიპი, თითოეული რვა შეყვანის ხაზით. ჩვენ მათ დავარქმევთ PIC1 და PIC2. PIC1 იღებს IRQ0-დან IRQ7-მდე და PIC2 იღებს IRQ8-დან IRQ15-მდე. PIC1 იყენებს პორტს 0x20 ბრძანებებისთვის და 0x21 მონაცემებისთვის, ხოლო PIC2 იყენებს პორტს 0xA0 ბრძანებებისთვის და 0xA1 მონაცემებისთვის.

ორივე PIC ინიციალიზებულია რვა ბიტიანი სიტყვით, რომელსაც ეწოდება ინიციალიზაციის ბრძანების სიტყვები (ICW).

დაცულ რეჟიმში, ორივე PIC-მა ჯერ უნდა გასცეს ინიციალიზაციის ბრძანება ICW1 (0x11). ის ეუბნება PIC-ს, რომ დაელოდოს კიდევ სამი ინიციალიზაციის სიტყვის ჩამოსვლას მონაცემთა პორტში.

ეს ბრძანებები გაივლის PIC-ს:

  • შეწევის ვექტორი (ICW2),
  • რა არის Master/Slave ურთიერთობები PIC-ებს შორის (ICW3),
  • დამატებითი ინფორმაცია გარემოს შესახებ (ICW4).

ინიციალიზაციის მეორე ბრძანება (ICW2) ასევე იგზავნება თითოეული PIC-ის შეყვანაში. ის ანიჭებს ოფსეტს, რაც არის მნიშვნელობა, რომელსაც ჩვენ ვამატებთ ხაზის ნომერს შეწყვეტის ნომრის მისაღებად.

საოპერაციო ოთახის ყველაზე ძირითადი კომპონენტი Linux სისტემებიარის ბირთვი. ეს არის ბირთვი, რომელიც მოქმედებს, როგორც შუალედური კავშირი მომხმარებლის პროგრამებიდა კომპიუტერული ტექნიკა. ყველა ბინარულ დისტრიბუციაში ჩვენ არ გვჭირდება ფიქრი ბირთვის აწყობაზე და კონფიგურაციაზე, დისტრიბუციის დეველოპერებმა უკვე გააკეთეს ყველაფერი ჩვენთვის. მაგრამ თუ ჩვენ გვსურს თავად ავაშენოთ ჩვენი დისტრიბუცია ან დავაყენოთ ბირთვის უახლესი ვერსია, ჩვენ უნდა ავაშენოთ ბირთვი ხელით.

პირველი ვარიანტი აქტუალური იყო მათთვის, ვისაც სურდა მათი აღჭურვილობისგან მაქსიმალური შესრულების მიღება, მაგრამ ახლა, კომპიუტერის სიმძლავრის სწრაფი ზრდის გათვალისწინებით, ბირთვის აწყობისას შესრულების ზრდა სრულიად შეუმჩნეველია. დღესდღეობით, ბირთვის შექმნა შეიძლება საჭირო გახდეს არაორობითი დისტრიბუციის მომხმარებლებისთვის, როგორიცაა Gentoo, მათთვის, ვისაც სურს ბირთვში გარკვეული ცვლილებების შეტანა, ბირთვის უახლესი ვერსიის მიღება და, რა თქმა უნდა, მათთვის, ვისაც სურს სრულად გაიგოს. მათი სისტემის მუშაობა.

ამ გაკვეთილში ჩვენ განვიხილავთ, თუ როგორ უნდა ავაშენოთ Linux ბირთვი. პირველი ნაწილი გეტყვით, თუ როგორ უნდა დააკონფიგურიროთ ბირთვი ავტომატური რეჟიმი. ასე ვთქვათ, მათთვის, ვისაც არ სურს გაიგოს, როგორ მუშაობს, ვისაც მხოლოდ მზა პროდუქტის მიღება სჭირდება, როგორც გამოსავალი - აწყობილი ბირთვი. მეორე ნაწილში განვიხილავთ ძირითად ეტაპებს მექანიკური პარამეტრებიბირთვები, ეს პროცესი რთული და ნელია, მაგრამ მე შევეცდები მოგცეთ საფუძვლები, რათა თქვენ თავად გაარკვიოთ.

პირველი, რაც უნდა გააკეთოთ, არის ბირთვის წყაროების ჩამოტვირთვა. უმჯობესია აიღოთ წყაროები თქვენი განაწილების ვებსაიტიდან, თუ ისინი იქ არის, ან ბირთვის ოფიციალური ვებსაიტიდან: kernel.org. ჩვენ გადავხედავთ წყაროების ჩამოტვირთვას kernel.org-დან.

წყაროების ჩამოტვირთვამდე უნდა გადავწყვიტოთ ბირთვის ვერსია, რომელსაც ავაშენებთ. გამოშვებების ორი ძირითადი ვერსია არსებობს - სტაბილური (სტაბილური) და გამოშვების კანდიდატები (rc), რა თქმა უნდა, არის ასევე სტაბილური მხარდაჭერის ხანგრძლივი პერიოდით (გრძელვადიანი), მაგრამ ახლა მნიშვნელოვანია პირველი ორის გაგება. სტაბილურები, როგორც წესი, არ არის უახლესი, მაგრამ უკვე კარგად გამოცდილი ბირთვები მინიმალური რაოდენობაშეცდომები ტესტები, პირიქით, უახლესი, მაგრამ შეიძლება შეიცავდეს სხვადასხვა შეცდომებს.

ასე რომ, როდესაც ჩვენ გადავწყვეტთ ვერსიას, გადადით kernel.org-ზე და ჩამოტვირთეთ საჭირო წყაროები tar.xz ფორმატში:

ეს სტატია გამოიყენებს უახლეს, ამჟამად არასტაბილურ ვერსიას, 4.4.rc7.

თქვენ ასევე შეგიძლიათ მიიღოთ Linux ბირთვის წყაროები git უტილიტის გამოყენებით. პირველი, მოდით შევქმნათ საქაღალდე წყაროებისთვის:

mkdir kernel_sources

უახლესი ვერსიის ჩამოსატვირთად ჩაწერეთ:

git კლონი https://github.com/torvalds/linux

ბირთვის წყაროების გახსნა

ახლა ჩვენ შენახული გვაქვს წყაროები. გადადით წყაროს საქაღალდეში:

cd linux_sources

ან თუ ჩამოტვირთეთ Linux-ის ბირთვი ბრაუზერის გამოყენებით, მაშინ ჯერ შექმენით ეს საქაღალდე და დააკოპირეთ არქივი მასში:

mkdir linux_sources

cp ~/ჩამოტვირთვები/linux* ~/linux_sources

გახსენით არქივი tar უტილიტის გამოყენებით:

და გადადით საქაღალდეში შეუფუთავი ბირთვით, მე მაქვს ეს:

cd linux-4.4-rc7/

Linux kernel-ის ავტომატური კონფიგურაცია

სანამ Linux-ის ბირთვის შექმნას დავიწყებთ, მოგვიწევს მისი კონფიგურაცია. როგორც ვთქვი, პირველ რიგში გადავხედავთ ბირთვის build-ის დაყენების ავტომატურ ვარიანტს. თქვენს სისტემას უკვე აქვს აწყობილი, კონფიგურირებული სადისტრიბუციო მწარმოებლის მიერ და სრულად მოქმედი ბირთვი. თუ არ გსურთ გაუმკლავდეთ ბირთვის კონფიგურაციის სირთულეებს, შეგიძლიათ უბრალოდ ამოიღოთ მზად პარამეტრებიძველი ბირთვი და მათზე დაყრდნობით ახლის პარამეტრების გენერირება. ჩვენ მხოლოდ ახალი პარამეტრების მნიშვნელობების დაზუსტება მოგვიწევს. იმის გათვალისწინებით, რომ ქ უახლესი ვერსიებიარ არის მნიშვნელოვანი ცვლილებები და არ არის დაგეგმილი, თქვენ შეგიძლიათ უპასუხოთ ყველა ამ პარამეტრს, როგორც ამას კონფიგურაციის სკრიპტი გვთავაზობს.

გამოყენებული ბირთვის პარამეტრები ინახება არქივში /proc/config.gz. მოდით გავხსნათ კონფიგურაცია და მოვათავსოთ ჩვენს საქაღალდეში zcat უტილიტის გამოყენებით:

პროცესის განმავლობაში, თქვენ უნდა უპასუხოთ რამდენიმე კითხვას. ეს არის ახალი პარამეტრები, რომლებიც შეიცვალა ან დაემატა ახალ ბირთვს და მხარდაჭერა ახალი ტექნიკისთვის, უმეტეს შემთხვევაში შეგიძლიათ აირჩიოთ ნაგულისხმევი ვარიანტი. ჩვეულებრივ, არსებობს სამი ვარიანტი: y - ჩართვა, n - არ მოიცავს, m - ჩართეთ როგორც მოდული. რეკომენდებული ვერსია დაწერილია დიდი ასოები, მის ასარჩევად უბრალოდ დააჭირეთ Enter.

ყველაფრის გაკეთებას დაახლოებით 10 წუთი დასჭირდება. პროცესის დასრულების შემდეგ, ბირთვი მზად არის ასაშენებლად. შემდეგ ჩვენ განვიხილავთ ბირთვის ხელით კონფიგურაციას, მაგრამ შეგიძლიათ პირდაპირ გამოტოვოთ Linux ბირთვის შექმნა.

Linux Kernel Tuning-ის სახელმძღვანელო

ხელით კონფიგურაცია რთული და შრომატევადი პროცესია, მაგრამ ის საშუალებას გაძლევთ გაიგოთ, როგორ მუშაობს თქვენი სისტემა, რა ფუნქციებია გამოყენებული და შექმნათ ბირთვი ფუნქციების მინიმალური საჭირო კომპლექტით, რომელიც შეესაბამება თქვენს საჭიროებებს. ჩვენ განვიხილავთ მხოლოდ ძირითად ნაბიჯებს, რომლებიც უნდა დასრულდეს ბირთვის აწყობისა და მუშაობისთვის. დანარჩენი ყველაფერი თავად უნდა გაარკვიოთ ბირთვის დოკუმენტაციის საფუძველზე. საბედნიეროდ, კონფიგურაციის პროგრამას აქვს ვრცელი დოკუმენტაცია თითოეული პარამეტრისთვის, რომელიც დაგეხმარებათ გაიგოთ სხვა პარამეტრების ჩართვა.

დავიწყოთ. Linux ბირთვის პარამეტრების მენიუს გასაშვებად, აკრიფეთ:

ეს გახსნის პროგრამას ncurses ინტერფეისით:

როგორც ხედავთ, რამდენიმე საჭირო ვარიანტი უკვე შედის, რათა დაყენების პროცესი გაგიადვილოთ. დავიწყოთ ყველაზე ძირითადი პარამეტრებით. პარამეტრის გასააქტიურებლად დააჭირეთ y-ს, რომ ჩართოთ მოდულით - m, გადასაადგილებლად გამოიყენეთ ისრები და Enter, შეგიძლიათ დაბრუნდეთ ზედა დონეზე გასვლის ღილაკის გამოყენებით გახსენით ელემენტი. ზოგადი დაყენება.

აქ ჩვენ ვაყენებთ შემდეგ პარამეტრებს:

ლოკალური ვერსია - ადგილობრივი ვერსიაბირთვები გაიზრდება ერთით ყოველი აშენებისას, რათა ახალი ბირთვები არ ჩაანაცვლოს ძველს ინსტალაციის დროს, დააყენეთ მნიშვნელობა 1-ზე.

ვერსიის ინფორმაციის ავტომატურად დამატება ვერსიის სტრიქონზე- დაამატეთ ვერსია ბირთვის ფაილის სახელს.

ბირთვის შეკუმშვის რეჟიმი- ბირთვის გამოსახულების შეკუმშვის რეჟიმი, ყველაზე ეფექტური lzma.

ნაგულისხმევი ჰოსტის სახელი- კომპიუტერის სახელი ნაჩვენებია შეყვანის მოთხოვნაზე

POSIX შეტყობინების რიგები- POSTIX რიგების მხარდაჭერა

ანონიმური მეხსიერების პეიჯინგის მხარდაჭერა -ჩართეთ swap მხარდაჭერა

საკონტროლო ჯგუფის მხარდაჭერა- პროცესების ჯგუფებს შორის რესურსების განაწილების მექანიზმის მხარდაჭერა

Kernel.config მხარდაჭერადა ჩართეთ წვდომა .config-ზე /proc/config.gz-ის საშუალებით- ჩართეთ ბირთვის კონფიგურაციის ამოღების შესაძლებლობა /proc/config.gz-ის საშუალებით

ეს არის ის, დაბრუნდით დონეზე და ჩართეთ იგი ჩატვირთვადი მოდულის მხარდაჭერის ჩართვა,ეს ფუნქცია საშუალებას გაძლევთ ჩატვირთოთ გარე მოდულები, შემდეგ გახსენით მისი მენიუ და ჩართოთ:

მოდულების გამორთვის მხარდაჭერა

მოდულების იძულებითი გამორთვა

ისევ უკან ვბრუნდებით და ვხსნით პროცესორის ტიპი და მახასიათებლები:

პროცესორის ოჯახი (Opteron/Athlon64/Hammer/K8)- აირჩიეთ თქვენი პროცესორის ტიპი.

მოდით ისევ დავბრუნდეთ და გადავიდეთ განყოფილებაში ფაილური სისტემები, შეამოწმეთ ყველა საჭირო ველი აქ.

აუცილებლად ჩართეთ გაფართოებული 3 (ext3) ფაილური სისტემადა გაფართოებული 4 (ext4) ფაილური სისტემა- სტანდარტული ext3 და ext4 ფაილური სისტემების მხარდაჭერა

ვბრუნდებით და მივდივართ ბირთვის გატეხვა.

აქ ჩვენ ვაერთიანებთ ჯადოსნური SysRq გასაღები- SysRq ჯადოსნური ფუნქციების მხარდაჭერა, არა აუცილებელი რამ, მაგრამ ზოგჯერ სასარგებლო.

დარჩენილია კიდევ ერთი წერტილი, ყველაზე რთული, რადგან თავად მოგიწევთ მისი გავლა. მოწყობილობის დრაივერები- თქვენ უნდა გაიაროთ სექციები და ჩართოთ დრაივერები თქვენი აღჭურვილობისთვის. აღჭურვილობაში ვგულისხმობ არასტანდარტულ მყარ დისკებს, მაუსებს, USB მოწყობილობებს, ვებ კამერებს, Bluetooth, WIFI გადამყვანებს, პრინტერებს და ა.შ.

თქვენ შეგიძლიათ ნახოთ, თუ რა აღჭურვილობაა დაკავშირებული თქვენს სისტემასთან ბრძანებით:

როგორც კი დაასრულებთ ყველა საფეხურს, ბირთვი მზად არის ასაშენებლად, მაგრამ სავარაუდოდ ბევრი გექნებათ გასარკვევი.

გასასვლელად დააჭირეთ ღილაკს რამდენჯერმე გასვლა.

Linux-ის ბირთვის აგება

ყველა მომზადების დასრულების შემდეგ შესაძლებელია Linux-ის ბირთვის აშენება. მშენებლობის პროცესის დასაწყებად, გაუშვით:

მოდულების დამზადება

ახლა შეგიძლიათ წახვიდეთ ყავის დასალევად ან გაისეირნოთ, რადგან აწყობის პროცესი გრძელია და დაახლოებით ნახევარი საათი დასჭირდება.

ახალი ბირთვის დაყენება

როდესაც ბირთვი და მოდულები იკრიბება, ახალი ბირთვი შეიძლება დამონტაჟდეს. თქვენ შეგიძლიათ ხელით დააკოპიროთ ბირთვის ფაილი bootloader საქაღალდეში:

cp arch/x86_64/boot/bzImage /boot/vmlinuz

ან შეგიძლიათ უბრალოდ შეასრულოთ ინსტალაციის სკრიპტი, დაუყოვნებლივ დააინსტალიროთ მოდულები ერთდროულად:

sudo make install && sudo make modules_install

ინსტალაციის შემდეგ არ დაგავიწყდეთ Grub bootloader-ის კონფიგურაციის განახლება:

grub-mkconfig -o /boot/grub/grub.cfg

და გადატვირთეთ კომპიუტერი, რომ ნახოთ ახალი ბირთვი მოქმედებაში:

დასკვნები

ესე იგი. ამ სტატიაში ჩვენ დეტალურად განვიხილეთ, თუ როგორ უნდა ავაშენოთ Linux ბირთვი წყაროდან. ეს სასარგებლო იქნება ყველასთვის, ვისაც სურს უკეთ გაიგოს მათი სისტემა და მათთვის, ვისაც სურს მიიღოს ყველაზე მეტი ახალი ვერსიაბირთვები თქვენს სისტემაში. თუ თქვენ გაქვთ რაიმე შეკითხვები, დასვით კომენტარებში!

მოდით დავწეროთ მარტივი ბირთვი, რომელიც შეიძლება ჩაიტვირთოს GRUB ჩამტვირთველის გამოყენებით x86 სისტემაზე. ეს ბირთვი აჩვენებს შეტყობინებას ეკრანზე და დაელოდება.

როგორ ჩაიტვირთება x86 სისტემა?

სანამ ბირთვის წერას დავიწყებთ, მოდით გავიგოთ, როგორ ჩაიტვირთება სისტემა და გადასცემს კონტროლს ბირთვს.

პროცესორის რეგისტრების უმეტესობა უკვე შეიცავს გარკვეულ მნიშვნელობებს გაშვებისას. რეგისტრი, რომელიც მიუთითებს ინსტრუქციების მისამართზე (Instruction Pointer, EIP) ინახავს მეხსიერების მისამართს, სადაც დევს პროცესორის მიერ შესრულებული ინსტრუქცია. ნაგულისხმევი EIP არის 0xFFFFFFFF0. ამრიგად, x86 პროცესორებია ტექნიკის დონედაიწყეთ მუშაობა 0xFFFFFFFF0 მისამართიდან. ეს რეალურად არის 32-ბიტიანი მისამართების სივრცის ბოლო 16 ბაიტი. ამ მისამართს ეწოდება გადატვირთვის ვექტორი.

ახლა ჩიპსეტის მეხსიერების რუკა უზრუნველყოფს, რომ 0xFFFFFFF0 ეკუთვნის BIOS-ის კონკრეტულ ნაწილს და არა RAM-ს. ამაში BIOS დროაკოპირებს თავის RAM-ში უფრო სწრაფი წვდომისთვის. მისამართი 0xFFFFFFF0 შეიცავს მხოლოდ ინსტრუქციას გადახტომის მისამართზე მეხსიერებაში, სადაც ინახება BIOS-ის ასლი.

ასე იწყებს BIOS კოდის შესრულებას. BIOS ჯერ ეძებს მოწყობილობას, რომლიდანაც შესაძლებელია ჩატვირთვა წინასწარ დაყენებული თანმიმდევრობით. ეძებს ჯადოსნური ნომერი, რომელიც განსაზღვრავს არის თუ არა მოწყობილობა ჩატვირთვადი (პირველი სექტორის 511-ე და 512-ე ბაიტი ტოლი უნდა იყოს 0xAA55).

როდესაც BIOS აღმოაჩენს ჩატვირთვის მოწყობილობას, ის აკოპირებს მოწყობილობის პირველი სექტორის შიგთავსს RAM-ში, დაწყებული ფიზიკური მისამართიდან. 0x7c00; შემდეგ მიდის მისამართზე და ახორციელებს გადმოწერილ კოდს. ეს კოდი ე.წ ჩამტვირთავი.

ჩამტვირთველი იტვირთება ბირთვს ფიზიკურ მისამართზე 0x100000. ეს მისამართი გამოიყენება როგორც საწყისი მისამართი x86 სისტემების ყველა დიდ ბირთვში.

ყველა x86 პროცესორი იწყება მარტივი 16-ბიტიანი რეჟიმით რეალური რეჟიმი. GRUB ჩამტვირთველი ცვლის რეჟიმს 32-ბიტიანზე დაცული რეჟიმი CR0 რეგისტრის დაბალი ბიტის დაყენება 1 . ამრიგად, ბირთვი იტვირთება 32-ბიტიან დაცულ რეჟიმში.

გაითვალისწინეთ, რომ Linux-ის ბირთვის შემთხვევაში, GRUB ხედავს Linux-ის ჩატვირთვის პროტოკოლებს და ჩატვირთავს ბირთვს რეალურ რეჟიმში. ბირთვი ავტომატურად გადადის დაცულ რეჟიმში.

რა გვჭირდება?

  • x86 კომპიუტერი;
  • Linux;
  • ld (GNU Linker);

შესვლის წერტილის დაყენება ასამბლერში

რაც არ უნდა გინდოდეს მხოლოდ C-ით შემოიფარგლო, რაღაცის დაწერა მოგიწევს ასამბლერში. ჩვენ დავწერთ მასზე პატარა ფაილს, რომელიც იქნება ჩვენი ბირთვის საწყისი წერტილი. ის მხოლოდ C-ში დაწერილი გარე ფუნქციის გამოძახებას და პროგრამის ნაკადის შეჩერებას გააკეთებს.

როგორ შეგვიძლია დავრწმუნდეთ, რომ ეს კოდი არის საწყისი წერტილი?

ჩვენ გამოვიყენებთ ლინკერის სკრიპტს, რომელიც აკავშირებს ობიექტის ფაილებს საბოლოო შესრულებადი ფაილის შესაქმნელად. ამ სკრიპტში ჩვენ პირდაპირ მივუთითებთ, რომ გვინდა ჩატვირთოთ მონაცემები 0x100000 მისამართზე.

აქ არის ასამბლერის კოდი:

;;kernel.asm ბიტი 32 ;nasm დირექტივა - 32 ბიტიანი სექცია .ტექსტი გლობალური დაწყება extern kmain ;kmain განსაზღვრულია c ფაილის დაწყებაში: cli ;ბლოკის შეფერხებები mov esp, stack_space ;დასტის მაჩვენებლის დაყენება ზარი kmain hlt ;CPU განყოფილების შეჩერება .bss resb 8192 ;8KB stack stack_space-ისთვის:

პირველი ინსტრუქცია, ბიტი 32, არ არის x86 შეკრების ინსტრუქცია. ეს არის დირექტივა NASM ასამბლერისთვის, რომელიც განსაზღვრავს კოდის გენერირებას პროცესორისთვის, რომელიც მუშაობს 32 ბიტიან რეჟიმში. ჩვენს შემთხვევაში ეს არ არის აუცილებელი, მაგრამ ზოგადად სასარგებლო.

განყოფილება კოდით იწყება მეორე სტრიქონზე.

გლობალური არის კიდევ ერთი NASM დირექტივა, რომელიც წყაროს კოდის სიმბოლოებს გლობალურს ხდის. ამ გზით დამაკავშირებელმა იცის სად არის საწყისი სიმბოლო - ჩვენი შესვლის წერტილი.

kmain არის ფუნქცია, რომელიც განისაზღვრება kernel.c ფაილში. extern ნიშნავს, რომ ფუნქცია გამოცხადებულია სხვაგან.

შემდეგ მოდის start ფუნქცია, რომელიც უწოდებს kmain ფუნქციას და აჩერებს პროცესორს hlt ინსტრუქციით. ამიტომაც წინასწარ გამორთეთ შეფერხებები cli ინსტრუქციის გამოყენებით.

იდეალურ შემთხვევაში, ჩვენ უნდა გამოვყოთ გარკვეული მეხსიერება და მივუთითოთ მას სტეკის მაჩვენებლით (esp). თუმცა, როგორც ჩანს, GRUB-მა ეს უკვე გააკეთა ჩვენთვის. თუმცა, თქვენ მაინც გამოყოფთ გარკვეულ ადგილს BSS განყოფილებაში და გადაიტანეთ სტეკის მაჩვენებელი მის დასაწყისში. ჩვენ ვიყენებთ resb ინსტრუქციას, რომელიც ინახავს ბაიტების მითითებულ რაოდენობას. kmain-ის გამოძახებამდე დაუყოვნებლივ, სტეკის მაჩვენებელი (esp) დაყენებულია სწორი ადგილიმოძრაობის ინსტრუქციით.

ბირთვი C

kernel.asm-ში ჩვენ დავურეკეთ kmain() ფუნქციას. ამრიგად, ჩვენი C კოდი უნდა დაიწყოს შესრულება kmain()-ით:

/* * kernel.c */ void kmain(void) ( const char *str = "ჩემი პირველი ბირთვი"; char *vidptr = (char*)0xb8000; //ვიდეო mem იწყება აქ. unsigned int i = 0; unsigned int j = 0 /* ეს მარყუჟი ასუფთავებს ეკრანს * არის 25 სტრიქონი 80 სვეტიდან თითოეულ ელემენტს სჭირდება 2 ბაიტი */ ხოლო (j< 80 * 25 * 2) { /* blank character */ vidptr[j] = " "; /* attribute-byte - light grey on black screen */ vidptr = 0x07; j = j + 2; } j = 0; /* this loop writes the string to video memory */ while(str[j] != "\0") { /* the character"s ascii */ vidptr[i] = str[j]; /* attribute-byte: give character black bg and light grey fg */ vidptr = 0x07; ++j; i = i + 2; } return; }

ჩვენი ბირთვი ყველაფერს გააკეთებს არის ეკრანის გასუფთავება და ხაზის "ჩემი პირველი ბირთვის" ჩვენება.

ჯერ ვქმნით vidptr მაჩვენებელს, რომელიც მიუთითებს მისამართზე 0xb8000. დაცულ რეჟიმში, "ვიდეო მეხსიერება" იწყება ამ მისამართიდან. ეკრანზე ტექსტის საჩვენებლად, ჩვენ ვიტოვებთ 80 ASCII სიმბოლოს 25 ხაზს, დაწყებული 0xb8000-დან.

თითოეული სიმბოლო ნაჩვენებია არა ჩვეულებრივი 8 ბიტით, არამედ 16-ით. პირველი ბაიტი ინახავს თავად სიმბოლოს, ხოლო მეორე - ატრიბუტი-ბაიტი. იგი აღწერს პერსონაჟის ფორმატირებას, როგორიცაა მისი ფერი.

მწვანე სიმბოლო s შავ ფონზე გამოსატანად ჩვენ დავწერთ ამ სიმბოლოს პირველ ბაიტში და მნიშვნელობას 0x02 მეორეში. 0 ნიშნავს შავ ფონს, 2 ნიშნავს მწვანე ტექსტის ფერს.

აქ არის ფერადი სქემა:

0 - შავი, 1 - ლურჯი, 2 - მწვანე, 3 - ცისფერი, 4 - წითელი, 5 - იისფერი, 6 - ყავისფერი, 7 - ღია ნაცრისფერი, 8 - მუქი ნაცრისფერი, 9 - ღია ცისფერი, 10/a - ღია მწვანე, 11/b - ღია ცისფერი, 12/c - ღია წითელი, 13/d - ღია მაგენტა, 14/e - ღია ყავისფერი, 15/f - თეთრი.

ჩვენს ბირთვში გამოვიყენებთ ღია ნაცრისფერ ტექსტს შავ ფონზე, ასე რომ, ჩვენს ატრიბუტ ბაიტს ექნება მნიშვნელობა 0x07.

პირველ წრეში პროგრამა ბეჭდავს ცარიელ სიმბოლოს მთელ 80x25 ზონაში. ეს გაასუფთავებს ეკრანს. შემდეგ ციკლში, სიმბოლოები ნულშეწყვეტილი სტრიქონიდან „ჩემი პირველი ბირთვი“ ატრიბუტის ბაიტით, რომელიც ტოლია 0x07, ჩაიწერება „ვიდეო მეხსიერებაში“. ეს დაბეჭდავს სტრიქონს ეკრანზე.

დამაკავშირებელი ნაწილი

ჩვენ უნდა შევკრიბოთ kernel.asm ობიექტურ ფაილში NASM-ის გამოყენებით; შემდეგ გამოიყენეთ GCC kernel.c სხვა ობიექტურ ფაილში შესადგენისთვის. შემდეგ ისინი უნდა დაერთოს შესრულებად ჩატვირთვის ბირთვს.

ამისთვის გამოვიყენებთ სავალდებულო სკრიპტს, რომელიც არგუმენტად გადაეცემა ld-ს.

/* * link.ld */ OUTPUT_FORMAT(elf32-i386) ENTRY(დაწყება) SECTIONS ( . = 0x100000; .text: ( *(.text) ) .data: ( *(.data) ) .bss: ( *( .ბსს) ))

ჯერ ვიკითხავთ გამომავალი ფორმატიროგორც 32-ბიტიანი შესრულებადი და დამაკავშირებელი ფორმატი (ELF). ELF არის სტანდარტული ფორმატი ბინარული ფაილები x86 არქიტექტურის Unix სისტემები. შესვლაიღებს ერთ არგუმენტს, რომელშიც მითითებულია სიმბოლოს სახელი, რომელიც არის შესასვლელი წერტილი. სექციები- ეს ყველაზე მეტია მნიშვნელოვანი ნაწილი. ის განსაზღვრავს ჩვენი შესრულებადი ფაილის მარკირებას. ჩვენ განვსაზღვრავთ, თუ როგორ უნდა იყოს დაკავშირებული სხვადასხვა განყოფილებები და სად განვათავსოთ ისინი.

SECTIONS-ის შემდეგ ფრჩხილებში, წერტილი (.) აჩვენებს პოზიციის მრიცხველს, რომელიც ნაგულისხმევია 0x0. ის შეიძლება შეიცვალოს, რასაც ჩვენ ვაკეთებთ.

მოდით შევხედოთ შემდეგი ხაზი: .ტექსტი: ( *(.ტექსტი) ) . ვარსკვლავი (*) არის სპეციალური სიმბოლო, რომელიც შეესაბამება ნებისმიერი ფაილის სახელს. გამოთქმა *(.text) ნიშნავს ყველა .ტექსტის განყოფილებას ყველა შეყვანის ფაილიდან.

ამრიგად, ლინკერი აერთიანებს ობიექტის ფაილების ყველა კოდის განყოფილებას შესრულებადი ფაილის ერთ განყოფილებაში პოზიციის მრიცხველის მისამართზე (0x100000). ამის შემდეგ, მრიცხველის მნიშვნელობა იქნება 0x100000 + მიღებული მონაკვეთის ზომა.

იგივე ხდება სხვა განყოფილებებთან.

Grub და Multiboot

ახლა ყველა ფაილი მზად არის ბირთვის შესაქმნელად. მაგრამ კიდევ ერთი ნაბიჯი რჩება.

არსებობს x86 ბირთვების ჩატვირთვის სტანდარტი ჩამტვირთველის გამოყენებით, სახელწოდებით Multiboot სპეციფიკაცია. GRUB ჩატვირთავს ჩვენს ბირთვს მხოლოდ იმ შემთხვევაში, თუ ის აკმაყოფილებს ამ სპეციფიკაციებს.

მათ შემდეგ, ბირთვი უნდა შეიცავდეს სათაურს მის პირველ 8 კილობაიტში. გარდა ამისა, ეს სათაური უნდა შეიცავდეს 3 ველს, რომელიც არის 4 ბაიტი:

  • ჯადოსნურიველი: შეიცავს ჯადოსნურ რიცხვს 0x1BADB002ბირთვის იდენტიფიცირება.
  • ველი დროშები: ჩვენ არ გვჭირდება, მოდით დავაყენოთ ნულზე.
  • ველი საკონტროლო ჯამი: თუ ამას დაუმატებთ წინა ორს, უნდა მიიღოთ ნული.

ჩვენი kernel.asm ასე გამოიყურება:

;;kernel.asm ;nasm დირექტივა - 32 ბიტიანი 32 სექცია .ტექსტი ;მრავალ ჩატვირთვის სპეციფიკა გასწორება 4 dd 0x1BADB002 ;ჯადოსნური dd 0x00 ;დროშები dd - (0x1BADB002 + 0x00) ;შეამოწმეთ. m+f+c უნდა იყოს ნულოვანი გლობალური დაწყება extern kmain ;kmain განისაზღვრება c ფაილის დაწყებაში: cli ;block interrupts mov esp, stack_space ;stack pointer call kmain hlt ;CPU განყოფილების შეჩერება .bss resb 8192 ;8KB დასტასთვის stack_space:

ბირთვის აგება

ახლა ჩვენ შევქმნით ობიექტის ფაილებს kernel.asm და kernel.c-დან და დავაკავშირებთ მათ ჩვენი სკრიპტის გამოყენებით.

Nasm -f elf32 kernel.asm -o kasm.o

ეს ხაზი გაუშვებს ასამბლერს, რათა შეიქმნას kasm.o ობიექტის ფაილი ELF-32 ფორმატში.

Gcc -m32 -c ბირთვი.c -o kc.o

"-c" ოფცია უზრუნველყოფს, რომ არ მოხდეს ფარული კავშირი კომპილაციის შემდეგ.

Ld -m elf_i386 -T ბმული.ld -o ბირთვი kasm.o kc.o

ეს გაუშვებს ლინკერს ჩვენი სკრიპტით და შექმნის შესრულებად სახელად ბირთვი.

grub-ის დაყენება და ბირთვის გაშვება

GRUB მოითხოვს ბირთვის სახელს, რათა დააკმაყოფილოს ნიმუში ბირთვი- . ასე რომ, გადაარქვით ბირთვს სახელი. მე დავარქვი ჩემი ბირთვი-701.

ახლა ჩასვით დირექტორიაში / ჩექმა. ამისათვის დაგჭირდებათ სუპერმომხმარებლის უფლებები.

IN კონფიგურაციის ფაილი GRUB grub.cfg დაამატეთ შემდეგი:

სათაური myKernel root (hd0,0) kernel /boot/kernel-701 ro

არ დაგავიწყდეთ ფარული მენიუს დირექტივის წაშლა, თუ ეს არის.

გადატვირთეთ კომპიუტერი და დაინახავთ ბირთვების სიას, თქვენის ჩათვლით. აირჩიეთ და ნახავთ:

სტატიების ეს სერია ეძღვნება დაბალი დონის პროგრამირებას, ანუ კომპიუტერის არქიტექტურას, ოპერაციული სისტემების დიზაინს, ასამბლეის ენის პროგრამირებას და მასთან დაკავშირებულ სფეროებს. ჯერჯერობით წერას ორი ჰაბრაუზერი აკეთებს - და . ბევრი სკოლის მოსწავლეებისთვის, სტუდენტებისთვის და თუნდაც პროფესიონალი პროგრამისტებისთვის, ეს თემები ძალიან რთული შესასწავლია. დაბალი დონის პროგრამირებას ეძღვნება უამრავი ლიტერატურა და კურსი, მაგრამ რთულია სრული და ყოვლისმომცველი სურათის მიღება. ძნელია ერთი ან ორი წიგნის წაკითხვის შემდეგ ასამბლეის ენაზე და ოპერაციულ სისტემაზე, ყოველ შემთხვევაში ზოგადი მონახაზიწარმოიდგინეთ, როგორ მუშაობს ეს სინამდვილეში რთული სისტემადამზადებულია რკინის, სილიკონისგან და მრავალი პროგრამისგან - კომპიუტერი.

ყველა თავისებურად წყვეტს სასწავლო პრობლემას. ზოგი კითხულობს უამრავ ლიტერატურას, ზოგი ცდილობს სწრაფად გადავიდეს პრაქტიკაზე და გაერკვია, ზოგი ცდილობს მეგობრებს აუხსნას ყველაფერი, რასაც სწავლობენ. და ჩვენ გადავწყვიტეთ გავაერთიანოთ ეს მიდგომები. ასე რომ, სტატიების ამ კურსში ჩვენ ეტაპობრივად გაჩვენებთ, თუ როგორ უნდა დავწეროთ მარტივი ოპერაციული სისტემა. სტატიები იქნება მიმოხილვითი ხასიათის, ანუ ისინი არ შეიცავს ამომწურავ თეორიულ ინფორმაციას, მაგრამ ჩვენ ყოველთვის ვეცდებით მივაწოდოთ ბმულები კარგ თეორიულ მასალებს და ვუპასუხოთ ყველა კითხვას, რომელიც წამოიჭრება. ჩვენ არ გვაქვს მკაფიო გეგმა, ამდენი მნიშვნელოვანი გადაწყვეტილებებიმიღებული იქნება გზაზე, თქვენი გამოხმაურების გათვალისწინებით.

ჩვენ შეიძლება განზრახ შევაფერხოთ განვითარების პროცესი, რათა თქვენ და საკუთარ თავს მივცეთ საშუალება, სრულად გავიგოთ არასწორი გადაწყვეტილების სრული შედეგები, ისევე როგორც მასში გარკვეული ტექნიკური უნარების დახვეწა. ასე რომ თქვენ არ უნდა აღიქვათ ჩვენი გადაწყვეტილებები, როგორც ერთადერთი სწორი და ბრმად დაგვიჯერეთ. კიდევ ერთხელ ხაზგასმით აღვნიშნავთ, რომ ველით მკითხველთა აქტიურობას სტატიების განხილვაში, რამაც დიდი გავლენა უნდა მოახდინოს ზოგადი პროცესიშემდგომი სტატიების შემუშავება და დაწერა. იდეალურ შემთხვევაში, დროთა განმავლობაში ვისურვებდი, რომ ზოგიერთი მკითხველი შეუერთდეს სისტემის განვითარებას.

ვივარაუდებთ, რომ მკითხველი უკვე იცნობს ასამბლეის და C ენების საფუძვლებს, ასევე კომპიუტერული არქიტექტურის ელემენტარულ ცნებებს. ანუ ჩვენ არ აგიხსნით რა არის რეგისტრი ან, ვთქვათ, ოპერატიული მეხსიერება. თუ არ გაქვთ საკმარისი ცოდნა, ყოველთვის შეგიძლიათ მიმართოთ დამატებით ლიტერატურას. ცნობების მოკლე სია და კარგი სტატიების მქონე საიტების ბმულები მოცემულია სტატიის ბოლოს. ასევე მიზანშეწონილია იცოდეთ როგორ გამოიყენოთ Linux, რადგან კომპილაციის ყველა ინსტრუქცია მოცემულია სპეციალურად ამ სისტემისთვის.

ახლა კი - უფრო ახლოს აზრთან. სტატიის დანარჩენ ნაწილში თქვენთან ერთად დავწერთ კლასიკური პროგრამა « გამარჯობა მსოფლიო" ჩვენი Helloworld იქნება ცოტა კონკრეტული. ის არ დაიწყება არცერთიდან ოპერაციული სისტემა, მაგრამ პირდაპირ, ასე ვთქვათ, "შიშველ მეტალზე". სანამ კოდის წერას დავიწყებთ, მოდით გავარკვიოთ ზუსტად როგორ ვცდილობთ ამის გაკეთებას. და ამისთვის უნდა განვიხილოთ კომპიუტერის ჩატვირთვის პროცესი.

ასე რომ, აიღეთ თქვენი საყვარელი კომპიუტერი და დააჭირეთ სისტემის ერთეულზე ყველაზე დიდ ღილაკს. ჩვენ ვხედავთ სასაცილო ეკრანმზოგს, სისტემის ერთეულიდინამიკი მხიარულად ისმის და გარკვეული პერიოდის შემდეგ ოპერაციული სისტემა იტვირთება. როგორც გესმით, ოპერაციული სისტემა ინახება მყარ დისკზე და აქ ჩნდება კითხვა: როგორ ჯადოსნურადოპერაციული სისტემა ჩაიტვირთა RAM-ში და დაიწყო შესრულება?

იცოდეთ ეს: სისტემა, რომელიც ნებისმიერ კომპიუტერზეა, პასუხისმგებელია ამაზე და მის სახელს - არა, Windows-ს კი არა, ენას დააწვინეთ - მას BIOS ჰქვია. მისი სახელი ნიშნავს Basic Input-Output System, ანუ ძირითადი შეყვანის-გამომავალი სისტემა. BIOS მდებარეობს დედაპლატზე არსებულ პატარა ჩიპზე და იწყება დაჭერისთანავე დიდი ღილაკიჩართულია BIOS-ს აქვს სამი ძირითადი ამოცანა:

  1. აღმოაჩინე ყველა დაკავშირებული მოწყობილობა (პროცესორი, კლავიატურა, მონიტორი, ოპერატიული მეხსიერება, ვიდეო ბარათი, თავი, მკლავები, ფრთები, ფეხები და კუდები...) და შეამოწმეთ მათი ფუნქციონირება. ამაზე პასუხისმგებელია POST (Power On Self Test) პროგრამა. თუ სასიცოცხლო მნიშვნელობის აპარატურა არ არის გამოვლენილი, მაშინ ვერანაირი პროგრამული უზრუნველყოფა ვერ დაეხმარება და ამ ეტაპზე სისტემის სპიკერიატყდება რაღაც საშინელებას და ოპერაციული სისტემა საერთოდ ვერ მიიღებს მას. მოდი სამწუხარო რამეებზე ნუ ვილაპარაკებთ, დავუშვათ, რომ გვაქვს სრულად მომუშავე კომპიუტერი, გავიხაროთ და გადავიდეთ მეორეზე. BIOS ფუნქციები:
  2. ოპერაციული სისტემის მიწოდება ძირითადი ნაკრებიაპარატურასთან მუშაობის ფუნქციები. მაგალითად, BIOS ფუნქციების საშუალებით შეგიძლიათ ეკრანზე ტექსტის ჩვენება ან კლავიატურიდან მონაცემების წაკითხვა. ამიტომაც ჰქვია ძირითადი სისტემა I/O როგორც წესი, ოპერაციული სისტემა ამ ფუნქციებზე წვდომას წყვეტს.
  3. ოპერაციული სისტემის ჩამტვირთველის გაშვება. ამ შემთხვევაში, როგორც წესი, იკითხება ჩატვირთვის სექტორი - შენახვის საშუალების პირველი სექტორი (ფლოპი დისკი, მყარი დისკი, CD, ფლეშ დრაივი). მედია გამოკითხვის რიგის დაყენება შესაძლებელია BIOS SETUP-ში. ჩატვირთვის სექტორი შეიცავს პროგრამას, რომელსაც ზოგჯერ პირველადი ჩამტვირთველი ეწოდება. უხეშად რომ ვთქვათ, ჩამტვირთველის ამოცანაა ოპერაციული სისტემის გაშვება. ოპერაციული სისტემის ჩატვირთვის პროცესი შეიძლება იყოს ძალიან სპეციფიკური და დიდად დამოკიდებული მის მახასიათებლებზე. ამიტომ, პირველადი ჩამტვირთველი იწერება უშუალოდ OS-ის დეველოპერების მიერ და იწერება ჩატვირთვის სექტორში ინსტალაციის დროს. როდესაც ჩამტვირთავი იწყება, პროცესორი რეალურ რეჟიმშია.
სამწუხარო ამბავი ის არის, რომ ჩამტვირთველი უნდა იყოს მხოლოდ 512 ბაიტის ზომა. რატომ ასე ცოტა? ამისათვის ჩვენ უნდა გავეცნოთ ფლოპი დისკის სტრუქტურას. აქ არის ინფორმაციული სურათი:

სურათზე ნაჩვენებია დისკის ზედაპირი. ფლოპი დისკს აქვს 2 ზედაპირი. თითოეულ ზედაპირს აქვს რგოლის ფორმის ბილიკები (ტრასები). თითოეული ტრეკი დაყოფილია რკალის ფორმის პატარა ნაჭრებად, რომლებსაც სექტორები ეწოდება. ასე რომ, ისტორიულად, ფლოპი დისკის სექტორს აქვს 512 ბაიტის ზომა. პირველივე სექტორი დისკზე, ჩატვირთვის სექტორი, იკითხება BIOS-ის მიერ მეხსიერების ნულოვან სეგმენტში ოფსეტურით 0x7C00, შემდეგ კი კონტროლი გადადის ამ მისამართზე. ჩამტვირთველი ჩვეულებრივ იტვირთება არა თავად OS, არამედ სხვა ჩამტვირთველი პროგრამა ინახება დისკზე, მაგრამ რაიმე მიზეზის გამო (სავარაუდოდ, ეს არის ზომა) არ ჯდება ერთ სექტორში და რადგან ამ დროისთვის ჩვენი OS-ის როლს ასრულებს ბანალური Helloworld, ჩვენი მთავარი მიზანია კომპიუტერის გაკეთება. დაიჯერეთ ჩვენი ოპერაციული სისტემის არსებობა, თუნდაც ერთ სექტორში, და გაუშვით იგი.

როგორ არის სტრუქტურირებული ჩატვირთვის სექტორი? კომპიუტერზე, ჩატვირთვის სექტორისთვის ერთადერთი მოთხოვნაა, რომ მისი ბოლო ორი ბაიტი შეიცავდეს მნიშვნელობებს 0x55 და 0xAA - ხელმოწერები. ჩატვირთვის სექტორი. ასე რომ, უკვე მეტ-ნაკლებად გასაგებია, რა უნდა გავაკეთოთ. მოდით დავწეროთ კოდი! მოცემული კოდი იწერება yasm ასამბლერისთვის.

განყოფილება. ტექსტი

გამოყენება16

org 0x7C00 ; ჩვენი პროგრამა ჩატვირთულია მისამართზე 0x7C00

დაწყება:

mov ax, cs

mov ds, ax ; აირჩიეთ მონაცემთა სეგმენტი



mov si, შეტყობინება

cld ; მიმართულება სიმებიანი ბრძანებებისთვის

mov ah, 0x0E ; BIOS-ის ფუნქციის ნომერი

mov bh , 0x00 ; ვიდეო მეხსიერების გვერდი

puts_loop:

lodsb ; ჩატვირთეთ შემდეგი სიმბოლო al-ში

ტესტი ალ, ალ ; null სიმბოლო ნიშნავს ხაზის დასასრულს

jz puts_loop_exit

int 0x10 ; გამოიძახეთ BIOS ფუნქცია

jmp puts_loop

puts_loop_exit:

jmp$; მარადიული ციკლი



შეტყობინება:

db "გამარჯობა სამყარო!" , 0

დასრულება:

ჯერ 0x1FE - დასრულება+ დაწყება დბ 0

დბ 0x55, 0xAA ; ჩატვირთვის სექტორის ხელმოწერა

ეს მოკლე პროგრამასაჭიროებს რამდენიმე მნიშვნელოვან განმარტებას. ხაზი org 0x7C00 საჭიროა იმისათვის, რომ ასამბლერმა (იგულისხმება პროგრამა და არა ენა) სწორად გამოთვალოს მისამართები ლეიბლებისა და ცვლადების (puts_loop, puts_loop_exit, message). ამიტომ ვაცნობებთ მას, რომ პროგრამა ჩაიტვირთება მეხსიერებაში მისამართზე 0x7C00.
ხაზებში
mov ax, cs

mov ds, ax
მონაცემთა სეგმენტი (ds) დაყენებულია კოდის სეგმენტის (cs) ტოლი, ვინაიდან ჩვენს პროგრამაში მონაცემებიც და კოდიც ინახება ერთ სეგმენტში.

შემდეგ ციკლში, შეტყობინება "Hello World!" ნაჩვენებია სიმბოლოების მიხედვით. ამ მიზნით გამოიყენება 0x10 შეწყვეტის ფუნქცია 0x0E. მას აქვს შემდეგი პარამეტრები:
AH = 0x0E (ფუნქციის ნომერი)
BH = ვიდეო გვერდის ნომერი (ჯერ არ ინერვიულოთ, მიუთითეთ 0)
AL = ASCII სიმბოლოს კოდი

სტრიქონზე "jmp$" პროგრამა იყინება. და მართალია, არ არის საჭირო დამატებითი კოდის შესრულება. თუმცა, იმისათვის, რომ კომპიუტერმა კვლავ იმუშაოს, თქვენ უნდა გადატვირთოთ.

სტრიქონში „ჯერ 0x1FE-finish+start db 0“ პროგრამის დანარჩენი კოდი (გარდა ბოლო ორი ბაიტისა) ივსება ნულებით. ეს კეთდება ისე, რომ შედგენის შემდეგ, პროგრამის ბოლო ორი ბაიტი შეიცავს ჩატვირთვის სექტორის ხელმოწერას.

როგორც ჩანს, პროგრამის კოდი დავალაგეთ, ახლა ვცადოთ ამ ბედნიერების შედგენა. კომპილაციისთვის ჩვენ გვჭირდება, მკაცრად რომ ვთქვათ, ასამბლერი - ზემოთ ნახსენები



რაიმე შეკითხვა?

შეატყობინეთ შეცდომას

ტექსტი, რომელიც გაეგზავნება ჩვენს რედაქტორებს: