Skriver ditt eget operativsystem. En guide för att skapa ett enkelt UNIX-liknande operativsystem. Arbeta med portar: läsning och utdata

WikiHow fungerar som en wiki, vilket innebär att många av våra artiklar är skrivna av flera författare. Under skapandet av denna artikel arbetade 90 personer, inklusive anonymt, för att redigera och förbättra den.

Operativsystem, som består av hundratusentals rader kod, tillåter användare att interagera med datorhårdvara. De är vanligtvis skrivna i programmeringsspråken C, C++ och assembly.

Steg

    Först, lär dig programmering. Kunskaper i montör krävs; Det rekommenderas också starkt att ha en förståelse för andra ytterligare programmeringsspråk på lägre nivå, såsom C.

    Bestäm på vilken enhet du vill ladda operativsystemet. Detta kan vara en CD, DVD, flashminne, HDD eller en annan dator.

    Bestäm hur du vill att ditt operativsystem ska se ut. Ska detta vara den fullständiga versionen av operativsystemet med grafiskt gränssnitt användargränssnitt (GUI) eller kanske något mer minimalistiskt? Du måste veta vilken riktning du ska gå i innan du påbörjar processen.

    Kontrollera vilken processorplattform ditt operativsystem stöder. AI-32 och x86_64 är de två vanligaste versionerna för persondatorer, så de kan anses vara det bästa valet.

    Bestäm om du föredrar att göra allt själv från grunden, eller om det finns kärnor som du vill bygga på systemet utifrån. Linux från grunden är ett projekt för dig som till exempel vill skapa sin egen Linux-distribution.

    Välj om du ska använda din egen bootloader eller den förbyggda Grand Unified Bootloader (GRUB). Eftersom kodning av ditt eget startprogram kräver omfattande kunskap om datorhårdvara och BIOS, kan det skjuta tillbaka programmeringsschemat för den levande kärnan.

    Ta ett beslut om vilket programmeringsspråk du ska använda. Det är naturligtvis fullt möjligt att utveckla ett OS i ett språk som Pascal eller BASIC, men det är att föredra att skriva det i C eller assemblerspråk. Assembly språk är absolut nödvändigt, eftersom vissa viktiga delar av operativsystemet kräver kunskap om just detta språk. C++, å andra sidan, innehåller de nyckelord som krävs för att köra den fullständiga versionen av operativsystemet.

    • För att bygga ett OS med C- eller C++-kod kommer du naturligtvis att använda en eller annan kompilator. Detta innebär att du måste läsa manualen/instruktionerna/dokumentationen för den valda C/C++-kompilatorn som medföljer programvaran eller finns tillgänglig på distributörens webbplats. Du måste lära dig många komplexa saker om kompilatorn, och du måste också lära dig dess design och ABI för att förbättra C++. Du förväntas förstå de olika exekveringsuppgiftsformaten (ELF, PE, COFF, vanlig binär, etc.) och kommer att notera att det ursprungliga Windows PE-formatet (.exe) är skyddat av upphovsrätt.
  1. Välj Application Programming Interface (API). Ett urval av bra API:er är POSIX, eftersom det är väldokumenterat. Alla Unix-system har åtminstone delvis stöd för POSIX, så det skulle vara trivialt att paketera Unix-program till ditt operativsystem.

    Bestäm en design. Det finns monolitiska kärnor och mikrokärnor. Monolitiska kärnor kör alla tjänster i kärnan, medan mikrokärnor har en liten kärna i kombination med en anpassad implementering av tjänster. I allmänhet är monolitiska kärnor snabbare, men mikrokärnor har bättre isolering och skydd mot eventuella fel.

    Tänk på utveckling och lagarbete. På så sätt kommer du att spendera mindre tid på att lösa stora problem, vilket gör att du kan skapa ett bättre operativsystem på kortare tid.

    Torka inte din hårddisk helt. Kom ihåg att formatering av disken kommer att radera alla dina data permanent! Använd GRUB eller en annan hanterare för att duplicera uppstart av din dator från ett annat operativsystem tills din version är fullt funktionell redo.

    Börja smått. Var uppmärksam på de små sakerna först, som textvisning och avbrott, innan du går vidare till komplexa element som minneshantering och multitasking.

    Spara säkerhetskopia senaste fungerande version. Detta ger en viss sinnesfrid om något går helt fel med den aktuella versionen av ditt operativsystem eller efterföljande tillägg. Om din dator går sönder och inte kan starta, som du själv förstår, är det ett utmärkt tillfälle att ha en andra kopia att arbeta med, så att du kan fixa de befintliga problemen.

    Testa ditt nya operativsystem på en virtuell maskin. Istället för att starta om din dator varje gång du gör ändringar eller överför filer från din produktionsdator till testmaskinen, kan du använda ett program för att köra operativsystemet i en virtuell maskin medan ditt nuvarande operativsystem fortsätter att köras. VM-applikationer inkluderar VMWare (som också har en fritt tillgänglig server), alternativ öppen källkod, Bochs, Microsoft Virtual PC (inte kompatibel med Linux) och XVM VirtualBox.

    Släpp en releaseversion. Detta gör att användare kan berätta om möjliga brister i ditt operativsystem.

  2. Operativsystemet bör också vara användarvänligt, så se till att lägga till användbara funktioner som blir en integrerad del av din design.

    • När utvecklingen är klar, fundera på om du vill göra koden offentligt tillgänglig eller göra den privat.
    • Se till att göra säkerhetsfunktioner till din högsta prioritet om du vill att ditt system ska vara livskraftigt.
    • Starta inte ett utvecklingsprojekt för operativsystem med målet att lära dig programmera. Om du inte kan C, C++, Pascal eller något av de andra relevanta språken och funktionerna, inklusive pekartyper, lågnivåbitoperationer, bitväxling, inline assembler, etc., så är du inte redo ännu för att skapa OS.
    • Bläddra bland portaler som OSDev och OSDever för att hjälpa dig att förbättra ditt eget operativsystem. Var särskilt medveten om att för de flesta frågor föredrar OSDev.org-communityt att du själv konsulterar webbplatsens innehåll istället för att gå med i ett forum. Om du ändå bestämmer dig för att gå med i forumets medlemmar måste det finnas vissa förutsättningar för detta. Du måste ha ingående kunskaper i C eller C++ och x86 assemblerspråk. Du bör också förstå allmänna och komplexa begrepp inom programmering som länkade listor, köer, etc. OSDev-communityt anger uttryckligen i sina regler att ingen kommer att passa nya programmerare. Om du försöker utveckla ett OS är det självklart att du är en programmeringsgud. Du måste också läsa processormanualen för den processorarkitektur du väljer; till exempel x86 (Intel), ARM, MIPS, PPC, etc. En sådan referensbok om processorstrukturen kan lätt hittas genom att söka på Google ("Intel Manuals", "ARM manuals", etc.). Gå inte med i OSDev.org-forumet för att ställa uppenbara frågor. Detta kommer helt enkelt att resultera i svar som "Läs den jävla manualen." Först bör du prova att läsa Wikipedia, manualer för de olika verktyg du ska använda.
    • Kontrollera efter potentiella döda punkter och andra fel. Luckor, återvändsgränder och andra problem kan påverka ditt operativsystems design.
    • Om du vill ha en enklare metod, tänk på Linux-distributioner som Fedora Revisor, Custom Nimble X, Puppy Remaster, PCLinuxOS mklivecd eller SUSE Studio och SUSE KIWI. Det operativsystem som skapas tillhör dock företaget som först introducerade den här tjänsten (även om du har rättigheterna att fritt distribuera, ändra och köra den som du vill under GPL).
    • En bra lösning skulle vara att skapa en helt ny partition för operativsystemet som utvecklas.

    Varningar

    • Att vårdslöst kopiera operativsystemet till hårddisken kan skada det fullständigt. var försiktig
    • Du kommer inte att ha ett helt färdigt system på två veckor. Börja med ett startbart operativsystem och gå sedan vidare till mer intressanta saker.
    • Om du gör något hänsynslöst som att skriva slumpmässiga bytes till slumpmässiga I/O-portar, kommer du att förstöra ditt OS och kan (i teorin) steka din hårdvara.
    • Förvänta dig inte att det ska vara lätt att bygga ett kvalitetsoperativsystem. Det finns många komplexa ömsesidiga beroenden. Till exempel, för att operativsystemet ska kunna hantera flera processorer måste din minneshanterare ha "låsningsmekanismer" för att förhindra extra processorer från att komma åt samma resurs samtidigt. De "block" som används involverar en schemaläggare för att säkerställa att endast en processor har åtkomst till en kritisk resurs vid varje given tidpunkt, medan alla andra är inaktiva. Emellertid beror schemaläggarens funktion på närvaron av minneshanteraren. Detta är ett exempel på dödlägesberoende. Nej standardmetod lösa sådana problem; Varje skapare av operativsystem förväntas vara tillräckligt skicklig för att komma med sin egen lösning.

Denna artikelserie ägnas åt programmering på låg nivå, det vill säga datorarkitektur, enhet operativsystem, assemblerspråksprogrammering och relaterade områden. Hittills är det två habrausers som skriver - iley och pehat. För många gymnasieelever, studenter och till och med professionella programmerare visar sig dessa ämnen vara mycket svåra att lära sig. Det finns mycket litteratur och kurser som ägnas åt programmering på låg nivå, men det är svårt att få en komplett och allomfattande bild. Det är svårt, efter att ha läst en eller två böcker om assemblerspråk och operativsystem, att åtminstone i allmänna termer föreställa sig hur detta komplexa system av järn, kisel och många program – en dator – faktiskt fungerar.

Alla löser inlärningsproblemet på sitt sätt. Vissa människor läser mycket litteratur, vissa försöker snabbt gå vidare till praktiken och räkna ut det medan de går, vissa försöker förklara för sina vänner allt de studerar. Och vi bestämde oss för att kombinera dessa tillvägagångssätt. Så i den här artikelkursen kommer vi att demonstrera steg för steg hur man skriver ett enkelt operativsystem. Artiklarna kommer att vara av översiktskaraktär, det vill säga att de inte kommer att innehålla uttömmande teoretisk information, men vi kommer alltid att försöka ge länkar till bra teoretiskt material och svara på alla frågor som dyker upp. Vi har ingen tydlig plan, så många viktiga beslut kommer att fattas längs vägen, med hänsyn till din feedback.

Vi kan medvetet stoppa utvecklingsprocessen för att tillåta dig och oss själva att till fullo förstå de fulla konsekvenserna av ett dåligt beslut, samt för att finslipa vissa tekniska färdigheter på det. Så du ska inte uppfatta våra beslut som de enda korrekta och blint tro oss. Låt oss än en gång betona att vi förväntar oss att läsarna är aktiva i att diskutera artiklar, vilket i hög grad bör påverka den övergripande processen för att utveckla och skriva efterföljande artiklar. Helst skulle jag med tiden vilja att några av läsarna hänger med i utvecklingen av systemet.

Vi kommer att anta att läsaren redan är bekant med grunderna i assembly och C-språk, såväl som de elementära begreppen datorarkitektur. Det vill säga, vi kommer inte att förklara vad ett register eller, säg, RAM är. Om du inte har tillräckligt med kunskap kan du alltid vända dig till ytterligare litteratur. En kort lista med referenser och länkar till webbplatser med bra artiklar finns i slutet av artikeln. Det är också tillrådligt att veta hur man använder Linux, eftersom alla kompileringsinstruktioner kommer att ges specifikt för detta system.

Och nu - närmare saken. I resten av artikeln kommer vi att skriva det klassiska programmet "Hello World". Vår Helloworld kommer att vara lite specifik. Det kommer inte att lanseras från något operativsystem, utan direkt, så att säga, "på bar metall." Innan vi börjar skriva koden, låt oss ta reda på exakt hur vi försöker göra detta. Och för detta måste vi överväga processen att starta upp datorn.

Så ta din favoritdator och tryck på den största knappen på systemenheten. Vi ser en rolig skärmsläckare, systemenhet Högtalaren piper glatt och efter en stund laddas operativsystemet. Som du förstår lagras operativsystemet på en hårddisk, och här uppstår frågan: hur laddades operativsystemet magiskt in i RAM-minnet och började exekvera?

Vet detta: systemet som finns på vilken dator som helst är ansvarigt för detta, och dess namn - nej, inte Windows, spetsa tungan - det kallas BIOS. Dess namn står för Basic Input-Output System, det vill säga ett grundläggande input-output system. BIOS finns på ett litet chip på moderkort och startar omedelbart efter att du har tryckt på den stora ON-knappen. BIOS har tre huvuduppgifter:

  1. Upptäck alla anslutna enheter (CPU, tangentbord, bildskärm, Bagge, grafikkort, huvud, armar, vingar, ben och svansar...) och kontrollera deras funktionalitet. POST-programmet (Power On Self Test) ansvarar för detta. Om vital hårdvara inte upptäcks, kommer ingen programvara att kunna hjälpa, och vid denna tidpunkt kommer systemhögtalaren att gnisa något illavarslande och operativsystemet kommer inte att komma till saken alls. Låt oss inte prata om sorgliga saker, låt oss anta att vi har en fullt fungerande dator, glädjas och gå vidare till att överväga den andra BIOS-funktionen:
  2. Förse operativsystemet med en grundläggande uppsättning funktioner för att arbeta med hårdvara. Genom BIOS-funktioner kan du till exempel visa text på skärmen eller läsa data från tangentbordet. Det är därför det kallas det grundläggande input/output-systemet. Vanligtvis kommer operativsystemet åt dessa funktioner genom avbrott.
  3. Startar laddaren för operativsystemet. I det här fallet läses som regel startsektorn - den första sektorn av lagringsmediet (diskett, hårddisk, CD, flashenhet). Medieavfrågningsordningen kan ställas in i BIOS SETUP. Startsektorn innehåller ett program som ibland kallas den primära starthanteraren. Grovt sett är bootloaderns uppgift att starta operativsystemet. Processen att ladda ett operativsystem kan vara mycket specifik och mycket beroende av dess funktioner. Därför skrivs den primära starthanteraren direkt av OS-utvecklarna och skrivs till startsektorn under installationen. När starthanteraren startar är processorn i verkligt läge.
Den tråkiga nyheten är att starthanteraren endast bör vara 512 byte stor. Varför så få? För att göra detta måste vi bekanta oss med strukturen på disketten. Här är en informativ bild:

Bilden visar ytan på en diskenhet. En diskett har 2 ytor. Varje yta har ringformade banor (spår). Varje spår är uppdelat i små bågformade bitar som kallas sektorer. Så historiskt sett har en diskettsektor en storlek på 512 byte. Den allra första sektorn på disken, bootsektorn, läses av BIOS till nollminnessegmentet vid offset 0x7C00, och sedan överförs kontrollen till denna adress. Bootloadern laddar vanligtvis in i minnet inte själva operativsystemet, utan ett annat bootloader-program lagras på disken, men av någon anledning (mest troligt är denna anledning storleken) inte passar in i en sektor. Och eftersom rollen för vårt operativsystem för närvarande spelas av en banal Helloworld, är vårt huvudmål att få datorn att tro i existensen av vårt operativsystem, även i en sektor, och lansera det.

Hur är bootsektorn uppbyggd? På en PC är det enda kravet för startsektorn att dess sista två byte innehåller värdena 0x55 och 0xAA - startsektorns signatur. Så det är redan mer eller mindre klart vad vi behöver göra. Låt oss skriva kod! Den givna koden är skriven för yasm assembler.

Avsnitt .text use16 org 0x7C00 ; vårt program laddas på adress 0x7C00 start: mov ax, cs mov ds, ax ; välj datasegmentet mov si, message cld ; riktning för strängkommandon mov ah, 0x0E ; BIOS-funktionsnummer mov bh, 0x00 ; videominnessida puts_loop: lodsb ; ladda nästa symbol i al test al, al ; nolltecknet betyder slutet på raden jz puts_loop_exit int 0x10 ; anropa BIOS-funktionen jmp puts_loop puts_loop_exit: jmp $ ; evig loop meddelande: db "Hello World!", 0 finish: gånger 0x1FE-finish+start db 0 db 0x55, 0xAA ; bootsektorsignatur

Detta korta program kräver några viktiga förklaringar. Linjen org 0x7C00 behövs så att assemblern (som betyder programmet, inte språket) korrekt beräknar adresserna för etiketter och variabler (puts_loop, puts_loop_exit, meddelande). Så vi informerar honom om att programmet kommer att laddas in i minnet på adressen 0x7C00.
I rader
mov yxa, cs mov ds, yxa
datasegmentet (ds) sätts lika med kodsegmentet (cs), eftersom både data och kod i vårt program lagras i samma segment.

Nästa i slingan visas meddelandet "Hello World!" tecken för tecken. För detta ändamål används funktionen 0x0E av avbrott 0x10. Den har följande parametrar:
AH = 0x0E (funktionsnummer)
BH = videosidnummer (inte bry dig ännu, ange 0)
AL = ASCII teckenkod

På raden "jmp$" fryser programmet. Och med rätta, det finns inget behov av att exekvera extra kod. Men för att datorn ska fungera igen måste du starta om.

På raden "gånger 0x1FE-slut+start db 0" fylls resten av programkoden (förutom de två sista byten) med nollor. Detta görs så att efter kompilering innehåller programmets två sista byte signaturen för startsektorn.

Vi verkar ha sorterat ut programkoden, låt oss nu försöka kompilera denna lycka. För sammanställning behöver vi, strängt taget, en assembler - den ovan nämnda yasm. Det är tillgängligt i de flesta Linux-förråd. Programmet kan sammanställas enligt följande:

$ yasm -f bin -o hello.bin hello.asm

Den resulterande hello.bin-filen måste skrivas till startsektorn på disketten. Detta görs ungefär så här (naturligtvis, istället för fd måste du ersätta namnet på din enhet).

$ dd if=hej.bin av=/dev/fd

Eftersom inte alla fortfarande har hårddiskar och disketter kan du använda virtuell maskin t.ex. qemu eller VirtualBox. För att göra detta måste du skapa en diskettavbildning med vår bootloader och sätta in den i den "virtuella diskettenheten".
Skapa en diskavbildning och fyll den med nollor:

$ dd if=/dev/zero of=disk.img bs=1024 count=1440

Vi skriver vårt program i början av bilden:
$ dd if=hej.bin of=disk.img conv=notrunc

Vi lanserar den resulterande bilden i qemu:
$ qemu -fda disk.img -boot a

Efter lanseringen bör du se qemu-fönstret med den glada raden "Hello World!" Det är här den första artikeln slutar. Vi kommer gärna att se din feedback och önskemål.

rTYCHEFUFCHHA CHUEI UCHPYI YUFBFEMEK!

rTEDSHDHEYE CHSHCHRKHULY NPZMY VSHCHFSH OEULPMSHLP ЪBRKhFBOOSCHNY. oBUBMSHOBS ЪБЗТХЛБ, Assembler, BIOS. UEZPDOS NSCH OBLPOEG RETEYIDYN L VPMEE YOFETEUOPK Y RPOSFOPK YUBUFY - NSCH OBUYOBEN RYUBFSH SDTP. th RYUBFSH NSCH EZP VKhDEN OM SESSCHLE CHCHUPLPZP HTPCHOS y.

h OBYUBMSHOSHCHK ЪBZTHYUYL PUFBMPUSH CHOEUFY CHUEZP RBTH DPRPMOEOYK Y PO VHDEF RPMOPUFSHA ZPFPCH ZTHYFSH MAVSHCHE 32-VYFOSHCHE SDTB.

PRTEDEMEOYE PVAENB PRETBFYCHOPK RBNSFY

lPOYUOP, NPTsOP RPDUDUYFBFSH PVYAIN RBNSFY CHTHYUOHA CH SDTE - RETEVYTBFSH BDTEUB PF 0x100000 Y RSHCHFBFSHUS ЪBRYUBFSH FKhDB OBYUEOYE PFMYYOHMSY JA 0PFxXJFJU. eUMY RTY YUFEOYY NSCH RPMKHYUBEN RPMHYUEOOPE OBYUEOYE, FP CHUЈ IPTPYP, YOBYUE RBNSFSH LPOYUYMBUSH - ЪBRPNYOBEN BDTEU RPUMEDOEZP KHDYUOPZP YUFEOYS, LPRETBNSFPIEN YRBPNYOBEN BDTEU. pDOBLP FBLPC URPUPV YNEEF DCHB OEDPUFBFLB:

1) EZP UMEDHEF YURPMSHЪPCHBFSH DP CHLMAYUEOYS UFTBOYUOPK BDTEUBGYY, YUFPVSH YNEFSH DPUFHR LP CHUEK ZHJYYUEULPK RBNSFY, MYVP KHUFTBYCHBFSH ЪBRYPOOSH" RETEFT "BRYPOOSH" YUETE. mYYOSS FTBFB CHTENEY, RTY HUMPCHYY, YuFP FEUFYTPCHBOYE RBNSFY BIOS Y FBL CHSHHRPMOSEF RTY OBYUBMSHOPK YOYGYBMYBGYY, B NSCH DEMBEN DCHPKOHA TBVPFH.

2). l FPNH CE BIOS RYYEF CH UBNHA PVSHYUOKHA RBNSFSH FBVMYGSH ACPI, LPFPTSHCHE RTYZPDSFUS PRETBGYPOOPK UYUFEN Y OE UFPYF YI ЪBFYTBFSH DP RTPYUFEOOS.

yЪ LFPPZP UMEDHEF, YuFP MHYUYE URTPUYFSH RTP PVYAEN PRETBFYCHOPK RBNSFY X BIOS, VMBZP PÅ RTEDPUFBCHMSEF CHUE OEPVIPDYNSCHE ZHKHOLGYY.

yUFPTYYUEULY RETCHPK JHOLGYEK PRTEDEMEOYS PVYANB PRETBFYCHOPK RBNSFY VSHMP RTETSCHCHBOIE 0x12. pOP OE RTOYNBEF OILBLYI CHIPDOSCHI RBTBNEFTPCH, CH OM CHSHCHIPDE CH TEZYUFTE AX UPDETSYFUS TBNET VBPCHPK RBNSFY CH LYMPVBKFBI. vББПЧБС RBNSFSH - FE UBNSHCHE 640 lv DPUFHROSCHE CH TEBMSHOPN TETSINE. UEKYUBU CHSC HCE OE UNPTSEFE OBKFY LPNRSHAFET, ZDE VSH VSHMP NEOEE 640 lv RBNSFY, OP NBMP MY. yURPMSHЪPCHBFSH EЈ OBN UNSHUMB OEF - EUMY RTPGEUUPT RPDDETSYCHBEF ЪBEEYEЈOOOSCHK TETSYN, FP CHTSD MY KH OEZP VKhDEF NEOSHYE OEULPMSHLYI NEZBVFBKF .

pVYaЈNSCH RBNSFY TPUMY Y 640 lv UFBMP NBMP. fPZDB RPSCHYMBUSH OPCHBS ZHKHOLGYS - RTETSCHCHBOYE 0x15 AH=0x88. pOB CHP'CHTBEBEF CH AX TBNET TBUYYTEOOPK RBNSFY (STUDENTER 1 nv) CH LYMPVBKFBI CH AX. bFB ZHOLGYS OE NPTSEF CHPCHTBEBFSH OBYUEOYS VPMSHYE 15 nov (15 + 1 YFPZP 16 nov).

lPZDB Y 16 nv UVBMP OEDPUFBFPYuOP RPSCHYMBUSH OPCHBS ZHOLGYS - RTETSCHCHBOIE 0x15, AX=0xE801. POB CHPCHTBEBEF TEKHMSHFBFSCH BC CH 4 TEZYUFTBI:

AXE - TBNET TBUYYTEOOOPK RBNSFY DP 16 nv Ch LYMPVBKFBI
BX - TBNET TBUYYTEOOOPK RBNSFY UCHETI 16 nv L VMPLBI RP 64 lv
CX - TBNET ULPOZHYZHTYTPCHBOOPC TBUYYTEOOPK RBNSFY DP 16 nv Ch LYMPVBKFBI
DX - TBNET ULPOZHYZHTYTPCHBOOPC TBUYYTEOOPK RBNSFY UCHETI 16 nv h VMPLBI RP 64 lv

YuFP FBLPE "ULPOZHYZHTYTPCHBOOBS" RBNSFSH RTPYCHPDYFEMY BIOS UHDS RP CHUENKH OE DPZPCHPTYMYUSH, RPFPNKH OBDP RTPUFP, EUMY CH AX Y BX OHMY, VTBFSH OBYUEOYE Y CX.

oP Y LFPZP PLBBBMPUSH NBMP. CHEDSH CHUE RETEYUUMEOOSCH CHE ZHKHOLGYY YNEAF PZTBOYUEOOYE PVAENB RBNSFY CH 4 stjärnor, L FPNH TSE OE KHYYFSHCHBAF FP, YuFP RBNSFSH NPTsEF VSCFS OE OERTETCHCHOSCHN VMPLPN. rPFPNH CH OPCHSHHI BIOS RPSCHYMBUSH EEЈ PDOB ZHOLGYS - RTETSCHCHBOIE 0x15, AX=0xE820. POB CHPCHTBEBEF OE RTPUFP YUYUMP, B LBTFH RBNSFY. CHIPDOSHCHE RBTBNEFTSCH:

EAX=0xE820
EDX=0x534D4150 ("SMAP")
EBX - UNEEEEOYE PF OYUBMB LBTFSH RBNSFY (DMS OYUBMB 0)
ECX - TBNET VKHZHETB (LBL RTBCHYMP 24 VBKFB - TBNET PDOPZP BMENEOFB)
ES:DI - BDTEU VKHZHETB, LKhDB OBDP ЪBRYUBFSH PYUETEDOPK BMENEOF

CHPIPDOSHE RBTBNEFTSCH:

EAX=0x534D4150 ("SMAP")
EBX - OPChPE UNEEOOYE DMS UMEDHAEEZP CHSHCHJPCHB ZHKHOLGYY. eUMY 0, FP CHUS LBTFB RBNSFY RTPYUIFBOB
ECX - LPMYUEUFChP TEBMSHOP CHPCHTBEYOOOSCHI VBKF (20 YMY 24 VBKFB)
h KHLBBOOPN VKHZHETE UPDETSYFUS PYUETEDOPK BMENEOF LBTFSH RBNSFY.

LBTSDSCHK BMENEOF LBTFSH RBNSFY YNEEF UMEDHAEHA UFTHLFHTH (OBRYYKH CH UYOFBLUIUE UI, RPFPNKH YuFP TBVPT DBOOSHI NSCH VKHDEN DEMBFSH HTSE CH SDTE):

Struktur (osignerad lång lång bas; //vБПЧШЧК ЖЪЪУУУУЛК БДТУ ТЭЗІПOB osignerad lång lång längd; //тБЪNET TEZYPOB CH VBKFBI unsigned long type; //fpiBYtTE lång typ;Y_POYr/accepterad lång typ; OOSH BFTYVHFSCH ACPI );

rPUMEDOYK BMENEOF UFTKHLFKhTSCH OE PVSBFEMEO. eeЈ CH PDOPN YUFPYUOYLE CHYDEM, UFP RETED ЪBRTPUPN BMENEOFB UFPYF RPNEUFYFSH FHDB EDYOYULH. lPOYUOP, UEKYUBU NSCHOE RPDDETSYCHBEN ACPI, OP MHYUYE ЪBTBOEE RPBBVPFYFUS P FPN, YUFPVSH RPMKHYUYFSH LBL NPTsOP VPMSHYE DBOOSCHI. h PFMYYUYY PF RBTBNEFTPCH RBNSFY, CHU PUFBMSHOP NPTsOP MEZLP KHOBFSH Y YY ЪBEEYEЈOOOPZP TETSINB OBRTSNHA, VE BIOS.

TEZYPOSH RBNSFY, PRYUSCHCHBENSHCH LBTFPC, NPZHF VshchFSH OUEULPMSHLYI FYRPCH:

1 - pVSHYUOBS RBNSFSH. nPTsEF VSHFSH UCHPVPDOP YURPMSHЪPCHBOB pu DMS UCHPYI GEMEC. rPLB NSCH FPMSHLP L OEK Y VKhDEN PVTBEBFSHUS, B CHUY PUFBMSHOP RTPRKHULBFSH.
2 - ъБТЭЧТПЧБОП (OBRTYNET, LPD BIOS). bFB RBNSFSH NPTSEF VSCHFSH LBL ZHJYYUEULY OEDPUFHROB DMS ЪBRYUY, FBL Y RTPUFP ЪBRYUSH FHDB OETSEMBFEMSHOB. fBLHA RBNSFSH MHYUYE OE FTPZBFSH.
3 - dPUFHROP RPUME RTPYUFEOOIS FBVMYG ACPI. CHETPSFOP, YNEOOP CH FYI VMPLBI LFY FBVMYGSH Y ITBOSFUS. rPLB DTBKCHET ACPI OE RTPYUIFBEF FBVMYGSHCH, bFH RBNSFSH MHYUYE OE FTPZBFSH. rPFPN NPTsOP YURPMSHЪPCHBFSH FBL CE, LBL Y RBNSFSH FYRB 1.
4 - bFH RBNSFSH UMEDHEF UPITBOSFSH NETSDH NVS UEUUYSNY. fBLHA RBNSFSH NSCH FTPZBFSH OE VKhDEN, RPLB OE KHOBEN, YuFP FBLPE NVS UEUUYY:-)

OE CHUE BIOS NPZHF RPDDETSYCHBFSH UFH ZHKHOLGYA. eUMY LBLBS-FP ZHOLGYS OE RPDDETTSYCHBEFUS, FP RTY CHSHCHIPDE YЪ OEЈ KHUFBOPCHMEO ZHMBZ RETERPMOOYS Y UMEDHEF PVTBEBFSHUS L VPMEE UFBTPC. nsch VKhDEN YURPMSHЪPCHBFSH ZHTNBF LBTFSH RBNSFY ZHKHOLGYY 0xE820. eUMY UBNH YFKH ZHOLGYA CHSHCHBFSH OE RPMHYUMPUSH - RPMKHYUBFSH PVYAIN RBNSFY PVSHYUOSCHNY UTEDUFCHBNY Y UPJDBCHBFSH UCPA UPVUFCHEOOHA LBTFH RBNSPSFY Y LMOPZBENE. rPULPMSHLH PRTEDEMEOYE PVYaЈNB RBNSFY ЪBDББУБ ОХЦОБС ЪДС ЪБРХУЛБ 32-VYFOPZP ЪДС ЪБРБOP4-VNYY PT ЪБРБOP4-VNYYP FSH EЈ CH CHYDE RPDRTPZTBNNNSCH. lBTFKH RBNSFY TBNEUFYN RP BDTEUKH 0x7000. OE DKHNBA, YuFP POB NPTsEF VShchFSH VPMSHYE RBTSH LYMPVBKF. rPUMEDOYK BMENEOF CHTHYUOKHA UDEMBEN FYRB 0 - FBLPZP FYRB OE CHPTBEBEF BIOS Y LFP Y VHDEF RTYOBLPN LPOGB.

; rPMHYUEOYE LBTFSH RBNSFY get_memory_map: mov di, 0x7000 xor ebx, ebx @: mov eax, 0xE820 mov edx, 0x534D4150 mov ecx, 24 mov ebx, 1 in dword, 1 in di x jnz @b @:cmp di, 0x7000 ja .ok mov dword, 0x100000 mov dword, 0 mov dword, 0 mov dword, 1 mov dword, 0 mov ax, 0xE801 int 0x15 jnc @f mov ah, 0x88 int 0x15 , dx @: test cx, cx jz @f mov ax, cx mov bx, dx @: movzx eax, ax movzx ebx, bx mov ecx, 1024 mul ecx push eax mov eax, ebx mov ecx, 65536 pop mul eax, edx mov , eax add di, 24 jmp .ok .ok: xor ax, ax mov cx, 24 / 2 rep stosw ret

OH CHPF Y ZPFPCH OBY OBYUBMSHOSHCHK ЪБЗТХЪУйЛ VHI 32-VYFOSHCHI SDEt. h ЪBLMAYUEOYE RTYCHPTSKH EZP RPMOSHCHK LPD Y NSCH RETEKDEN L SDTH.

; oYUBMSHOSHCHK ЪБЗТХЪУYЛ SDTB DMS BTIIFELFHTSCH x86-format Binär som "bin" org 0x7C00 jmp boot ; ъБЗПМПЧПЛ ListFS align 4 fs_magic dd ? fs_version dd ? fs_flags dd ? fs_base dq ? fs_size dq ? fs_map_base dq ? fs_map_size dq ? fs_first_file dq ? fs_uid dq? fs_block_size dd ? ; ъБЗПМПЧПЛ ЖБКМБ virtuell på 0x800 f_info: f_name rb 256 f_next dq ? f_prev dq ? f_förälder dq ? f_flags dq ? f_data dq ? f_storlek dq ? f_ctime dq ? f_mtime dq ? f_atime dq ? slut virtuellt ; dBOOSHE OBYUBMSHOPZP ЪБЗТХЪУЛБ etikett sektor_per_spår ord vid $$ etikett head_count byte vid $$ + 2 etikett disk_id byte vid $$ + 3 reboot_msg db "Tryck på valfri tangent...",13,10,0 boot_file_name db "boot.bin" ; hCHCHPD UFTPLAY DS:SI OM BLTB write_str: push si mov ah, 0x0E @: lodsb test al, al jz @f int 0x10 jmp @b @: pop si ret ; lTYFYUEULBS PYYVLB fel: pop si call write_str ; RETEЪБЗТХЛБ starta om: mov si, reboot_msg call write_str xor ah, ah int 0x16 jmp 0xFFFF:0 ; ъБЗТХЛБ UELFPTB DX:AX CH VHJET ES:DI load_sector: push dx add axe, word adc dx, word cmp byte, 0xFF je .use_EDD push bx cx si div mov cl, dl inc cl div mov dh, ah mov dl, mov bx, di mov al, 1 mov si, 3 @: mov ah, 2 int 0x13 jnc @f xor ah, ah int 0x13 dec si jnz @b .error: call error db "DISK ERROR",13,10 ,0 @: pop si cx bx dx ret .use_EDD: push si mov byte, 0x10 mov byte, 0 mov word, 1 mov , di push es pop word mov , ax mov , dx mov word, 0 mov word, 0 mov ah , 0x42 mov dl, mov si, 0x600 int 0x13 jc .error pop si dx ret ; rPYUL ZHBKMB U YNEOEN DS:SI CH LBFBMPZE DX:AX find_file: push cx dx di .find: cmp axe, -1 jne @f cmp dx, -1 jne @f .not_found: call error db "NOT FOUND",13, 10.0 @: mov di, f_info call load_sector push di mov cx, 0xFFFF xor al, al repne scasb neg cx dec cx pop di push si repe cmpsb pop si je .found mov ax, word mov dx, word jmp .find . found: pop di dx cx ret ; ъБЗТХЛБ FELHEEZP ZHBKMB CH RBNSFSH RP BDTEUKH BX:0. lPMYUEUFCHP ЪБЗТХЦЕОШИ UELFPTPCH CHPCHTBEBEFUS CH AX load_file_data: push bx cx dx si di mov ax, word mov dx, word .load_list: cmp ax, -1 jne @f cmp dx, -1 jne @f cmp dx: -1 jne . ax, bx pop bx sub axe, bx shr axe, 9 - 4 ret @: mov di, 0x8000 / 16 call load_sector mov si, di mov cx, 512 / 8 - 1 .load_sector: lodsw mov dx, add si, 6 cmp ax, -1 jne @f cmp dx, -1 je .file_end @: push es mov es, bx xor di, di call load_sector add bx, 0x200 / 16 pop es loop .load_sector lodsw mov dx, jmp .load_list ; fPULB CHIPDB CH OBYUBMSHOSHCHK ЪBZTHYUYL boot: ; oBUFTPYN UEZNEOFOSHE TEZYUFTSH jmp 0:@f @: mov ax, cs mov ds, ax mov es, ax ; oBUFTPYN UFEL mov ss, axe mov sp, $$ ; TBTEYIN RTETSCHCHBOYS sti; ъBRPNOYN OPNET ЪБЗТХЪПУОПЗП ДУЛБ mov, dl; prtedemyn RBTBNEFTSH ЪБЗТХЪПУОПЗП ДУЛБ mov ah, 0x41 mov bx, 0x55AA int 0x13 jc @f mov byte, 0xFF jmp .disk_detected @: mov x0ah, in 3 mov x 1 c load_sector. error inc dh mov , dh och cx, 111111b mov , cx .disk_detected: ; ъъззз anj р р р р р р р р р р р р р р о, Word mov dx, Word Call Find_File mov BX, 0x7e00 / 16 call load_data; RETEIPDYN OM RTDDPMCEOYE jmp boot2 ; rKHUFPE RTPUFTBOUFCHP Y UYZOBFKHTB rb 510 - ($ - $$) db 0x55.0xAA ; dPRPMOYFEMSHOSH DBOOSH ЪБЗТХЪУЛБ load_msg_preffix db "Laddar "",0 load_msg_suffix db "". ..",0 ok_msg db "OK",13,10,0 config_file_name db "boot.cfg",0 start16_msg db "Startar 16 bitars kärna...",13,10,0 start32_msg db "Startar 32 bitars kärna. ..",13,10,0 label module_list at 0x6000 label memory_map at 0x7000 ; @b @: mov byte, 0 mov ax, si pop si ret ; push si mov si, load_msg_suffix call write_str pop si push si bp mov dx, word mov axe, word @: push ax call split_file_name mov bp, ax pop ax call find_file testbyte, 1 jz @f mov si, bp mov dx, word MOV AX, Word JMP @b @: Call Load_File_Data Mov Si, Ok_MSG Call Write_Str Pop Bp Si Ret; RPMHYUEE LBTPHSH RBNSFY Get_Memory_MAP: MOV DI, Memory_map XOR EBX, EBX @: MO: MO: MO: MO: MO: MO: MO: MO: V EAX, 0xe820 MOV EDX, 0x534D4150, 0x534D4150, 0x534D4150 dword, 1 int 0x15 jc @f add di, 24 test ebx, ebx jnz @b @: cmp di, 0x7000 ja .ok mov dword, 0x100000 mov dword, 0 mov dword, 0 mov dword, 0 mov dword, 0 mov dword , 0xE801 int 0x15 jnc @f mov ah, 0x88 int 0x15 jc .ok mov cx, ax xor dx, dx @: test cx, cx jz @f mov ax, cx mov bx, dx @: movzx eax, ax mov,zx bx mov ecx, 1024 mul ecx push eax mov eax, ebx mov ecx, 65536 mul ecx pop edx add eax, edx mov , eax add di, 24 jmp .ok .ok: xor ax, ax mov cx, 2 rep stow ret ; rTPDPMTSEOYE OBUBMSHOPZP ЪБЗТХЪУЛБ boot2: ; ъБЗТХЪН ЛПОжІЗХТБГИПООСЧК ЖБКМ ЪБЗТХЪУЛБ mov si, config_file_name mov bx, 0x1000 / 16 call; CHSPMOYN ЪБЗТХПУОШК ULTYRF mov bx, 0x9000 / 16 mov bp, module_list mov dx, 0x1000 .parse_line: mov si, dx .parse_char: lodsb test al, al jz .config jemand cmp jemand 1, al_0 .run_command jmp .parse_char .run_command: mov byte, 0 xchg dx, si cmp byte, 0 je .parse_line ; rHUFBS UFTPLB cmp byte, "#" är .parse_line ; lPNNEOFBTYK cmp byte, "L" är .load_file ; ъБЗТХЛБ ЖБКМБ cmp byte, "S" är .start ; ъBRHUL SDTB; oEYCHEUFOBS LPNBODB mov al, mov [.cmd], al call error db "Okänt startskriptkommando "" .cmd db ? db ""!",13,10,0 .config_end: ; rTY RTBCHYMSHOPN LPOZHYZHTBGYPOOPN ZHBKME NSCH OE DPMTSOSCH UADB RPRBUFSH; jmp omstart; ъБЗТХЛБ ЖБКМБ.load_file: push dx inc si call load_file push ax mov cx, 512 mul cx mov ord, ax mov ord, dx mov ord, 0 mov ord, 0 mov ax, bx mov cx, 16 mov ord, dx mov ord, 0 mov ord, 0 pop ax shr ax, 9 - 4 lägg till bx, ax lägg till bp, 16 pop dx jmp .parse_line ; ъBRHUL SDTB.start: ; rTPCHETYN, YuFP ЪBZTHTSEO IPFS VSC PDIO ZhBKM cmp bx, 0x9000 / 16 ja @f call error db "NO KERNEL LOADED",13,10,0 @: ; ъBRPMOSEN RPUMEDOYK BMENEOF URYULB ZHBKMPCH xor yxa, ax mov cx, 16 mov di, bp rep stosw ; RETEIPDYN L RTPGEDHTE YOYGYBMYBGYY SDTB DMS OKHTSOPK TBTSDOPUFY inc si cmp word, "16" je .start16 cmp word, "32" je .start32 ;cmp word, "64" ;je .start64 ; oEYCHEUFOBS TSTSDOPUFSH SDTB-anropsfel db "Ogiltigt startkommando-argument",13,10,0 ; ъBRHUL 16-TBTSDOPZP SDTB.start16: mov si, start16_msg mov bx, module_list mov dl, jmp 0x9000 ; ъBRHUL 32-TBTSDOPZP SDTB.start32: ; CHCHCHPDYN HCHEDPNMEOYE P ЪBRHULE 32-VYFOPZP SDTB mov si, start32_msg call write_str ; rTPCHETYN, YuFP RTPGEUUPT OE IHTSE i386 mov ax, 0x7202 push ax popf pushf pop bx cmp axe, bx je @f call error db "Required i386 or better",13,10,0 @: ; rPMKHYUN LBTFH RBNSFY anrop get_memory_map ; pYYUFYN FBVMYGSH UFTBOYG xor yxa, ax mov cx, 3 * 4096 / 2 mov di, 0x1000 rep stosw ; ъBRPMOIN LBFBMPZ UFTBOIG mov-ord, 0x2000 + 111b mov-ord, 0x3000 + 111b ; ъBRPMOIN RETCHHA FBVMYGH UFTBOYG mov eax, 11b mov cx, 0x100000 / 4096 mov di, 0x2000 @: stosd add eax, 0x1000 loop @b ; ъBRPMOYN RPUMEDOAA FBVMYGH UFTBOYG mov di, 0x3000 mov eax, dword eller eax, 11b mov ecx, dword shr ecx, 12 @: stosd add eax, 0x1000 loop @b mov 00; Kärnstack mov-ord, 0x3000 + 11b ; Kernel sidtabell ; ъБЗТХЪН ЪБУЕОЕЧ CR3 mov eax, 0x1000 mov cr3, eax ; ъБЗТХХЪН ЪБУУЭОЭ Ч GDTR lgdt ; ъBRTEFYN RTETSCHCHBOYS cli; RETEKDEN CH ЪBEEYEЈOOOSCHK TETSYN mov eax, cr0 eller eax, 0x80000001 mov cr0, eax ; RETEKDEN OM 32-VYFOSHCHK LPD jmp 8:start32; fBVMYGB DEULTYRFPTPCH UEZNEOFPPCH DMS 32-VYFOPZP SDTB align 16 gdt32: dq 0 ; NULL - 0 dq 0x00CF9A000000FFFF ; KOD - 8 dq 0x00CF92000000FFFF ; DATA - 16 gdtr32: dw $ - gdt32 - 1 dd gdt32 ; 32-VYFOSHCHK LPD use32 start32: ; oBUFTPYN UEZNEOFOSH TEZYUFTSHY UFEL mov eax, 16 mov ds, ax mov es, ax mov fs, ax mov gs, ax mov ss, ax mov esp, 0xFFFFDFFC ; rPNEUFYN CH DL OPNET ЪБЗТХЪПУОПЗП ДУЛБ mov dl, ; rPNEUFYN CH EBX BDTEU URYULB ЪБЗТХЦеOOШ itsХБКМПЧ mov ebx, module_list ; rPNEUFYN CH ESI BDTEU LBTFSH RBNSFY mov esi, memory_map ; RETEIPDYN OM SDTP jmp 0xFFC00000

RETCHPE SDTP

sDTP RPLB KH OBU VKhDEF UPUFPSFSH YJ DCHHI ZHBKMPCH - startup.asm Y main.c. startup.asm OHTSEO DMS FPZP, YuFPVSH VShchFSH KHCHETEOOSCHNY, YuFP KHRTBCHMEOYE RPRBDEF OM ZHKHOLGYA kernel_main. CHEDSH POB NPTSEF VSHCHFSH OE CH OBYUBME ZHBKMB, B UPDETSYNPE startup.o NSCH RPMOPUFSHA LPOFTPMYTHEN Y EUMY HLBTSEN EZP RETCHSHCHN MYOLETKH, FP VHDEN HRTBCHMSFSH Y RETCHSHSHBOPPZCHNY DCHPYBUPMYTHEN

Format Elf Public _Start Extrn Kernel_Main Section ".text" Exekverbar _Start: Movzx edx, DL Push edx Push ESI Push EBX LGDT Ring Kernel_Main @:; CLI; HLT JMP @B -sektion ".Data" Skrivbar GDT: DQ 0 DQ 0x00CF9A000000000000000000000000 0FFFF gdtr: dw $ - gdt dd gdt

OH CHPF Y RPUMEDOYK OM LPD OM YUYUFPN Assembler :-). AV CHSHCHRPMOSEF RTPUFEKYHA ЪBDББУХ - ХМПЦИФШ Ш УФЭЛ ФТИ БТЗХНЭОФБ DMS ZHKHOLGYY kernel_main H RETEEDFSHEM. rPUME CHPCHTBFB YЪ OEЈ SDTP KHIPDYF CH VEULPOYUOSCHK GYLM. rP UPZMBYEOYA CHSHCHJPCHB ZHKHOLGYK ui RBTBNEFTSCH UMEDHEF RYIBFSH CH UFEL CH PVTBBPN RPTSDLE. fBLCE LFPF LPD YOYGYBMYBGYY ЪBZTHTSBEF OPCHPE OBYUEOYE CH GDTR - FERTSH FBVMYGB DEULTYRFPTPCH UEZNEOFPCH OBIPDIFUS CH RTPUFTBOUFCHE SDTB Y DBCE EUMY NSCH PFN POFCHKPYTHEN O Jag PYYVPL.

b FERTSH UBNPE CHLHUOPE - RTPUFEKEEE SDTP OM INSTÄLLNINGEN CHCHUPLPZP HTPCHOS:

Typedef struct ( osignerad lång lång bas; osignerad lång lång storlek; ) BootModuleInfo; void kernel_main(char boot_disk_id, void *memory_map, BootModuleInfo *boot_module_list) ( char *screen_buffer = (void*)0xB8000; char *msg = "Hej värld!"; osignerad int i = 24 * 80; while (*msg) ( screen_buffer = *msg; msg++; i++; ) )

bFP SDTP OE DEMBEF OYUEZP PUPVEOOOPZP - RTPUFP CHCHCHPDYF UFTPLH "Hej världen!" OM RPUMEDOAA UFTPYULH FELUFPCHPZP LTBOB. uFTHHLFKHTB PRYUBOOBS CH OBYUBME VKhDEF OHTSOB DMS DPUFKHRB L URYULKH ЪBZTHTSEOOSCHI NPDHMEK.

chBTsOP RPNOYFSH, YuFP OILBLPK UFBODBTFOPK VYVMYPFELY KH OBU OEF - OBN DPUFHROSCH FPMSHLP FE ZHKHOLGYY, LPFPTSHCHE NSCH UDEMBEN UBNY. CHUE printf, strcpy, memcpy Y F. R. RTYDFUS TEBMYЪPCHCHCHBFSH UBNPUFPSFEMSHOP, OE RSCHFBKFEUSH PVTBFYFSHUS LOYN. h UMEDHAEEN CHSHCHRKHULE NSCH ЪБКНЈНУС UPЪDBOYEN OBEZP UPVUFCHEOOPZP TsKhFLP KhTEЪBOOPZP BOBMMPZB libc, YUFPVSH RTPZTBNNNYTPCHBFSH VEESHMP KhDP. FHF OBUYOBEFUS UBNBS YOFETEUOBS YUBUFSH, B RTYOSFSHCHE TEYEOYS PE NOPZPN RPCHMYSAF OM CHUA UFTHLFHTH UYUFENSCH.

uVPTLB SDTB

yURPMOSENSCH ZHBKMSCH UPVYTBAFUS CH DCHB LFBRB - LPNRYMSGYS, B RPFPN MYOLPCHLB. OM RETCHPN LFBR LPNRYMSFPT RTEPVTBKHEF YUIPDOSCHK LPD CH LPNBODSCH RTPGEUUPTB Y UPITBOSEF CHUY LFP CH PVYAELFOSHCHK ZHBKM. LBTSDSCHK NPDHMSH UYUFENSCH UPITBOSEFUS CH PFDEMSHOPN ZHBKME. h LFPN ZHBKME FBL CE UPDETSYFUS YOZHPTNBGYS P ZHKHOLGYSI, PRYUBOOSCHI CH NPDHMY, RPFPNH YЪ PDOPZP ZHBKMB NPTsOP UCHPVPDOP CHSCCHCHBFSH ZHKHOLGYA YЪ DTHZPZP. CHEUSH LPD CH PVAELFOSCHI ZHBKMBI OE RTYCHSBO L LPOLTEFOSHCHN BDTEUBN. OM CHFPTPN LFBR MYOLET UPVYTBEF CHUE PVAELFOSCH ZHBKMSCH CH PDYO VYOBTOSHCHK. rTY LFPN LPD RTYCHSCHCHBEFUS L LPOLTEFOSHN BDTEUBN (EUMY, LPOYUOP, NSCH OE UPVYTBEN DIOBNYUEULY ЪBZTHTSBENKHA VYVMYPFELH), CHNEUFP UUSCHMPL OM ZHKHOLGSOYMSAFTEUSOLHTUBCH. OBN OHTsOP RPMKHYUFSH OM CHSHCHIPDE PUPVSHCHK DCHPYUOSCHK ZhBKM. ьFP RTPUFP LPD Y DBOOSCH, VEЪ LBLYI-MYVP ЪБЗПМЧЛПЧ (ФП EUФШ ЛФП OE PE І OE ELF). h LBUEUFCHE VBPCHPZP BDTEUB YURPMSH'HEFUS BDTEU 0xFFC00000. DMS HRTPEEOYS LFPPZP NSCH PRYYEN CHUЈ, YuFP OBN OHTSOP CH UREGYBMSHOPN ZHTNBFE ULTYRFB ld:

OUTPUT_FORMAT("binary") ENTRY(_start) SECTIONS ( .text 0xFFC00000: ( *(.text) *(.code) *(.rodata*) ) .data ALIGN(0x1000) : ( *(.data) ) .bss ALIGN(0x1000) : ( *(.bss) ) .empty ALIGN(0x1000) - 1: ( BYTE(0) ) )

ьFPF ULTYRF ZPCHPTYF, YuFP OBY ZHBKM VKhDEF METSBFSH CH RBNSFY OERTETCHCHOSCHN VMPLPN OBUYOBS U BDTEUB 0xFFC00000. h UBNPN OBYUBME VKhDEF YDFY UELGYS LPDB, RPFPN UELGYS skrivskyddad DBOOSCHI, ЪBFEN PVSHYUOSHI DBOOSCHI, RPFPN OEYOYYBMYYYTPCHBOOSCHI. CHUE UELGYY CHSHTPCHOOESCH OM TBNET UFTBOYGSCH 4 levs (CHDTHZ NSCH RPFPN ЪBIPFYN ЪBEIFYFSH OM HTPCHOE FBVMYGSHCH UFTBOYG LPD PF ЪBRYUY). rPUMEDOEE PRYUBOIE UELGYY.empty OEPVIPDYNP DMS FPZP, YUFPVSH DBTSE OEYOYYYBYMYYPTPCHBOOSCH RETENEOOOSCH ЪBOINBMY NEUFP CH ZHBKME (FBN VHDHF OHMY). CHEDSH OBYUBMSHOSHCHK ЪBZTHYUYL CHSHCHDEMSEF RBNSFSH DMS SDTB THLPCHPDUFCHHSUSH TBNETPN ZHBKMB.

uPVTBFSH CHUY SDTP NPTsOP UMEDHAEYNY LPNBODBNY:

Fasm startup.asm startup.o gcc -c -m32 -ffristående -o main.o main.c ld --oformat=binär -melf_i386 -T script.ld -o kernel.bin startup.o main.o

rBTBNEFT GCC -ffristående KHLBSCCHBEF ENKH PFLMAYUYFSH CHUE UFBODBTFOSCH VYVMYPFELY. CHEDSH SING RTYCHSBOSCH L LPOLTEFOPK PRETBGYPOOPK UYUFENE, B NSCH RYYEN OPCHHA.

uVPTLB PVTBB DYULB

pVPKDHUSH VE MYYOYI LPNNEOFBTYECH Y RTPUFP RTYCHEDH MYOHLUPCHSHCHK ULTYRF UVPTLY PVTBBB:

Dd if=bin/boot.bios.bin of=bin/boot_sector.bin bs=512 count=1 dd if=bin/boot.bios.bin of=disk/boot.bin bs=1 skip=512 cp bin/kernel .bin disk/kernel.bin bin/make_listfs of=disk.img bs=512 storlek=2880 boot=bin/boot_sector.bin src=./disk

enligt RTEDRPMBZBEF, YuFP CHUE ULPNRYMYTPCHBOOSCH ZHBKMSCH METSBF Ch bin Ch FELKHEEN LBFBMPZE, B EEЈ YNEEFUS LBFBMPZ disk, Ch LPFPTPN METSYF boot.cfg UMEDHAEEZP UPDETSBOYS:

# Laddar kärnan Lkernel.bin # Starta 32-bitars kärna S32

eUMY CHU CHUY UDEMBMY RTBCHYMSHOP, RPMKHYUEOOOSCHK PVTB NPTsOP ЪBRKHUFYFSH CH BNHMSFPTE YMY DBTSE OM TEBMSHOPN TSEMEY CHSHCH RPMKHYUFE RPDPVOKHA LBTFYOKH:

ъБЗТХЪУйЛ UYFSHCHCHBEF LPOZHYZKHTTBGYPOOSCHK ZHBKM, ЪБЗТХЦБЭФ SDTP, RETEYPDYF CH ЪBEEYEEЈOOOSCHK TETSYN Y RETEDHBYHBECH. rPMHYUYCH EZP, OBIYE SDTP CHSHCHCHPDYF RPUMEDOAA UFTPLH OM BLTBO. bFP MYYSH OBYUBMP DPMZPZP RKhFY, NSCH RETEIPDYN L UBNPK YOFETEUOPK YUBUFY TBTBVPFLY. FERTSH CHSHCHRKHULY VKHDHF ZPTBJDP VPMEE RTPUFSHCHN DMS CHPURTYSFYS, VMBZPDBTS YURPMSH'PCHBOYA SJSHLB CHSHCHUPLPZP HTPCHOS, LPFPTSCHK LBL S OBDEAUSH CHUE Y FBL ЪOBAF. eUMY CHCH OE IPFYFE TBVYTBFSHUS U Assembler, NPTSEFE RTPUFP CHOSFSH NPK ZPFPCHSHCHK UBZTHYUYL J startup.asm Y YYNEOSFSH HCE FPMSHLP UPDETSYNPE main.c, RPULPMSHLH CHEUSHПФПППППП D P . Ф ЦЈУФЛП ЛБЛИЕ-МИВП РБТББНИФТШ ШДТБ (ЛППНЭ ЖУ У ЛППТБН НППНЭ ЖУ У ЛППТБЧН ПЪЧПМСЭФ RPUFTPIFSH OM UCHPEK VBJE YFP KhZPDOP.

bChFPNBFYBGYS UVPTLY YMY Makefile

ChSCH NPZMY ЪBNEFYFSH, YuFP CHTHYUOKHA OBVYCHBFSH UFPMSHLP LPNBOD DPUFBFPYUOP KhFPNYFEMSHOP. l FPNH CE OE CHUEZDB EUFSH OEPVIPDYNPUFSH RETELPNRYMYTCHBFSH CHUE ZhBKMSCH. OBRTYNET, EUMY startup.asm OE VSHM YЪNEOЈO, NPTsOP OE CHSHCHCHBFSH fasm. UREGYBMSHOP DMS HRTPEEOYS LPNRYMSGY RTYMPTSEOYK VSHMB RTYDKHNBOB HFYMYFB fabrikat, LPFPTBS CHIPDIF CH UFBODSBTFOHA RPUFBCHLH GCC Y MinGW.

mAVPK Makefile UFPYF YЪ OBVPTB RTBCHYM U FBLPK UFTHLFKhTPK:

YNSGEMYYMYZHBKMB: yNSRETCHPZPyUIIPDOPZPzhBKMB yNSchFPTPZPyUIIDDOPZPzhBKMB...lPNBODSCHLPNRYMSGYY

RETCHPE RTBCHYMP, LPFPTPE DPMTSOP VShchFSH CH MAVPN Makefile - GEMS alla. göra UNPFTYF OM ЪBCHYUYNPUFY GEMY all Y LPNRYMYTHEF YI, B ЪBFEN CHSHPRMOSEF LPNBODSCH Y LFK GEMY. dMS LBTSDPK DTHZPK GEMY UOBYUBMB UPVYTBAFUS EЈ ЪBCHYUYNPUFY. rTY LFPN YNS GEMY Y YNS ЪBCHYUINPUFEK NPZHF UPCHRBDBFSH U YNEOBNY TEBMSHOSCHI ZHBKMPCH. h FBLPN UMKHYUBE RETEUVPTLB GEMY RTPYЪPKDF FPMSHLP EUMY YUIPDOYLY VSHMY YЪNEOOSHCH.

eEЈ PDOB GEMSH, LPFPTBS YBUFP YURPMSHЪHEFUS CH Makefile - ren. eЈ ЪБДББУБ ХДБМІФШ CHUE VYOBTOSH ZHBKMSCH, YUFPVSH OBYUBFSH UVPTLH "U YUYUFPZP MYUFB". chPF FBL NPTSEF CHSHCHZMSDEFSH Makefil VHI SDTB:

Alla: startup.o main.o script.ld ld --oformat=binär -melf_i386 -T script.ld -o kernel.bin startup.o main.o startup.o: startup.i386.asm fasm startup.i386.asm startup.o main.o: main.c gcc -c -m32 -ffristående -o main.o main.c ren: rm -v *.o kernel.bin

bFPF FELUF OEPVIPDYNP UPITBOIFSH CH ZHBKM U YNEOEN Makefile (VEY TBUYYTEOYS) CH LBFBMPZ U YUIPDOSHNY FELUFBNY SDTB. FERTSH DPUFBFPYUOP CHSHPRMOYFSH LPNBODH gör VEЪ RBTBNEFTPC, OBIPDSUSH CH LFPN LBFBMPZE Y NSCH RPMKHYUN ZHBKM kernel.bin (MYVP UPPVEEOYS PV PYYVLOE, EUMY YUFP-FP RPYMP ).

b CHPF FBL S UPVYTBA ЪBZTHYUYL:

Alla: boot.bios.bin boot.bios.bin: boot.bios.asm fasm boot.bios.asm boot.bios.bin ren: rm -v boot.bios.bin

Th make_listfs:

Alla: kompilera kompilera: make_listfs.c gcc -o make_listfs make_listfs.c ren: rm -f make_listfs make_listfs.exe

OH Y OBLPOEG TBUULBTSH RTP CHSHCHJPCH DTHZYI Makefil YЪ PDOPZP. med DPUFBFPYUOP MEOYCH, YuFPVSH DBTSE ЪBIPDYFSH CH LBFBMPZY U LBTSDSCHN LPNRPEOFPN UYUFENSH, RPFPNH UPЪDBM 1 Makefile, LPFPTSCHK UPVYTBEF UTBH CHUA UYUPENKH. x NEOS EUFSH RBRLB src, CH OEK RPDLBFBMPZY: boot, kernel, make_listfs. h UBNPC src OBIPDFUS CHPF FBLPC Makefil:

Alla: make -C boot/ make -C kernel/ make -C make_listfs/ clean: make -C boot/ clean make -C kernel/ clean make -C make_listfs clean

FERETSH, OBIPDSUSH LBFBMPZE src S RTPUFP RYYKH make Y RPMHYUBA RPMOPUFSHHA UPVTBOOHA UYUFENKH, B EUMY OBRYUBFSH make clean, FP CHUE DCHPYYUOSCH ZHBKMSCH VHDHF KHDPMSHEOSH YDOFBMSHEOSH YDOFBMSHEOSH YDOPH.

OH Y CH DPCHETYEOYE RPUMEDOYK ULTYRF, LPFPTSCHK CHSHHRPMOSEF RPMOHA LPNRYMSGYA Y UVPTLH CHUEI LPNRPEOFPCH Y PVTBB DYULB. h PDOPN LBFBMPZE U OIN OBDP TBBNEUFYFSH src, RHUFPK LBFBMPZ bin Y LBFBMPZ disk U ZHBKMPN boot.cfg.

#!/bin/sh make -C src cp src/boot/boot.bios.bin bin/ cp src/kernel/kernel.bin bin/ cp src/make_listfs/make_listfs bin/ dd if=bin/boot.bios.bin of=bin/boot_sector.bin bs=512 count=1 dd if=bin/boot.bios.bin of=disk/boot.bin bs=1 skip=512 cp bin/kernel.bin disk/kernel.bin bin/make_listfs of=disk.img bs=512 storlek=2880 boot=bin/boot_sector.bin src=./disk read -p "Tryck på Retur för att fortsätta..." dummy

u FBLYN OBVPTPN ULTYRFPCH UVPTLB UYUFENB UFBOPCHYFUS RTEDEMSHOP RTPUFPK, PUPVEOOOP EUMY HYUEUFSH, YuFP RPUMEDOYK ULTYRF NPTsOP ЪBRHULBFSH DCHPKOSHCHN LMYBLPKEDJ NPHBLPCENO Y TBMYUOSHE LPNBODSCH CHTPDE dd, cp, rm OE UHEEUFCHHAF RPD Windows, RPFPNH EЈ RPMSHЪPCHBFEMSN RTYZPDYFUS RBBLEF MSYS YMY Cygwin. pDOBLP RTPUFBS UVPTLB CHUEI LPNRPEOFPCH VHDEF TBVPFBFSH DBCE EUMY X CHBU EUFSH FPMSHLP GCC Y fasm (make_listfs MEZLP ULPNRYMYTHEFUS Y ЪBRHUFYFUS CHYDE Windows-RTYMPCEOY C).

rTYNEYUBOYE DMS RPMSHЪPCHBFEMEK pu Windows

ld DMS Windows OE UPCHUEN RPMOPGEOOSHCHK - PO OE RPDDETSYCHBEF CHCHCHPD UTBH CH VYOBTOSHCHK ZHBKM, FPMSHLP CH EXE. YURTBCHYFSH LFP NPTsOP UPЪDBCH UOBYUBMB EXE (ld OE PVTBFYF CHOYNBOYE, YuFP VBPCHSHCHE BDTEUB UELGYK OECHPNPTSOSCHE DMS CHEODPCHSHHI VYOBTOILPC), B RPFPN ChShchFCHEYFUSH PSHPN ChShchFCHEYFUSH PSHPNCH PNPTSOSCHE DMS eUMY CHSH UFPMLOЈFEUSH U FEN, YuFP ld PFLBSCHCHBEFUS UPЪDBCHBFSH ZhBKM kernel.bin, CHPURPMSHKFEUSH CHPF FBLYN Makefil VHI SDTB:

Alla: startup.o main.o script.ld ld -melf_i386 -T script.ld -o kernel.bin startup.o main.o objcopy kernel.bin -O binär startup.o: startup.i386.asm fasm startup.i386 .asm startup.o main.o: main.c gcc -c -m32 -ffristående -o main.o main.c clean: rm -v *.o kernel.bin

ъБПДОП ХВЭТИФЭ УФТПЛХ OUTPUT_FORMAT("binär") Ъ script.ld. FERETSH Y RPD Windows RPMKHYUFUS UPVTBFSH SDTP UYUFENSCH.

ъБЗТХЪЛБ УИУФЭНШЧ OM TEBMSHOPK NBYYOE

rPUME FBLYI KHUREIPCH X OELPFPTSCHI NPTSEF CHP'OILOHFSH TSEMBOE PRTPVPCHBFSH OPCHHA pu OM TEBMSHOPN TSEMEYE. lFP OE RTEDUFBCHMSEF RTPVMEN. u RPNPESHA HxD CH Fönster PFLTPKFE DYULEFH YMY ZHMEYLH, CHSHVTBCH CHBTYBOF "pFLTSCHFSH DYUL". rTY PFLTSCHFY ZHMEYLY CHBTsOP PFLTSCHFSH YNEOOOP UBNH ZHMEYLKH, BOE ET TB'DEM. h DTHZPK CHLMBDLE PFLTPKFE disk.img, CHSHCHDEMYFE EZP UPDETSINPE RPMOPUFSHA Y ULPRYTHKFE OB DYUL U EZP UBNPZP OBYUBMB. rPUME LFPPZP NPTsOP OBTSBFSH "uPITBOIFSH" Y DPTsDBFSHUS PLPOYUBOYS ЪBRYUY. CHUE DBOOSCH OM ZHMEYL YMY DYULEF RTY LFPN VHDHF HOYUFPTSEOSHCH, B DMS FPZP, YUFPVSH EЈ YURPMSHЪPCHBFSH UOPCHB RP OBYUEOYA, EЈ RTYDFUS ЪБОПZBHTFПPF SHPHPFY!

rPMSHЪPCHBFEMY Linux NPZHF RPUFKHRYFSH RTPEE - CHSHRPMOYFSH UREGYBMSHOHA LPNBODH CH FETNYOBME. DMS DYULEFSH:

Dd if=disk.img av=/dev/fd0

DMS ZHMEYLY:

Dd if=disk.img av=/dev/sdX

chNEUFP sdX OBDP RPDUFBCHYFSH OBUFPSEEE YNS KHUFTPKUFCHB (sda, sdb, sdc, sdd Y F. D.). ZMBCHOPE RTY LFPN OE RETERKHFBFSH Y OE ЪBRYUBFSH PVTB OM UYUFENOSHCHK DYUL, HOYUFPTSYCH CHUE DBOOSCH. TBHNEEFUS, PVE LPNBODSCH DPMTSOSCHCHSHCHRPMOSFSHUS PF YNEOY rot YMY Y RPNPESH sudo.

rPUME LFPPZP OBDP OBUFTPIFSH CH BIOS ЪБЗТХЛХ У ДУЛЭФШЧ ІМИ ЖМИЛХ (UFBTSHCHE BIOS OE RPDDETSYCHBAF ZHMEYDPNFSHUS CHMBYDDPNFSHUS CHEMBYDDBNFSHUS CHEMBYTSJWORLD).

ъBLMAYUEOOYE

OH CHPF UPVUFCHOOOP Y CHUЈ OM UEZPDOS. nsch OBLPOEG-FP ЪBLPOYUMY RTPZTBNNNYTPCHBOYE OB Assembler (IPFS CH u CHU TBCHOP RTYDFUS YOPZDB DEMBFSH BUUENVMETOSCHE CHUFBCHLY DMS TBVPFSHCH U PVPPTKhDPCHBOYEN) Y RETEYSCHPCH. eEЈ PYUEOSH NOPZP RTEDUFPYF UDEMBFSH. NSC NPTSEFE HCE RTPCHPDYFSH TBMYUOSCH LURETYNEOFSH, YЪNEOSS NPK main.c, FPMSHLP KHYUFYFE, YuFP MAVBS PYYVLB (DPUFHR L OEURTPEGYTPCHBOOPC RBNSFY, DEMEOYE RTYCHE RTYCHE REMSHY) ЪBCHYUBOYA UYUFENSCH (NSCH RPLB OE PVTBVBFSCHBEN YULMAYUEOYS, RPFPNH RTPGEUUPT OE NPTSEF RTDPDPMTSYFSH TBVPFH RPUME PYYVLY). dP CHUFTEYUY!

MAvsche CHPRPTUSCH CHSC NPTSEFE ЪBDBFSH OM NPK BDTEU: [e-postskyddad]. y DB, UEKYUBU UBNPE CHTENS DMS TBMYUOSHI YDEK RP LPOGERGYY PU Y RTEDMPTSEOYK.

Original: AsmSchool: Gör ett operativsystem
Författare: Mike Saunders
Publiceringsdatum: 15 april 2016
Översättning: A. Panin
Översättningsdatum: 16 april 2016

Del 4: Med de färdigheter du har fått genom att läsa de tidigare artiklarna i den här serien kan du börja utveckla ditt eget operativsystem!

Vad är det för?

  • För att förstå hur kompilatorer fungerar.
  • För att förstå CPU-instruktioner.
  • För att optimera din kod för prestanda.

Under flera månader gick vi igenom en svår väg som började med utvecklingen enkla program i assemblerspråk för Linux och avslutades i den sista artikeln i serien med utvecklingen av fristående kod som körs på en persondator utan operativsystem. Nåväl, nu ska vi försöka samla all information tillsammans och skapa ett riktigt operativsystem. Ja, vi kommer att följa i Linus Torvalds fotspår, men först måste vi svara på följande frågor: "Vad är ett operativsystem? Vilka av dess funktioner måste vi återskapa?"

I den här artikeln kommer vi bara att fokusera på operativsystemets grundläggande funktioner: ladda och köra program. Komplexa operativsystem utför många fler funktioner, som hantering och bearbetning av virtuellt minne nätverkspaket, men deras korrekta implementering kräver år av kontinuerligt arbete, så i den här artikeln kommer vi bara att överväga de grundläggande funktionerna som finns i alla operativsystem. Förra månaden utvecklade vi ett litet program som passade in i 512-byte-sektorn på en diskett (dess första sektor), och nu kommer vi att modifiera det lite för att lägga till funktionen att ladda ytterligare data från disken.

Utveckling av startladdare

Vi skulle kunna försöka minska storleken på vårt operativsystems binära kod så mycket som möjligt för att passa in i den första 512-byte sektorn på disketten, den som laddas av BIOS, men i det här fallet skulle vi inte kunna att implementera några intressanta funktioner. Därför kommer vi att använda dessa 512 byte för att hysa den binära koden för en enkel systemstarthanterare, som kommer att ladda OS-kärnans binära kod till RAM och exekvera den. (Efter detta kommer vi att utveckla själva OS-kärnan, som kommer att ladda den binära koden för andra program från disken och även köra den, men vi kommer att prata om detta lite senare.)

Du kan ladda ner källkoden för exemplen som diskuteras i den här artikeln på www.linuxvoice.com/code/lv015/asmschool.zip. Och det här är koden för vår systemstarthanterare från en fil som heter boot.asm:

BITS 16 jmp kort start ; Hoppa till etikett, hoppa över skivbeskrivning nop ; Tillägg före diskbeskrivningen %inkludera "bpb.asm" start: mov ax, 07C0h ; Ladda adress mov ds, axe ; Datasegment mov ax, 9000h ; Stackförberedelse mov ss, yxa mov sp, 0FFFFh ; Högen växer ner! cld ; Ställa in riktningsflaggan mov si, kern_filename call load_file jmp 2000h:0000h ; Övergång till OS-kärnan binär kod laddad från filen kern_filename db "MYKERNELBIN" %include "disk.asm" gånger 510-($-$$) db 0 ; Utfyllning av den binära koden med nollor upp till 510 byte dw 0AA55h ; Boot loader binär slutmarkeringsbuffert: ; Start av buffert för diskinnehåll

I den här koden är den första CPU-instruktionen jmp-instruktionen, som finns efter BITS-direktivet, som talar om för NASM-montören att 16-bitarsläge används. Som du säkert minns från den tidigare artikeln i serien börjar exekveringen av den 512-byte binära koden som laddas från BIOS från disken från början, men vi måste hoppa till en etikett för att hoppa över en speciell uppsättning data. Uppenbarligen skrev vi förra månaden helt enkelt koden till början av disken (med hjälp av verktyget dd) och lämnade resten av diskutrymmet tomt.

Nu måste vi använda en diskett med ett lämpligt MS-DOS-filsystem (FAT12), och för att fungera korrekt med detta filsystem måste vi lägga till en uppsättning specialdata nära början av sektorn. Denna uppsättning kallas ett BIOS Parameter Block (BPB) och innehåller data som disketikett, antal sektorer och så vidare. Det borde inte intressera oss i detta skede, eftersom mer än en serie artiklar skulle kunna ägnas åt sådana ämnen, varför vi har placerat alla instruktioner och data som är associerade med den i en separat källkodsfil som heter bpb.asm.

Baserat på ovanstående är detta direktiv från vår kod extremt viktigt:

%inkludera "bpb.asm"

Detta är ett NASM-direktiv som tillåter att innehållet i en specificerad källfil inkluderas i den aktuella källfilen under montering. På så sätt kan vi göra vår bootloader-kod så kort och begriplig som möjligt genom att placera alla detaljer om implementeringen av BIOS-parameterblocket i en separat fil. BIOS-parameterblocket måste placeras tre byte efter starten av sektorn, och eftersom jmp-instruktionen bara tar upp två byte, måste vi använda nop-instruktionen (dess namn står för "no operation" - det här är en instruktion som gör inget annat än slösa CPU-cykler ) för att fylla den återstående byten.

Jobbar med stacken

Därefter måste vi använda instruktioner som liknar de som diskuterades i den förra artikeln för att förbereda register och stacken, såväl som cld-instruktionen (står för "clear direction"), som låter oss ställa in riktningsflaggan för vissa instruktioner, t.ex. som lodsb-instruktionen, som, när den exekveras, kommer att öka värdet i SI-registret snarare än att minska det.

Efter det lägger vi in ​​adressen till strängen i SI-registret och anropar vår load_file-funktion. Men tänk på det en minut – vi har inte utvecklat den här funktionen än! Ja, det är sant, men dess implementering kan hittas i en annan källkodsfil som vi inkluderade kallad disk.asm.

FAT12-filsystemet som används på disketter som är formaterade i MS-DOS är ett av de enklaste tillgängliga filsystem, men att arbeta med dess innehåll kräver också en ansenlig mängd kod. Subrutinen load_file är cirka 200 rader lång och kommer inte att visas i den här artikeln, eftersom vi överväger processen att utveckla ett operativsystem, inte en drivrutin för ett specifikt filsystem, därför är det inte särskilt klokt att slösa utrymme på logga sidor på detta sätt. I allmänhet inkluderade vi disk.asm-källkodsfilen nästan innan slutet av den aktuella källkodsfilen och kan glömma det. (Om du fortfarande är intresserad av strukturen för FAT12-filsystemet kan du läsa den utmärkta översikten på http://tinyurl.com/fat12spec och sedan titta på disk.asm-källkodsfilen - koden som finns i den är bra kommenterat.)

I båda fallen laddar load_file-rutinen den binära koden från filen som är namngiven i SI-registret till segment 2000 vid offset 0, varefter vi hoppar till början av den för exekvering. Och det är allt - operativsystemets kärna är laddad och systemstarthanteraren har slutfört sin uppgift!

Du kanske har märkt att vår kod använder MYKERNELBIN istället för MYKERNEL.BIN som operativsystemets kärnfilnamn, vilket passar bra in i namnschemat 8+3 som används på disketter i DOS. Faktum är att FAT12-filsystemet använder en intern representation av filnamn, och vi sparar utrymme genom att använda ett filnamn som garanterat inte kräver vår load_file-rutin för att implementera en mekanism för att slå upp punkttecknet och konvertera filnamnet till intern representation av filsystemet.

Efter raden med direktivet för anslutning av källkodsfilen disk.asm, finns det två rader utformade för att fylla ut den binära koden för systemstarthanteraren med nollor upp till 512 byte och inkluderar slutmärket för dess binära kod (detta diskuterades i föregående artikel). Slutligen, i slutet av koden finns "buffer"-etiketten, som används av load_file-rutinen. I grund och botten behöver load_file-rutinen ledigt utrymme i RAM-minnet för att göra en del mellanarbete medan vi söker efter en fil på disken, och vi har gott om ledigt utrymme efter att ha laddat starthanteraren, så vi placerar bufferten här.

För att montera systemets starthanterare, använd följande kommando:

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

Nu måste vi skapa en virtuell diskettavbildning i MS-DOS-format och lägga till vår bootloader binära kod till dess första 512 byte med hjälp av följande kommandon:

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

Vid denna tidpunkt kan processen att utveckla en systemstarthanterare anses vara avslutad! Vi har nu en startbar diskettavbildning som låter oss ladda operativsystemets kärnbinära kod från en fil som heter mykernel.bin och köra den. Därefter väntar en mer intressant del av arbetet på oss - utvecklingen av själva operativsystemets kärna.

Operativsystems kärna

Vi vill att vår operativsystemkärna ska utföra många viktiga uppgifter: visa ett hälsningsmeddelande, acceptera input från användaren, avgöra om inmatningen är ett kommando som stöds och köra program från disken när användaren anger sina namn. Det här är operativsystemets kärnkod från filen 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 mov cx call, 32768 jc load_fail anrop 32768 jmp loop load_fail: mov si, load_fail_msg anrop lib_print_string jmp loop list_files: mov si, file_list anrop lib_get_file_list anrop lib_print_string jmp loop prompt db 13, 10, "MyOS 13, 10, "Mitt OS > 1, 0, 0, 0, 0, 0, 0,1 ", 0 user_input gånger 256 db 0 file_list gånger 1024 db 0 %inkludera "lib.asm"

Innan du tittar på koden bör du vara uppmärksam på den sista raden med direktivet för att inkludera källkodsfilen lib.asm, som också finns i asmschool.zip-arkivet från vår webbplats. Detta är ett bibliotek med användbara rutiner för att arbeta med skärmen, tangentbordet, strängarna och diskarna som du också kan använda - i det här fallet inkluderar vi denna källkodsfil i slutet av huvudkällkodsfilen för operativsystemets kärna i ordning att göra den senare så kompakt och vacker som möjligt . Se avsnittet lib.asm biblioteksrutiner för ytterligare information om alla tillgängliga subrutiner.

I de första tre raderna av operativsystemets kärnkod fyller vi segmentregistren med data för att peka på segment 2000 där den binära koden laddades in. Detta är viktigt för garanterat korrekt funktion instruktioner som lodsb, som måste läsa data från det aktuella segmentet och inte från något annat. Efter detta kommer vi inte att utföra några ytterligare operationer på segmenten; vårt operativsystem kommer att fungera med 64 KB RAM!

Längre in i koden finns en etikett som motsvarar början av slingan. Först och främst använder vi en av rutinerna från lib.asm-biblioteket, nämligen lib_print_string, för att skriva ut hälsningen. Byte 13 och 10 före hälsningsraden är escape-tecken. ny linje, tack vare vilket hälsningen inte kommer att visas omedelbart efter utmatningen av något program, utan alltid på en ny rad.

Efter detta använder vi en annan rutin från lib.asm-biblioteket som heter lib_input_string, som tar användarens tangentbordsindata och lagrar den i en buffert som pekas på i SI-registret. I vårt fall deklareras bufferten nära slutet av operativsystemets kärnkod enligt följande:

User_input gånger 256 db 0

Denna deklaration låter dig skapa en buffert på 256 tecken, fylld med nollor - dess längd bör räcka för att lagra kommandon för ett enkelt operativsystem som vårt!

Därefter utför vi validering av användarinmatning. Om den första byten i user_input-bufferten är noll, tryckte användaren helt enkelt på Enter utan att ange något kommando; Glöm inte att alla strängar slutar med nolltecken. Så i det här fallet ska vi bara gå till början av slingan och skriva ut hälsningen igen. Men om användaren anger något kommando måste vi först kontrollera om han skrev in kommandot ls. Hittills har du bara kunnat observera jämförelser av enskilda byte i våra assemblerprogram, men glöm inte att det också är möjligt att jämföra dubbelbytevärden eller maskinord. I den här koden jämför vi det första maskinordet från user_input-bufferten med maskinordet som motsvarar raden ls och, om de är identiska, flyttar vi till kodblocket nedan. Inom detta kodblock använder vi en annan rutin från lib.asm för att få en kommaseparerad lista med filer på disken (som ska lagras i file_list-bufferten), skriva ut den listan till skärmen och gå tillbaka till slingan för att bearbeta användarinmatning.

Utförande av tredjepartsprogram

Om användaren inte anger kommandot ls, antar vi att de skrev in namnet på programmet från disken, så det är vettigt att försöka ladda det. Vårt lib.asm-bibliotek innehåller en implementering av den användbara lib_load_file-rutinen, som analyserar FAT12-diskfilsystemtabellerna: den tar en pekare till början av en rad med filnamnet genom AX-registret, samt ett offsetvärde för laddning binär kod från en programfil via CX-registret. Vi använder redan SI-registret för att lagra en pekare till strängen som innehåller användarinmatning, så vi kopierar denna pekare till AX-registret och placerar sedan värdet 32768, som används som en offset för att ladda den binära koden från programfilen, in i CX-registret.

Men varför använder vi just detta värde som offset för att ladda binär kod från en programfil? Tja, detta är bara ett av minneskartalternativen för vårt operativsystem. Eftersom vi arbetar i ett enda 64 KB-segment och vår kärnbinär laddas med offset 0, måste vi använda de första 32 KB minne för kärndata och de återstående 32 KB för att ladda programdata. Offset 32768 är alltså mitten av vårt segment och låter oss tillhandahålla tillräckligt med RAM till både operativsystemets kärna och laddade program.

Rutinen lib_load_file utför sedan en mycket viktig operation: om den inte kan hitta en fil med det givna namnet på disken, eller av någon anledning inte kan läsa den från disken, avslutas den helt enkelt och ställer in en speciell bärflagga. Detta är en CPU-tillståndsflagga som ställs in under utförandet av vissa matematiska operationer och bör inte intressera oss för tillfället, men samtidigt kan vi bestämma närvaron av denna flagga för att fatta snabba beslut. Om rutinen lib_load_asm ställer in bärflaggan använder vi instruktionen jc (hopp om bär) för att hoppa till ett kodblock som skriver ut felmeddelandet och återgår till början av användarinmatningsslingan.

I samma fall, om överföringsflaggan inte är inställd, kan vi dra slutsatsen att subrutinen lib_load_asm framgångsrikt har laddat den binära koden från programfilen till RAM-minnet på adress 32768. Allt vi behöver i det här fallet är att initiera exekveringen av binären kod laddad på denna adress, det vill säga börja köra det användarspecificerade programmet! Och efter att ret-instruktionen har använts i det här programmet (för att återgå till anropskoden), behöver vi helt enkelt återgå till slingan för bearbetning av användarinmatning. Sålunda skapade vi ett operativsystem: det består av de enklaste kommandotolknings- och programladdningsmekanismerna, implementerade i cirka 40 rader assemblerkod, om än med mycket hjälp från rutiner från lib.asm-biblioteket.

För att sätta ihop operativsystemets kärnkod, använd följande kommando:

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

Efter detta måste vi på något sätt lägga till filen mykernel.bin till diskettavbildningsfilen. Om du är bekant med tricket med att montera skivbilder med loopback-enheter, kan du komma åt innehållet i skivavbildningen med floppy.img, men det finns ett enklare sätt att använda GNU Mtools (www.gnu.org/software /mtools). Detta är en uppsättning program för att arbeta med disketter som använder MS-DOS/FAT12 filsystem, tillgängliga från paketförråd programvara alla populära Linux-distributioner, så allt du behöver göra är att använda apt-get, yum, pacman eller vilket verktyg du än använder för att installera mjukvarupaket på din distribution.

Efter att du har installerat lämpligt programpaket måste du köra följande kommando för att lägga till filen mykernel.bin till diskavbildningsfilen floppy.img:

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

Notera de roliga symbolerna i slutet av kommandot: kolon, kolon och snedstreck. Nu är vi nästan redo att lansera vårt operativsystem, men vad är poängen om det inte finns några appar för det? Låt oss rätta till detta missförstånd genom att utveckla en extremt enkel applikation. Ja, nu kommer du att utveckla en applikation för ditt eget operativsystem - tänk bara hur mycket din auktoritet kommer att stiga bland nördarna. Spara följande kod i en fil som heter test.asm:

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

Den här koden använder helt enkelt BIOS-funktionen för att skriva ut ett "X" på skärmen och återställer sedan kontrollen till koden som kallade den - i vårt fall är den koden operativsystemets kod. Organisationsraden som börjar programmets källkod är inte en CPU-instruktion, utan ett NASM assembler-direktiv som säger att den binära koden kommer att laddas in i RAM-minnet vid offset 32768, och därför måste alla förskjutningar beräknas om för att ta hänsyn till detta.

Den här koden måste också monteras, och den resulterande binära filen måste läggas till i diskettavbildsfilen:

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

Ta nu ett djupt andetag, gör dig redo att begrunda de oöverträffade resultaten av ditt eget arbete och starta upp diskettavbildningen med en PC-emulator som Qemu eller VirtualBox. Till exempel kan följande kommando användas för detta ändamål:

Qemu-system-i386 -fda floppy.img

Voila: systemstarthanteraren boot.img, som vi integrerade i den första sektorn av diskavbildningen, laddar operativsystemets kärna mykernel.bin, som visar ett välkomstmeddelande. Ange kommandot ls för att få namnen på två filer som finns på disken (mykernel.bin och test.bin), och ange sedan namnet på den sista filen för att köra den och visa ett X på skärmen.

Det är coolt, eller hur? Nu kan du börja slutföra kommandoskal ditt operativsystem, lägg till implementeringar av nya kommandon och lägg även till ytterligare programfiler på disken. Om du vill köra det här operativsystemet på en riktig PC, bör du hänvisa till avsnittet "Köra starthanteraren på en riktig hårdvaruplattform" från föregående artikel i serien - du behöver exakt samma kommandon. Nästa månad kommer vi att göra vårt operativsystem kraftfullare genom att tillåta nedladdningsbara program att använda systemfunktioner, och introducera ett koddelningskoncept för att minska kodduplicering. Mycket av arbetet ligger kvar.

lib.asm biblioteksrutiner

Som nämnts tidigare, tillhandahåller biblioteket lib.asm en stor uppsättning användbara rutiner för användning inom ditt operativsystems kärnor och individuella program. Vissa av dem använder instruktioner och begrepp som ännu inte har berörts i artiklar i denna serie, andra (som diskrutiner) är nära besläktade med utformningen av filsystem, men om du anser dig vara kompetent i dessa frågor kan du läsa dem själv med deras implementeringar och förstå principen om drift. Det är dock viktigare att ta reda på hur man ringer dem från din egen kod:

  • lib_print_string - accepterar en pekare till en nollterminerad sträng via SI-registret och skriver ut strängen till skärmen.
  • lib_input_string - accepterar en pekare till en buffert via SI-registret och fyller denna buffert med tecken som angetts av användaren med hjälp av tangentbordet. Efter att användaren tryckt på Enter-tangenten avslutas raden i bufferten null och kontrollen återgår till den anropande programkoden.
  • lib_move_cursor - flyttar markören på skärmen till en position med koordinater som överförs genom DH (radnummer) och DL (kolumnnummer) registren.
  • lib_get_cursor_pos - denna subrutin bör anropas för att erhålla de aktuella rad- och kolumnnumren med hjälp av DH- respektive DL-registren.
  • lib_string_uppercase - tar en pekare till början av en nollterminerad sträng med hjälp av AX-registret och konverterar strängens tecken till versaler.
  • lib_string_length - tar en pekare till början av en nollterminerad sträng via AX-registret och returnerar dess längd via AX-registret.
  • lib_string_compare - accepterar pekare till början av två nollterminerade strängar med hjälp av SI- och DI-registren och jämför dessa strängar. Ställer in bärflaggan om linjerna är identiska (för att använda en hoppinstruktion beroende på jc carry-flaggan) eller rensar denna flagga om linjerna är olika (för att använda jnc-instruktionen).
  • lib_get_file_list - Tar en pekare till början av en buffert via SI-registret och lägger i den bufferten en nollterminerad sträng som innehåller en kommaseparerad lista med filnamn från disken.
  • lib_load_file - tar en pekare till början av en rad som innehåller filnamnet med hjälp av AX-registret och laddar innehållet i filen med den offset som passerar genom CX-registret. Returnerar antalet byte som kopierats till minnet (det vill säga filstorleken) med hjälp av BX-registret, eller ställer in bärflaggan om en fil med det angivna namnet inte hittas.

Jag säger genast, stäng inte artikeln med tankarna "Fan, ännu en Popov." Han har bara en polerad Ubuntu, medan jag har allt från grunden, inklusive kärnan och applikationer. Så, fortsättning under skärningen.

OS-grupp: Här.
Först ska jag ge dig en skärmdump.

Det finns inga fler av dem, och låt oss nu prata mer i detalj om varför jag skriver det.

Det var en varm aprilkväll, torsdag. Sedan barndomen drömde jag om att skriva ett OS, när jag plötsligt tänkte: "Nu vet jag fördelarna och känslan, varför inte förverkliga min dröm?" Jag googlade på webbplatser om detta ämne och hittade en artikel från Habr: "Hur man börjar och inte slutar skriva ett OS." Tack till dess författare för länken till OSDev Wiki nedan. Jag gick dit och började jobba. Det fanns all information om det minimala operativsystemet i en artikel. Jag började bygga cross-gcc och binutils och skrev sedan om allt därifrån. Du borde ha sett min glädje när jag såg inskriptionen "Hej, kärnvärld!" Jag hoppade direkt ur stolen och insåg att jag inte skulle ge upp. Jag skrev "konsol" (inom citattecken; jag hade inte tillgång till ett tangentbord), men bestämde mig sedan för att skriva ett fönstersystem. Till slut fungerade det, men jag hade inte tillgång till tangentbordet. Och så bestämde jag mig för att komma på ett namn baserat på X Window System. Jag googlade på Y Window System - det finns. Som ett resultat döpte jag Z Window System 0.1, inkluderat i OS365 pre-alpha 0.1. Och ja, ingen såg henne förutom jag själv. Sedan kom jag på hur jag skulle implementera tangentbordsstöd. Skärmdump av den allra första versionen, när det inte fanns något ännu, inte ens ett fönstersystem:

Textmarkören rörde sig inte ens, som du kan se. Sedan skrev jag ett par enkla applikationer baserad på Z. Och här är release 1.0.0 alpha. Det fanns många saker där, även systemmenyer. A filhanterare och kalkylatorn fungerade helt enkelt inte.

Jag blev direkt terroriserad av en vän som bara bryr sig om skönhet (Mitrofan, förlåt). Han sa: "Tvätta ner VBE-läget 1024*768*32, tvätta ner det, tvätta ner det! Nåväl, låt oss dricka upp det!" Tja, jag var redan trött på att lyssna på honom och klippte fortfarande ner honom. Om implementeringen nedan.

Jag gjorde allt med min bootloader, nämligen GRUB. Med dess hjälp kan du ställa in det grafiska läget utan komplikationer genom att lägga till några magiska rader i Multiboot-huvudet.

Ställ 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
Och sedan från Multiboot-informationsstrukturen tar jag framebuffer-adressen och skärmupplösningen och skriver pixlar där. VESA gjorde allt väldigt förvirrande - RGB-färger måste anges i omvänd ordning (inte R G B, utan B G R). I flera dagar förstod jag inte varför pixlarna inte visades!? Till slut insåg jag att jag glömde att ändra värdena för 16 färgkonstanter från 0...15 till deras RGB-ekvivalenter. Som ett resultat släppte jag den och klippte samtidigt ner gradientbakgrunden. Sedan gjorde jag en konsol, 2 applikationer och släppte 1.2. Åh ja, jag glömde nästan - du kan ladda ner OS på


Topp