Eigenes OS?


  • Mod

    Feedback bezüglich Code von Nobuo T:

    Test 1: Auf Floppy schreiben und "echt" booten.
    Resultat: Perfekt! 👍

    Erstes Code-Studium:
    Kernel auf 0x08000 gesetzt wie diskutiert,
    buffer weg,
    A20 eingeschaltet (die berühmte 21. Adressleitung),
    ansprechende Ausgabe der Meldung im PM

    @Nobuo T: Danke für die fundierte Unterstützung! 🙂

    @all: bitte den Code checken, gute Ideen werden sofort angenommen. Vielleicht kann man auch im Real Mode noch etwas Interessantes ergänzen, z.B. den CPUID-Befehl?!

    Waere praktisch, wenn du die Quellcodes in deinem Tutorial auch direkt als Downloads haettest (habe sie zumindest nicht gefunden).

    http://www.henkessoft.de/OS_Dev/Downloads/20090321_eh_os.zip

    Ich werde diesen m.E. gelungenen Einstieg in den PM nun didaktisch aufarbeiten, damit das Tutorial Einsteigern das Leben so leicht wie möglich macht. Da fehlen noch einige gute Abbildungen. Die Unterscheidung zwischen 16-Bit- und 32-Bit-Protected Mode ist didaktisch wichtig. Paging, Unterschied lineare/phys. Adresse ebenso.



  • Erhard Henkes schrieb:

    Vielleicht kann man auch im Real Mode noch etwas Interessantes ergänzen

    Vielleicht folgendes:

    Wenn man im PM ein Segmentregister initialisiert und dann wieder in den RM wechselt, bleibt die Segmentgröße des Selektors für das initialisierte Segmentregister erhalten. Dadurch hat man über dieses Segmentregister Zugriff auf die gesamte Segmentgröße auch im RM.

    Was heißt, die Begrenzung auf 1MB RAM im RM kann relativ einfach außer Kraft gesetzt werden. Das Segmentregister darf nur nicht im RM wieder verändert werden. Sonst ist der Spaß vorbei. Aber man ja beliebig oft hin und her wechseln. 🙂


  • Mod

    @+gjm+:
    Das klingt ja echt abenteuerlich! 😃
    Findet man dazu bereits einen Link im Internet oder ist das Geheimwissen?
    Wie läuft denn da die genaue Adressierung im zurück geschalteten RM ab?

    @abc.w: Hast Du den neuen Code bereits ausprobiert?
    http://www.henkessoft.de/OS_Dev/Downloads/20090321_eh_os.zip

    @Nobuo T: Deine konstruktiven Vorschläge wurden aufgenommen und hoffentlich weitgehend umgesetzt. Multitasking und A20 Gate wird jetzt auch breiter besprochen. Das ist schon ganz schön viel Text, hoffentlich brauchbare Abbildungen und ziemlich viele Zeilen Assembler, nur um die Basis PM zu erreichen. Didaktisch ist es wirklich nicht ganz einfach die x86 Historie verständlich zu erklären, ohne allzu langatmig zu werden. 😉


  • Mod

    Eine Frage zum Bootloader-Code:
    http://www.henkessoft.de/OS_Dev/OS_Dev1.htm#mozTocId80121

    Wir schreiben den Offset der Sprung-Adresse des Kernels nachs BX.
    Später springen wir ohne Verwendung dieses Registers.

    mov bx, 0x8000     ; set up start address of kernel
    ;...
    jmp 0x0000:0x8000   ; address of kernel
    

    ES:BX ist notwendig für int 0x13 als "Buffer Address Pointer".
    So läuft es auf jeden Fall auch:

    mov bx, 0x8000     ; set up start address of kernel
    ;...
    jmp bx   ; address of kernel
    

    Wäre letzteres o.k.? Ist das didaktisch nicht besser? Man verwendet BX auch im Sprungbefehl sofort sofort.

    Das erscheint ja auch etwas merkwürdig:

    mov [bootdrive], dl ; boot drive from DL
    ...
    mov dl,[bootdrive] ; select boot drive
    

    Man könnte den Kernel wohl auch von anderer Stelle laden?
    Da unser System dies nicht macht, könnte man [bootdrive] doch auch völlig weg lassen?

    Label load_kernel1 kann jetzt ja auch entfallen nach Nobuos Änderung?



  • ^^mannomann. ist das ein grausames gefrickel hier!


  • Mod

    ist das ein grausames gefrickel hier!

    Destruktive (Troll-)Beiträge werden mich nicht aufhalten oder irritieren. 😃
    Bessere Ideen bezüglich der Umsetzung oder Gesasmtkonstruktion sind dennoch gerne gesehen. 🙂
    Begründe mir lieber mal jemand, warum IBM das Boot-Programm genau nach 7C00h gesendet hat. Die exakte Erklärung habe ich bisher noch nirgends gefunden.



  • Naja, mit dem Wegoptimieren muss man immer aufpassen. Das ist sehr oft auch ein Kompromiss zwischen Stabilitaet, Kompatibilitaet und Optimierung, der einen gewisser "Verhaeltnisbereich" IMHO nicht verlassen sollte.

    Erhard Henkes schrieb:

    Wir schreiben den Offset der Sprung-Adresse des Kernels nachs BX.
    Später springen wir ohne Verwendung dieses Registers.

    mov bx, 0x8000     ; set up start address of kernel
    ;...
    jmp 0x0000:0x8000   ; address of kernel
    

    ES:BX ist notwendig für int 0x13 als "Buffer Address Pointer".
    So läuft es auf jeden Fall auch:

    mov bx, 0x8000     ; set up start address of kernel
    ;...
    jmp bx   ; address of kernel
    

    Wäre letzteres o.k.? Ist das didaktisch nicht besser? Man verwendet BX auch im Sprungbefehl sofort sofort.

    Im Code vor dem long jump zum Kern, kommen wir ganz gut zurecht, ohne genau zu wissen, welchen Wert cs hat, da alle Spruenge etc. nur relativ zum aktuellen Offset (IP) adressieren. Wenn du aber den Sprung ueber bx machst, hast du ein absolutes Offset zu CS und musst dir daher sicher sein, dass cs auch 0 ist. Sonst schiesst du am Ziel vorbei. 😉
    Kann also sein, dass das funktioniert. Die Chancen stehen aber auch nicht schlecht, dass es das auf anderen PCs nicht tut.

    Erhard Henkes schrieb:

    Das erscheint ja auch etwas merkwürdig:

    mov [bootdrive], dl ; boot drive from DL
    ...
    mov dl,[bootdrive] ; select boot drive
    

    Man könnte den Kernel wohl auch von anderer Stelle laden?
    Da unser System dies nicht macht, könnte man [bootdrive] doch auch völlig weg lassen?

    Bei der ersten Verwendung hatte ich die Sache in deinem alten Code auch auskommentiert (z. 16). Beim 2. Mal bin ich nicht sicher, ob dl vom Interrupt nicht geschrottet wird.

    Ansonsten ist das wieder eine Kompatibilitaetsfrage. Du kannst natuerlich die Info des BIOS wegschmeissen, dl einfach immer auf 0 (fdd A:) setzen und damit ausschliessen, dass jemand den Code jemals von was Anderem (2. Floppy, USB oder was auch immer) startet, aber IMHO sind es diese 2 Zeilen irgendwie nicht wert.
    Deshalb habe ich sie drin gelassen.

    Erhard Henkes schrieb:

    Label load_kernel1 kann jetzt ja auch entfallen nach Nobuos Änderung?

    Da hast du allerdings recht.

    Erhard Henkes schrieb:

    @+gjm+:
    Das klingt ja echt abenteuerlich! 😃
    Findet man dazu bereits einen Link im Internet oder ist das Geheimwissen?
    Wie läuft denn da die genaue Adressierung im zurück geschalteten RM ab?

    Nunja, IMHO eher ein glitch denn ein echtes feature. Gelegentlich findet man die Sache als "Flat Real Mode", "Unreal Mode" oder auch "Voodoo Mode" bezeichnet und in alten, frickeligen DOS-Spielen oder Demos verwendet.
    Hier gibt es etwas darueber zu lesen.

    Erhard Henkes schrieb:

    Begründe mir lieber mal jemand, warum IBM das Boot-Programm genau nach 7C00h gesendet hat. Die exakte Erklärung habe ich bisher noch nirgends gefunden.

    Vermutung:
    Historische Ursachen. Es war einfach das obere Ende des RAMs (32kb).


  • Mod

    Hallo Nobuo T, danke für deine Antwort. Bei mir wurde DL nicht geschrottet (sind die BIOS-Interrupts nicht gleich realisiert?), der Sprung mit BX alleine klappte ebenfalls. OK, ich werde auf Dich hören und das nur als Kommentar angeben. Load_kernel1 wird gestrichen. Danke für die Links! Voodoo ... 😃

    So sieht der bootloader nun (endgültig) aus:
    http://www.henkessoft.de/OS_Dev/OS_Dev1.htm#mozTocId80121

    Ich bitte um Check des Kernels:
    http://www.henkessoft.de/OS_Dev/OS_Dev1.htm#mozTocId412221
    (@Nobuo T: Abfrage eines Keystroke vor Reboot bei 'exit' wurde bereits eingebaut. Danke für den Hinweis!)

    Noch eine andere didaktische Frage: Die Bezeichnungen KB, MB etc. sind ja inzwischen nicht mehr eindeutig, vor allem bei MB, GB. Würdet ihr auf KiB, MiB, GiB etc. umsteigen? Die Akzeptanz dieses neuen Vorschlages ist ja noch gering.
    http://de.wikipedia.org/wiki/Kibibyte#IEC-Pr.C3.A4fixe_zur_Basis_2



  • Erhard Henkes schrieb:

    Als kleine Belohnung für die, die hier mitlesen:
    http://bazonline.ch/digital/games/bildstrecke.html?id=18752&pageID=1&n=1
    (sehr schöne nostalgische Fotostrecke zum Thema Computer und Werbung)

    hihi, uralt-computer passen schon irgendwie zu diesem thread.
    hier gibts übrigens noch mehr davon: http://www.computerhistory.org/collections/marketingbrochures/
    🙂



  • Hallo,
    Das Tutorial gefällt mir sehr gut 🙂

    Als Alternative zu Bochs und VirtualPC kann ich noch VirtualBox empfehlen (kostenlos).
    Funktionierte bei mir auf Anhieb. Um die Bootdiskette einzubinden muss diese nochnichtmal in ein Image verpackt sein...

    mfg
    Mr X


  • Mod

    Mr X schrieb:

    ... Das Tutorial gefällt mir sehr gut 🙂 ... Als Alternative zu Bochs und VirtualPC kann ich noch VirtualBox empfehlen (kostenlos). Funktionierte bei mir auf Anhieb. Um die Bootdiskette einzubinden muss diese noch nicht mal in ein Image verpackt sein ...

    Danke für das positive Feedback 🙂 und den Tipp mit VirtualBox. Das werde ich mir anschauen. Denn gerade diese Dinge sind didaktisch wichtig, denn jedes Emulations-Programm hat seine Schwächen und Stärken. Nur wenn Theorie, Praxis und Tools Hand in Hand gehen, macht die Sache Freude und treibt die Kreativität vorwärts.

    Nun habe ich auch die gtd.inc beschrieben, ein echtes Bit-Gefrickel.
    http://www.henkessoft.de/OS_Dev/OS_Dev1.htm#mozTocId398766
    Bitte schaut mal drüber, ob ich keinen Fehler gemacht habe.

    Jetzt fehlt noch die Erklärung des Kernel-Codes.


  • Mod

    @Nobuo T: An dem Punkt bin ich auch schon ins Grübeln gekommen, weil dies alles andere als intuitiv ist. Hier kam genau dazu eine Frage aus einem anderen internationalen Forum:
    http://forum.osdev.org/viewtopic.php?f=1&t=19451&p=152668#p152668

    One thing that caught my eye is this:

    Code:
    xor ax, ax
    [...]
    mov ss, ax
    mov sp, ax
    [...]
    call print_string

    You zero SS and SP and then you use the stack (implicitly by CALL). That looked strange first. It's not broken, but it is not very intuitive. Maybe you want to consider to note this in your text (I didn't see anything about that, maybe I just missed it) and explain why and how that works.

    Vielleicht kannst Du da noch etwas zu Deiner Idee erläutern. Da klafft noch eine "didaktische Lücke". 😃
    Das Thema Stack würde ich gerne auch noch beschreiben (Prinzip, SS, SP, Nutzung durch call, ...). Ich war allerdings über deine Lösung so verblüfft, dass ich erst mal genauer hinschauen wollte.

    Auch diese Zeile wirkt auf den ersten Blick merkwürdig.
    add sp, -0x40 ; make room for input buffer (64 chars)



  • Erhard Henkes schrieb:

    Auch diese Zeile wirkt auf den ersten Blick merkwürdig.
    add sp, -0x40 ; make room for input buffer (64 chars)

    na, so machste platz auf'm stack. macht ein c-compiler z.b auch so ähnlich, wenn du ein lokales array aus 64 bytes anlegst.
    🙂



  • Zum Thema Emulatoren könntest Du doch ein eigenes Kapitel machen, indem kurz beschrieben wird, wie man damit das OS zum laufen kriegt und welche Vor-/Nachteile das Programm hat, anstatt gelegentlich zwischendurch weitere Emulatoren vorzustellen...



  • @fricky: SP dürfte aber zu dem zeitpunkt auf 0 sein, weshalb das subtrahieren von 40h (= addieren von -40h?) komisch ist.
    Zumindestens kommt es mir komisch vor, wo liegt der (mein) denkfehler?


  • Mod

    Zum Thema Emulatoren könntest Du doch ein eigenes Kapitel machen, indem kurz beschrieben wird, wie man damit das OS zum laufen kriegt und welche Vor-/Nachteile das Programm hat, anstatt gelegentlich zwischendurch weitere Emulatoren vorzustellen...

    Ja, ist mir auch aufgefallen. Ursprünglich wollte ich nur Bochs verwenden, allerdings sind die Emulatoren so wichtig, dass man alle vorstellen sollte. Werde das konzentrieren. Allerdings erst hinten, denn vorne lenkt das doch stark ab. Nicht jeder arbeitet gerne gleichzeitg mit 10-15 Tools. 😉


  • Mod

    na, so machste platz auf'm stack.

    Das man den Stackpointer nach "unten" wandern lässt, um Platz zu schaffen, ist klar. Aber wie gesagt, ist der Wert vorher bei 0, wenn ich nicht irre. Also hinterher -40. Warten wir auf den Kommentar von Nobuo T. Das hat er genial "verbrochen". Vorher hatten wir den Stack bei 0x90000 (http://www.henkessoft.de/OS_Dev/Bilder/Speicherbelegung.JPG), wo viele ihn hin knallen. Unten bei 0 ist doch auch die IVT. Aber es läuft, das ist die Hauptsache, die theor. Erklärung kommt sicher noch. Vielleicht ein genialer "wrap" 😃



  • Richtig, +fricky. Push macht Platz auf dem Stack, indem 2 (oder 4, oder wie gross der Operand auch immer ist) Byte von (e)sp subtrahiert werden und schreibt dann den Operand dann nach ss:(e)sp.
    Hier wird halt nur Platz auf dem Stack reserviert, erstmal ohne was rein zu schreiben.

    Zu ss und sp mit 0 initialisieren:
    Ist vielleicht nicht sofort intuitiv, aber hier IMHO ziemlich praktisch.
    Wie gesagt hat man es beim x86 mit einem full descending stack zu tun. Wenn also bei sp=0 etwas auf den Stack gepackt wird, gibt es beim Subtrahieren von sp erstmal einen schoenen Ueberlauf, bzw. wrap-arround: zB. 0 - 2 = 0xFFFE. Genau da landet dann der erste Wert.

    Der Code sieht ansonsten beim ersten drueber schauen ganz gut aus. Aber meine msg00 haettest du eigentlich auch gleich ganz loeschen koennen, statt sie nur zu verschieben. War da nur zu debug-zwecken. Die Ausgabe habe ich wie es aussieht auch wieder entfernt, aber den Text vergessen. 😃

    Und zum Teil ueber die A20 zunaechst noch: Ueber die Ports 60h und 64h sprichst du erstmal nur mit dem Controller auf dem Mainboard. Darueber kannst du dann (seriell) etwas an die Tastatur schicken. Und D1 an 64h ist einfach ein Kommando, das bewirkt, dass das naechste Byte auf Port 60h beim outputport landet, statt bei der Tastatur.

    edit: Hui, haette nicht gedacht, dass einfache Integer-Arithmetik (signed/unsigned Addition vs. Subtraktion und Ueberlaeufe) hier fuer solche Verwirrung sorgt. 😕
    Oder hat das damit zu tun, dass da der Stack involviert ist? Das spielt wie gesagt in dem Zusammenhang ueberhaupt keine Rolle.



  • Habe mal die zip Datei 20090321_eh_os.zip runtergeladen, Makefile ein wenig angepasst (wegen meiner nasm Installation), das ganze mit meinen Werkzeugen gebaut und mit den binär Dateien in der zip-Datei verglichen. Alles gleich 🙂
    Unter bochs ausprobiert - es läuft.
    Nun wollte ich mal mit ndisasm ein wenig disassemblieren, hie und da mal schauen und schon gibt es ein kleines "Problem" mit kernel.bin. In der kernel.asm gibt es ja mal 16-Bit, mal 32-Bit Code, was mit [Bits 32] und [BITS 16] "umgeschaltet" wird. Nun, ndisasm kommt damit nicht klar oder ich kenne noch nicht die Möglichkeit, wie man es hinkriegt, dass alles zusammen sauber disassembliert wird... Wäre vielleicht ein Splittern der kernel.asm in zwei Dateien sinnvoll? Eine mit [BITS 16], die andere mit [BITS 32]?
    Eine Sache bezüglich Disassemblieren noch: Am Ende des Bootloaders und des Kernels wird mit Nullen aufgefüllt (X ist 510 oder 1024):

    times X-($-$$) db 0
    

    Was würde gegen z.B. so was sprechen:

    times X-($-$$) hlt
    

    Also Auffüllen mit HLT-Befehlen... Ist nur ein Vorschlag, aber ich persönlich sehe ein Paar Vorteile:
    - beim Disassemblieren sieht man sofort, wo das Ende ist (ok, mit Nullen sieht man es auch, aber man hat lauter add [bx+si],al stehen, was irgendwie unschön ist)
    - zumindest psychologische Wirkung, man fühlt sich ruhiger, weil, wenn die CPU dort warum auch immer ankommt, bleibt sie dort stehen und es passiert nichts 🙂



  • abc.w schrieb:

    Nun wollte ich mal mit ndisasm ein wenig disassemblieren, hie und da mal schauen und schon gibt es ein kleines "Problem" mit kernel.bin. In der kernel.asm gibt es ja mal 16-Bit, mal 32-Bit Code, was mit [Bits 32] und [BITS 16] "umgeschaltet" wird. Nun, ndisasm kommt damit nicht klar oder ich kenne noch nicht die Möglichkeit, wie man es hinkriegt, dass alles zusammen sauber disassembliert wird...

    Ja, ist ein Problem. Die Situation hat man auch nicht all zu haeufig, dass man 16- und 32-Bit code in einem binary hat.
    Ehrlich gesagt bin ich in die Verlegenheit auch noch nicht wirklich gekommen, bzw. hoechstens in einem Debugger - den kann man oft bei Bedarf umschalten.

    abc.w schrieb:

    Wäre vielleicht ein Splittern der kernel.asm in zwei Dateien sinnvoll? Eine mit [BITS 16], die andere mit [BITS 32]?

    Problem beim Aufteilen waere, dass man entweder beim 2. Teil mit dem Start-Offet (org) aufpassen muesste, oder Verschnitt haette, wenn man es an eine groessere, festgelegte Adresse packt.
    Da ist die uebersichtliche Disassemblierbarkeit mit einem einfachen disasm IMHO irgendwie nachrangig.

    abc.w schrieb:

    Eine Sache bezüglich Disassemblieren noch: Am Ende des Bootloaders und des Kernels wird mit Nullen aufgefüllt (X ist 510 oder 1024):

    times X-($-$$) db 0
    

    Was würde gegen z.B. so was sprechen:

    times X-($-$$) hlt
    

    Also Auffüllen mit HLT-Befehlen... Ist nur ein Vorschlag, aber ich persönlich sehe ein Paar Vorteile:
    - beim Disassemblieren sieht man sofort, wo das Ende ist (ok, mit Nullen sieht man es auch, aber man hat lauter add [bx+si],al stehen, was irgendwie unschön ist)
    - zumindest psychologische Wirkung, man fühlt sich ruhiger, weil, wenn die CPU dort warum auch immer ankommt, bleibt sie dort stehen und es passiert nichts 🙂

    Naja, grundsaetzlich vielleicht nicht schlecht, was die Uebersicht beim Disassemblieren betrifft, hat aber ansonsten nicht viel Zweck. Ob der Rechner nun stehen bleibt bis zum naechsten IRQ oder gleich ein wenig dummes Zeug rechnet ist bei so einem gravierenden Problem dann wohl auch ein wenig egal. :p


Anmelden zum Antworten