Aktív témák
-
kisfurko
senior tag
Ne ijesszétek már el...
Én írtam egy DOS extendert segítség nélkül (van beépített debuggere is, de az nem lesz kész már soha :D), szóval csak kitartás kell.
Először is le kell tiltani minden megszakítást. Utána létre kell hozni a GDT-t, IDT-t, egy TSS-t (ha nem csak 0-ás privilégium szinten akarsz szüttyögni), ezeket betölteni a megfelelő regiszterekbe. A GDT-ben minimum kell egy kód és egy adat szegmens leírója, ha nem csak 0-ás p. szint van, akkor kell még egy-egy ezekből megfelelő jogosultságokkal. Az IDT-ben kell a megszakításoknak (mind a 16-nak) és az exception-öknek bejegyzés. Ha BIOS-t is akarsz használni, akkor nem árt egy int vagy call gate, amivel hívhatod virtual 86 módban a valós kódokat. Ha kultúrált vagy, akkor csinálsz LDT-t is (ez egy bejegyzés a GDT-ben) és betöltöd azt is a megfelelő regiszterbe. Ajánlatos még átprogramozni a PIC-eket, hogy ne olyan kretén helyeken jöjjenek az interruptok (ne ütközzön például a GP Fault-tal a HDD). A GDT, IDT, TSS (szóval ezek a táblák nyugodtan lehetnek data sorokban, csak a bázisukat kell számítani, kivéve, ha mindig ugyanoda rakod).
Kérdezz nyugodtan, sajnos forrást most nem tudok bemásolni, mert nincs bekötve az első, szeretett, kicsi HDD-m, de ha kell, akkor szívesen megteszem, remélem még működik.
Sokat szoptam a témával, jelentősen lerövidítheti a fejlesztési idődet a segítség (mert okos intelék nem írnak le ám mindent...:(). Még az a szerencséd is megvan, hogy teljesen dokumentáltam, hogy később is megértsem.
Szerk: úgyis kihagytam valamit, ha kell még a segítség, szólj!
[Szerkesztve] -
kisfurko
senior tag
Baromira attól függ a bonyolultság, hogy mire akarja használni. Sima memóriakezelés nem nagy ügy, a processzkezelés sem (ha egyáltalán multitaszk kell neki), de ez nem is lényeg. Gondolj egy C64-re például, abban nem volt se memóriakezelés (jó, a BASIC interpreter kezelte a sajátját), se processzkezelés, se IPC, mégis jól lehetett használni.
Én is akartam saját operációs rendszert írni, de letettem róla, mert nem adnak ki elég információt a hardware-ekről. Meg azért is hagytam abba, mert a PC-vel ilyen szinten nem érdemes foglalkozni, diliházban végeztem volna az apró idiótaságok miatt. :DD
De ameddig irogattam az extendert (az első lépés a védett módhoz), addig baromira élveztem, nagyon sokat tanultam, szinte kívülről fújom az x86 architektúrát.
Egyszerűen csodálatos volt, amikor működött a V86 mód :)) -
kisfurko
senior tag
''Emiatt szerintem pmodeban is eleg lesz 1 privilegiumszint''
Azért ajánlanám a két szintet, mert ha stack hiba van (ami igen gyakori), akkor triple fault miatt reset a vége, és ekkor nem tudod, hol a hiba. Úgy célszerű csinálni, hogy először legyen meg a megfelelő IDT az exception-ök lekezeléséhez. Ezt úgy érdemes csinálni, hogy mondjuk:
exception1:
push 1
jmp exceptionhandler
exception2:
push 2
...
exceptionhandler:
kiírni a CS:EIP-t, a regisztereket, meg egyéb dolgokat, amik jólesnek...:))
Az IDT-be meg szépen beírkálod a címeket.
Meg tanulásnak sem rossz a több privilégiumszint. Mert ahhoz kell a TSS (ami a V86-hoz is kell).
Különben protected mode-ba lépni gyerekjáték, de hogy interaktív programot csináljál... Persze könnyen írhatsz egy billentyűkezelő interruptot pufferrel, meg egy karakterkiíró rutint, ami 0b8000h-tól rakja be a karaktereket (ilyet is csináltam, hogy a debugger csak maga fusson, minden segédrutin nélkül, így BIOS-t is lehet vele lépésről lépésre futtatni). Én inkább csináltam V86-os futtatást, hogy simán használhassam a BIOS rutinokat (meg esetleg a DOS-t).
A NASM pedig tényleg jó, én megszokásból használtam tasm-ot.
Igazából assemblert is akartam írni, meg linkert, talán egyszer még előveszem a ''project''-et :) Csak mostanság a 3D grafikával foglalkozom.
Szóval, ha kell segítség, akkor nyugodtan szólj!
[Szerkesztve] -
kisfurko
senior tag
Floppynál nincs MBR, az csak HDD-n van. Az MBR-ben a partíciók leírásai, és a boot szektor betöltője van. Igazából mindegy, mert fogja, betölti az első szektort, majd futtatja. Semmi másra nincs szükség, a FAT az a DOS része. Oda rakod a kerneled, ahová jólesik, neked kell betöltened a boot szektorbeli kóddal.
Azért nem látja a lemez tartalmát, mert számára ismeretlen a formátum.
Megszokásból csak a második sávon kezdik az adatokat, az első sávon csak a boot szektor van (HDD esetén MBR). -
-
kisfurko
senior tag
''Na a nasm-ban a ''jmp dword cs:ip'' 32bit-es és ''jmp word cs:ip'' 16bit-es far ugrás.
Megnéztem az exe-t hexa editorral és úgy néz ki, hogy a nasm jól fordítja le ezeket az utasításokat. Te próbáltad már a nasm-ot? Mert ha mégsem fordítja jól, akkor az álltalad írt gépikód formát fogom használni. Meg gondolom bizonyos esetekben kell a 66h prefix, amikor a szegmens 16bit-es az utasítás meg 32bit-es.''
Még nem nasm-oztam, mert mindent meg tudtam oldani tasm alatt, és át kellett volna szoknom. Egyszer olvastam végig a doksiját, és nem sokra emlékszem...
Na, megnéztem amit küldtél, de szerintem semmi probléma. Hogyan futtatod? Ugye nem debuggerben? Mivel nem használod a GDT-t (mert nincs szegmens betöltés), ezért azzal még nem kell foglalkozni. Van valami memory manager (qemm, emm386 stb.) bent a memóriában? És honnan tudod, hogy pontosan hol szállt el? Valami emulátorból próbálkozol?
[Szerkesztve] -
kisfurko
senior tag
''Létezik egyáltalán olyan debugger, amit lehetne használni?''
A probléma ott van, hogy védett módba csak valós módból lehet átkapcsolni, így csak olyan debuggert használhatsz, ami valós módú. De mivel átkapcsolsz, a szegmens regiszterek új értelmet nyernek, azokhoz kellene a megfelelő GDT stb. Ráadásul az interruptokat is engedélyezi (hiába volt cli, az csak virtuálisan, a programod szempontjából érvényes). A turbo debugger meg amúgy is elég béna (kicsi, valós módú programokhoz jó).
Én úgy csináltam, hogy stack-re mentettem az engem érdeklő regiszetereket, majd vissza valós módba, ott pedig szépen, kényelmesen kibányásztam a védett módú stack helyéről a számokat.
Az IDT az egy nagy tömb, felépítését tekintve olyan, mint a GDT. Ugyanúgy kell beállítani, mint a GDT-t (lidt). A bejegyzések szintén 8 byte-osak. Egy bejegyzés így néz ki:
dw offszet also 16 bitje
dw szelektor
db 0 ; ez igazabol csak 5 bites, ez azt mondja meg, hogy call gate eseten hany dwordot (wordot) kell a ket kulonbozo szint verme kozott masolni (mert minden szintnek sajat verme van)
db 0 ; a tipust es a priv. szintet adja meg, ugyanugy mint egy deszkriptornal. A tipus ertekei a kovetkezok lehetnek:
; 4 286 call gate
; 5 386 task gate
; 6 286 interrupt gate
; 7 286 trap gate
; 12 386 call gate
; 14 386 int gate
; 15 386 trap gate
dw 0 ; offszet felso 16 bitje
Szóval, baromira hasonló, de itt nem bázis és határ van, hanem szelektor és offszet. Ez a címe a kiszolgáló rutinnak, ide ugrik, a valós módban csak szelektor és offszet van pl. A típust állítsd interrupt gate-re (14)!
A kivételek listája:
0 osztás hiba
1 debug trap
2 NMI
3 int3 utasítás
4 into utasítás
5 bound hiba
6 érvénytelen utasítás
7 device not available (taszkváltásnál kell, vagy koprocesszor emulációnál)
8 double fault (hiba amikor éppen hibát szolgálsz ki)
10 érvénytelen TSS
11 szegmens nem aktív (a P bitje 0)
12 stack hiba
13 általános hiba (ez kell nagyon :DD)
14 lapozási hiba
16 FPU hiba
Meg még van egy pár, de ezek a legfontosabbak. A 9-es és 15-ös nem maradt ki, az nincs. A legegyszerűbb, ha csinálsz egy 32 elemű IDT-t, az lefedi az összes (akár jövőbeni) kivételt. Ahogy ajánlottam feljebb, az összes rutin csak annyi, hogy beteszed a számát a kivételnek a verembe, majd ugrasz a közös rutinra. Érdemes a verembe piszmogni, mert amikor a kiszolgálóra jut a vezérlés, akkor csak az ''áll készen'', hiszen a szegmens regiszterek ismeretlen értéket tartalmaznak (és a CS nem írható). Vagy benyomatod a verembe a DS-t, majd szabad a pálya. Mindenképpen 0-ás priv. szint kell a kiszolgáláshoz (természetesen a cél CS-nek is 0-ás szintűnek kell lennie)! Persze az int3, into lehet alacsonyabb szinten, ott azt jelenti, hogy melyik a legalacsonyabb szint, ahol végrehajtható az utasítás.
Van még egy lényeges dolog. Lehet, hogy tudsz róla, lehet, hogy nem. Amikor átváltasz másik módba, akkor a szegmensek megőrzik a régi tartalmukat. De itt ne csak a számra gondolj, amit betöltöttél, hanem azokra az értékekre, amit a deszkriptorban állítani tudsz (valós módban is ugyanúgy működik a határ ellenőrzés pl.). Hogy ez ne okozzon gondot, átváltás után szükség van egy szegmensközi ugrásra (far jmp pl.). Ha vissza is akarsz váltani, akkor szükséged lesz egy olyan deszkriptorra, ami egy valós módú szegmenst ír le. 64k-s határral, 16 bites móddal, 0-ás priv. szinttel. Lehet, hogy a fajtáját is be kell állítani, már nem emlékszem, hogy én hogyan csináltam.
Kiváncsian várom a fejleményeket! -
kisfurko
senior tag
''Most egy TSS-t szeretnék létrehozni, ami nem a 0-ás privilégium szinten futna.''
Ezt nem értem...:)
A TSS ahhoz kell, hogy mindenféle járulékos információt a taszkkal kapcsolatban tárolni tudjon a proci (Task State Segment). Itt tárolódik például az összes stack címe, vagy taszkváltáskor a regiszterek tartalma, de az I/O permission bitmap is (a V86 módnak). Tehát, ha akarsz 0-ás priv. szinten kívül más szintet, akkor kell egy TSS. A szükséges priv. szint stack mutatóit be kell állítani benne, majd a deszkriptor táblában létre kell hozni egy TSS bejegyzést, és ezt a címet (mint akár egy szegmens címét) be kell tölteni, csak nem egy szegmens regiszterbe, hanem a Task Register-be (pl. mov ax,TSS_SEG ltr ax). Ha nincs szükség az I/O permission bitmap-re, akkor állítsd a kezdetét a TSS határán túlra.
A C-s dologhoz sajnos nem tudok semmit tanácsolni, én csak full assemblyben csináltam mindent. Ezt a GRUB-os dolgot említették sokan, biztos jó. :DD