Forum: PC-Programmierung Assembler OS - Stack frage.


von Max (Gast)


Lesenswert?

Hallo.

Ich wollte mir es auch mal antun und beschäftige mich ein bisschen mit 
der OS-Entwicklung.
(http://www.tutorials.de/programming-tutorials/20706-ein-eigenes-kleines-betriebssystem.html)

Ich habe mir schon sehr viel im Internet durchgelesen, komm jedoch an 
einer Stelle nicht weiter.

Es geht dabei um das Zusammenbauen des Stacks.

Dieser wächst wie bekannt in Richtung niedriger Adressen.
1
cli                  ; Keine Interrupts verwenden!
2
mov ax, 0x9000       ; Adresse des Stack speichern
3
mov ss, ax           ; Stackadresse festlegen
4
mov sp, 0            ; Stackpointer auf 0 setzen
5
sti                  ; Jetzt lassen wir wieder Interrupts zu

Wenn nun der Stackpointer auf 0 gesetzt wird, dann müsste dieser doch 
beim nächsten Aufruf von push ins negative gehen (also z.B. 
0x9000:-0x0001 annehmen).
Kann das funktionieren (tut es zu mindestens bei mir) oder verstehe ich 
da etwas falsch?

Sollte es nicht eher sowas sein:
1
cli                  ; Keine Interrupts verwenden!
2
mov ax, 0x9000       ; Adresse des Stack speichern
3
mov ss, ax           ; Stackadresse festlegen
4
mov sp, 0x200        ; Stackpointer auf 512 setzen (kann also 512 * 16 Bit adressieren 
5
sti                  ; Jetzt lassen wir wieder Interrupts zu



Auch verstehe ich nicht ganz warum der Kernel nach 0x1000:0x0000 geladen 
wird und nicht einfach an den Anfang: 0x0000:0x0000
Sonst würde ja einfach Platz verschenkt.


mfg Max

von Nico22 (Gast)


Lesenswert?

Max schrieb:
> Auch verstehe ich nicht ganz warum der Kernel nach 0x1000:0x0000 geladen
> wird und nicht einfach an den Anfang: 0x0000:0x0000
> Sonst würde ja einfach Platz verschenkt.

Da sitzt der Code, der den Kernel lädt.

von Max (Gast)


Lesenswert?

Der sitzt doch eigentlich bei 0x7C00 und nicht bei 0x0000 oder?

von Max (Gast)


Lesenswert?

Da wären ja immerhin 3 kB Platz... (nach meiner Rechnung)

0x7c00 = 31744

31744/8/1024 = 3 kB

von Andreas F. (aferber)


Lesenswert?

Max schrieb:
> Wenn nun der Stackpointer auf 0 gesetzt wird, dann müsste dieser doch
> beim nächsten Aufruf von push ins negative gehen (also z.B.
> 0x9000:-0x0001 annehmen).

Überleg mal wie deine -1 (bzw. eigentlich -2 da immer zwei Bytes gepusht 
werden) binär im Zweierkomplement aussieht. Dann berücksichtige noch, 
dass beim PUSH erst der Stackpointer dekrementiert wird und dann die 
Daten an die neue Adresse SS:SP gespeichert werden. Damit dürfte klar 
sein, was da genau passiert.

Andreas

von sebihepp (Gast)


Lesenswert?

An der Adresse 0x0000 bis 0x7C00 befindet sich die IVT (Interrupt Vector 
Table) und BDA (BIOS Data Area). Deswegen sollte man seinen "Kernel" 
nicht dorthin laden. Die nächstmögliche Adresse wäre 0x7E00, man 
verwendet jedoch häufig 0x10000 (also 0x1000:0x0000) um die "krummen" 
Adressen am Ende zu vermeiden. Ohne das Segment zu wechseln kommst du 
nur auf 0x10000 - 0x7E00 = 0x8200 freie Bytes. Wenn du gleich bei dem 
Segment 0x1000 anfängst, hast du ganze 64KByte ohne das Segment wechseln 
zu müssen.

Wenn du wirklich in die Betriebssystemprogrammierung einsteigen willst, 
dann solltest du von vorneherein aufpassen keine Daten im RAM zu 
überschreiben. Anfangs kannst du dich nach folgender Tabelle richten:
http://wiki.osdev.org/Memory_Map_(x86)

Viele Grüße
Sebihepp

von Andreas F. (aferber)


Lesenswert?

sebihepp schrieb:
> An der Adresse 0x0000 bis 0x7C00 befindet sich die IVT (Interrupt Vector
> Table) und BDA (BIOS Data Area). Deswegen sollte man seinen "Kernel"
> nicht dorthin laden.

Die Zahlen sind komplett falsch. Die Interrupttabelle ist 1024 Bytes 
gross, im Adressbereich 0000:0000 bis 0000:03ff. Dahinter kommt die BDA 
mit 256 Bytes, von 0000:0400 bis 0000:04ff bzw. von 0040:0000 bis 
0040:00ff. Die erste freie Adresse ist demnach 0000:0500 bzw. 0050:0000.

0000:7c00 ist die Adresse, an die der Bootsektor vom BIOS geladen 
wird, das heisst aber nicht, dass alles davor belegt wäre.

> Die nächstmögliche Adresse wäre 0x7E00, man
> verwendet jedoch häufig 0x10000 (also 0x1000:0x0000) um die "krummen"
> Adressen am Ende zu vermeiden. Ohne das Segment zu wechseln kommst du
> nur auf 0x10000 - 0x7E00 = 0x8200 freie Bytes. Wenn du gleich bei dem
> Segment 0x1000 anfängst, hast du ganze 64KByte ohne das Segment wechseln
> zu müssen.

Die Adresse wird in dem Beispiel einfach nur aus Bequemlichkeit genutzt, 
da es dort sowieso nicht um ein "Betriebssytem" geht (auch wenn der 
Autor das behauptet), sondern allenfalls um einen minimalistischen 
Bootloader. So muss man sich keine Gedanken darum machen, dass man den 
Bootloader nicht durch den frisch geladenen OS-Code überschreibt.

Die Segmente liegen bei x86 nicht wie man meinen könnte auf glatt durch 
64kiB teilbaren Adressen, sondern alle 16 Bytes fängt ein neues Segment 
an, das dann natürlich weitgehend mit den Nachbarsegmenten überlappt. Es 
ist durchaus üblich (siehe z.B. das "Original", DOS in seinen 
Varianten), den Kern direkt im Anschluss an die BDA zu laden. Schaut man 
sich den Bootloader eines solchen OS an (z.B. bei FreeDOS), wird i.d.R. 
eine seiner ersten Amtshandlungen beim Booten sein, sich selbst in einen 
anderen Speicherbereich zu verschieben, um den Platz für den Kern 
freizumachen.

Einfach mal eben 62,5kiB zu verschenken bloss um das Betriebssystem in 
ein "glattes" Segment zu laden will angesichts des eingeschränkten 
Realmode-Adressraumes gut überlegt sein.

Andreas

von sebihepp (Gast)


Lesenswert?

>Die Zahlen sind komplett falsch. Die Interrupttabelle ist 1024 Bytes
>gross, im Adressbereich 0000:0000 bis 0000:03ff. Dahinter kommt die BDA
>mit 256 Bytes, von 0000:0400 bis 0000:04ff bzw. von 0040:0000 bis
>0040:00ff. Die erste freie Adresse ist demnach 0000:0500 bzw. 0050:0000.
>
>0000:7c00 ist die Adresse, an die der Bootsektor vom BIOS geladen
>wird, das heisst aber nicht, dass alles davor belegt wäre.

Was ist an der IVT falsch? Ich habe doch geschrieben dass sie in dem 
Bereich steht und das trifft voll und ganz zu, oder nicht?
Und es stimmt dass bereits ab 0x500 der freie Speicherbereich beginnt. 
Das war mein Fehler. Aber ich habe auch nicht behauptet dass die 
Belegung mit dem Bootsektor zusammenhängt. ;)

>Die Adresse wird in dem Beispiel einfach nur aus Bequemlichkeit genutzt,
>da es dort sowieso nicht um ein "Betriebssytem" geht (auch wenn der
>Autor das behauptet), sondern allenfalls um einen minimalistischen
>Bootloader. So muss man sich keine Gedanken darum machen, dass man den
>Bootloader nicht durch den frisch geladenen OS-Code überschreibt.

Natürlich ist das noch lange keine Betriebssystem. Aber nahezu jeder, 
der eines entwicklen will oder sich auch nur für die "unterste Ebene" 
interessiert, fängt mit solchen kleinen Beispielen an.

>Die Segmente liegen bei x86 nicht wie man meinen könnte auf glatt durch
>64kiB teilbaren Adressen, sondern alle 16 Bytes fängt ein neues Segment
>an, das dann natürlich weitgehend mit den Nachbarsegmenten überlappt. Es
>ist durchaus üblich (siehe z.B. das "Original", DOS in seinen
>Varianten), den Kern direkt im Anschluss an die BDA zu laden. Schaut man
>sich den Bootloader eines solchen OS an (z.B. bei FreeDOS), wird i.d.R.
>eine seiner ersten Amtshandlungen beim Booten sein, sich selbst in einen
>anderen Speicherbereich zu verschieben, um den Platz für den Kern
>freizumachen.

Und ja, die Segmente sind jeweils 16 Bytes groß. Habe ich etwas anderes 
gesagt? Wenn man aber noch im Segment 0 ist dann kommt man ohne zu 
wechseln nun mal maximal bis 0xFFFF. Wenn man nun das weitere Programm 
nach 0x500 ff. schreibt und das Programm immer weiter wächst - man 
probiert dies und das - und plötzlich geht nichts mehr und finde dann 
mal heraus, dass das Programm gerade so groß geworden ist, dass der 
Bootsektor überschrieben wird. Also vermeidet man das besser im voraus 
und nimmt die nächste Adresse, also 0x7E00. Theoretisch könnte man 
natürlich das Segment 0x07E0 benutzen und hätte auch wieder 64KBytes 
platz. Aber es ist doch viel schöner wenn das Programm bei Segment 
0x1000 beginnt, nicht? Dann kann man 0x1000 als Code Segment, 0x2000 als 
Datensegment, ... benutzen und es ist alles schön übersichtlich. =)

>Einfach mal eben 62,5kiB zu verschenken bloss um das Betriebssystem in
>ein "glattes" Segment zu laden will angesichts des eingeschränkten
>Realmode-Adressraumes gut überlegt sein.

Wer sagt denn verschwendet? Wenn dieser Bereich benötigt wird, kann man 
ihn immer noch benutzen. Aber gerade für Anfänger ist es sicher 
einfacher, wenn sie ganze 64KB frei haben und sicher nichts 
überschreiben. ;)

von Sebastian W. (maus23)


Lesenswert?

Hi,

Also ich glaub ihr und ein paar andere Websites habt einen Druckfehler.
Es ist 07c0 nicht 7c00!

Ich habs grade nochmal getestet

07c0 funktioniert
7c00 funktioniert nicht

gruss maus23

von sebihepp (Gast)


Lesenswert?

0x7C00 ist die lineare Adresse und 0x07C0 das Segment ;)

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.