Eigenes OS?


  • Mod

    ARM, Nintendo DS etc. ist sicher sehr interessant, leider hat nicht jeder so etwas greifbar, vom Programmieren mal abgesehen. Die 80i86 Historie hat uns wirklich einen monströsen Wirrwarr als Kellerfundament hinterlassen. Da habt ihr völlig Recht. Aber umso größer die didaktische Herausforderung. 😉

    Ich versuche die Reihenfolge:

    1. Werkzeuge bereit stellen
    2. Praktisches Arbeiten
    3. Notwendiges Backgroundwissen praxisnah vermitteln
    4. Experimentieren, um Auswirkungen zu zeigen

    einzuhalten.

    Wenn man einsteigt, benötigt man z.B. zunächst nur das Wissen über CPU, Register und Interrupts. Das findet man heute alles ordentlich bei wiki ... 🙂

    Das Mittel, das viele an der Hand haben, die am Internet hängen und Tutorials lesen, ist nun mal der 80i86 PC mit MS Windows.



  • Erhard Henkes schrieb:

    Das Mittel, das viele an der Hand haben, die am Internet hängen und Tutorials lesen, ist nun mal der 80i86 PC mit MS Windows.

    klar, aber du nutzt doch sowieso 'nen simulator (bochs) für deine experimente. dann könntest du auch 'nen simulator für eine interessante plattform verwenden, und nicht für dieses stokelige x86-gedöns.
    🙂



  • das 'monströse Wirrwarr' machts aber gerade interessant.


  • Mod

    @+fricky: Gegen dieses Argument kann man nur einwenden, dass nach der Emulation auch noch die echte Anwendung kommen sollte. Bei ARM fallen mir sofort z.B. Roboter und sonstige Elektronikanwendungen ein, ein schönes Thema, aber man kann nicht alles gleichzeitig machen.
    @": Ich denke, dass einige User, die mit dem PC unter MS Windows arbeiten, vielleicht einfach mal Lust haben, ein OS zum Ticken zu bringen. Das Entscheidende dabei ist die praktische Beherrschung des Themas, denn nur dann hat man die Kraft und Leidenschaft zum Experimentieren und Umsetzen von Ideen. Das ist der entscheidende Punkt, wo viele didaktische Ansätze stecken bleiben.

    Im ersten praktischen Schritt habe ich den Mini-Kernel komplett bootbar in Sektor 1 geschrieben.

    Im nächsten Schritt wird ein Bootloader in Sektor 1 vorgeschaltet, der den gleichen Mini-Kernel nun aus Sektor 2 lädt (BIOS INT für Floppy Disk) und im Speicher anspringt. Im Kernel wird dann lediglich die Boot-Signatur entfernt und die Segment-Adresse geändert.

    Das ist technisch für den Ersteller einfach durchführbar:

    nasm boot.asm -f bin -o boot.bin
    nasm kernel.asm -f bin -o kernel.bin
    copy /b boot.bin + kernel.bin myOS.img
    partcopy myOS.img 0 400 -f0

    siehe: http://www.henkessoft.de/OS_Dev/OS_Dev1.htm



  • Bitte denken Sie daran, sich für solche Aufgaben entsprechende bat-Dateien zu schreiben, wenn Sie experimentieren.

    Habe das Tutorial mal kurz überflogen und das ist der Satz, der mich persönlich "stört". Ich bin nämlich für den Einsatz von Makefiles. Man kann nämlich die Makefiles wie gewöhnliche Batch-Dateien schreiben mit dem Vorteil der "automatischen" Fehlerbehandlung:

    all:
        nasm boot.asm -f bin -o boot.bin 
        nasm kernel.asm -f bin -o kernel.bin 
        copy /b boot.bin + kernel.bin myOS.img 
        partcopy myOS.img 0 400 -f0
    

    Sollte in der einen oder anderen Zeile ein Fehler auftreten, wird Makeprozess sofort abgebrochen... Bei Batch-Dateien müsste man mit unübersichtlichen goto's arbeiten, was dann schnell unübersichtlich wird.



  • Ich habe ganz früher mal einige kleinere Dinge gebaut: Bootloader + Kommandozeile + FAT-Dateisystem + Interpreter (Sah aus wie Assembler, wurde aber interpretiert...). Alles nur 16-bit Assembler mit rawwrite auf Diskette und dann gebootet. Für 32-bit und PMode hatte ich weder die Zeit, noch die Nerven. Die Dokumentation hat mir seinerzeit schon gereicht um zu wissen, dass ich es gar nicht erst versuchen sollte. Warum ich das gemacht habe? Aus Eigeninteresse, und um etwas neues zu lernen. Inzwischen würde ich mich sehr für 64-bit OS-development interessieren, doch wie gesagt habe ich keine Zeit^^


  • Mod

    Ich bin nämlich für den Einsatz von Makefiles.

    Danke für diesen wichtigen Hinweis! Ich denke, zu Beginn erleichtert dies die Sache für einen Einsteiger nicht wirklich. Da lässt man ihn besser die einzelnen Befehlszeilen immer wieder in die Konsole hacken, damit er sich daran gewöhnt. Welches make.exe ohne notwendige DLL würdest Du denn einsetzen? Download-Link? Wo wird der Einsatz möglichst einfach beschrieben? Vielleicht überzeugst Du mich doch noch.

    Das waren die Gründe, warum ich mich - zumindest für die ersten Schritte - für bat-Dateien entschieden habe. Langfristig geht dies natürlich nicht, aber momentan habe ich noch keine C-Toolchain im Einsatz. Es gibt nur nasm.exe und partcopy.exe.

    Ich habe auch das img gegen bin getauscht, um nicht zu verwirren:

    nasm boot.asm   -f bin -o boot.bin
    nasm kernel.asm -f bin -o kernel.bin
    copy /b boot.bin + kernel.bin MyOS.bin
    partcopy MyOS.bin 0 400 -f0
    

    EDIT: Habe doch noch ein einfaches make.exe ohne DLL gefunden:
    http://www.steve.org.uk/Software/make/make.zip
    EDIT1: Bei den Bin-Tools von gcc 2.03 ist auch ein alleinstehendes make.exe dabei: http://www.osdever.net/downloads/compilers/DJGPP-Installer-nocpp.exe

    Ich werde dies im Tutorial abändern, weil der Einsatz von make.exe / makefile langfristig wirklich der richtige Weg ist. 👍

    Ist als Alternative zu den bat-Dateien dargestellt, siehe
    http://www.henkessoft.de/OS_Dev/OS_Dev1.htm#mozTocId748324 (Kapitel 4.5.3)
    Praktisches Hindernis können die notwendigen Separatoren im makefile sein, aber gvim zeigt dies sehr gut durch fehlende bzw. vorhandene rote Schriftfarbe an.
    Ein Einstieg in den make-Prozess muss auf jeden Fall erfolgen. Da führt wahrlich kein Weg beim OS Development vorbei.


  • Mod

    Bootloader + Kommandozeile + FAT-Dateisystem + Interpreter (Sah aus wie Assembler, wurde aber interpretiert...).

    bootloader + command-line interpreter ist nun vorhanden, zumindest im Real Mode. Einige Befehle und strcmp wurden bereits implementiert, damit man Experimente mit eigenen Befehlen leicht beginnen kann. Vielfach wird hier in Tutorials nur der Reboot eingebaut und man wird mit "Go wild!" oder "Viel Spaß!" alleine gelassen.
    Den Reboot haben wir mit "exit" natürlich auch schon. Ich hoffe, dass ich bisher alles möglichst verständlich und zum praktischen Nachmachen einladend dargestellt habe, sonst kann ich gleich aufhören. Diesbezüglich bitte ich um ernsthaftes Feedback.

    Der Tipp mit dem make-Prozess wurde inzwischen umgesetzt. Danke!

    Vielleicht könnt ihr auch über den Code schauen, denn bei einem OS sollte alles möglichst performant laufen.

    @/rant/: Wenn Du Lust hast, kannst Du mich gerne unterstützen, soweit Du kannst.
    Hier steht nichts unter Zeitdruck. Didaktische Qualität und leichte Umsetzung ist wichtig und geht vor Quantität.

    Die Umschaltung von RM nach PM übernehme ich gerne. Da knoble ich noch an der Didaktik.



  • Hallo,

    habe mal aus Neugier das Tutorial "durchgearbeitet" (mit copy-paste natürlich ;)).
    Habe mit bochs folgende Erfahrung gemacht: Booten funktioniert auch mit von nasm erzeugten Binärdateien. D.h. man benötigt nicht unbedingt ein Diskettenlaufwerk oder sonstiges. In der bochsrc.txt reicht z.B. folgendes:

    floppya: 1_44=c:\BochsMyOS\MyOS.bin, status=inserted
    

    Und die Datei MyOS.bin erzeugen wie immer:

    nasm.exe kernel.asm -f bin -o MyOS.bin
    

    Finde ich schön, weil wer hat denn heutzutage ein Diskettenlaufwerk...

    Und ein Problem habe ich mit meinem Makefile gehabt. Folgende Zeile wollte make irgendwie nicht akzeptieren:

    ...
        copy /b bootloader.bin + kernel.bin MyOS.bin
    

    Da kommt bei mir immer folgende Fehlermeldung:

    process_begin: CreateProcess(NULL, copy /b bootloader.bin + kernel.bin MyOS.bin, ...) failed.
    make (e=2): Das System kann die angegebene Datei nicht finden.
    mingw32-make: *** [OS3] Error 2
    

    Und hier ein "Workaround":

    ...
        cmd /c copy /b bootloader.bin + kernel.bin MyOS.bin
    

    Man muss also eine neue Instanz der Windows Befehlsinterpreters cmd starten und das eine Kommando übergeben. Warum auch immer. Aber wie dem auch sei, mein Makefile sieht jetzt so aus:

    all:
    	@echo Please select target: OS1 or OS2 or OS3
    
    OS1:
    	@echo OS1 selected.
    	rm -fv kernel.bin bootloader.bin MyOS.bin
    	c:\nasm-2.06rc6\nasm.exe kernel.asm -f bin -o MyOS.bin
    	@echo Ready.
    
    OS2:
    	@echo OS2 selected.
    	rm -fv kernel.bin bootloader.bin MyOS.bin
    	c:\nasm-2.06rc6\nasm.exe kernel2.asm -f bin -o MyOS.bin
    	@echo Ready.
    
    OS3:
    	@echo OS3 selected.
    	rm -fv kernel.bin bootloader.bin MyOS.bin
    	c:\nasm-2.06rc6\nasm.exe bootloader.asm -f bin -o bootloader.bin
    	c:\nasm-2.06rc6\nasm.exe kernel3.asm -f bin -o kernel.bin
    	cmd /c copy /b bootloader.bin + kernel.bin MyOS.bin
    	@echo Ready.
    
    clean:
    	rm -fv kernel.bin bootloader.bin MyOS.bin
    

    Man kann also mit make OS1 oder make OS2 oder make OS3 das eine oder andere assemblieren lassen...
    Ansonsten hat alles wunderbar funktioniert (unter bochs) 😉 Bin gespannt auf Protected Mode.


  • Mod

    habe mal aus Neugier das Tutorial "durchgearbeitet"

    Danke für den Test und das positive Feedback.

    floppya: 1_44=c:\BochsMyOS\MyOS.bin, status=inserted

    Darauf habe ich sofort im Tutorial als Alternative für das config-File hingewiesen.
    Heute gibt es wirklich kaum noch Floppy Disk Laufwerke. Aber es ist für viele Coder einfach ein tolles Gefühl, wenn man einen (alten) PC mit einem selbst gebastelten OS physisch von einer Floppy booten lassen kann. 😃
    Da ich selbst noch eines an meinem PC eingebaut habe, verwende ich es natürlich. Ich mag einfach dieses Klack-Geräusch und das aufleuchtende Lämpchen, wenn die Floppy angesprungen wird. Aber man muss mit der Zeit gehen. 😃

    Danke für den "Workaround" bezüglich copy. Ich verwende noch Win XP. Vielleicht klappt es deshalb. Weiß eigentlich jemand wo dieses copy heutzutage auf der Platte steckt? In win/system32 habe ich es nicht gefunden.

    Das ausgefeilte makefile wird sicher auch noch Verwendung finden, ich möchte aber nicht zu früh vom eigentlichen OS ablenken. Die Tools sind vor allem Mittel zum Zweck, und gerade beim Thema makefile blicken viele - durch die IDEs - nicht mehr durch.



  • Hab's mal ueberflogen ... besonders die Codes:

    1. Beispiel:
    Z. 19 (mov si, ...) ist ueberfluessig
    (Auf Kosten der Uebersicht haette man das und evtl. einiges Andere nat. insgesamt auch effizienter loesen koennen...)

    Z. 103, 104 (mov ah, ...; mov al, ...) ist uebelste x86-Suende: sowas schreibt man wenn irgendwie moeglich immer auf einmal in das uebergeordnete Register (hier ax)

    Z. 119, 120 das gleiche

    Das strcmp koennte man IMHO nun aber wirklich effizienter machen:
    Z. 130 ist ueberfluessig. Vergleiche besser direkt al mit [di]
    "cmp reg, 0" kannst du gegen "or al, al" o.Ae. tauschen (wie du es schon in print_string machst)
    ich bevorzuge da "test reg, reg", AFAIR belegt das weniger Platz... Zumindest solltest du dich mal fuer eins entscheiden. 😉
    Es waere auch sinnvoller, das Ergebnis im Z-Flag zurueck zu geben. Das ist naemlich bei deinem Code so wie so schon 0, wenn eq und 1, wenn nicht eq... -> Kram mit CF weg und nur ein Ruecksprung-Label. 🙂

    Z. 96 wieder der 0-Vergleich...

    Es waere uebrigens sinnvoll, die Initialisierung des Stacks nicht unter den Tisch fallen zu lassen.
    Ich bin mir nicht mal sicher, wo das BIOS den Stack anfangs hinzaubert. AFAIK gilt das als undefiniert. Da sollte man eigentlich nicht so einfach reinschreiben. Ein ueberwaeltigender Aufwand ist das nun wirklich auch nicht. Um dem Vorzubeugen: um das mov ss,.. und mov sp,.. gehoeren keine cli/sti, da man bei Intel so schlau war, bei "mov ss, ... " automatisch die Interrupts fuer die naechste Instruktion zu unterdruecken.

    Den "buffer" haette man um Platz zu sparen zB. auch auf den Stack packen koennen.

    Beispiel "Bootloader":
    Z. 30: xor ax, ax waere kuerzer... (Z. 37 dito)
    Z. 41-45: Wieder direkt hintereinander 8Bit-Register mit Konstanten geladen: Boese!

    insgesamt ist das nat. ein *sehr* rudimentaerer BL, aber ich gehe mal davon aus, das war beabsichtigt...

    Beispiel in 4.5.2:
    IMHO wenig sinnvoll, es und ds in BL und Kern direkt hintereinander auf den selben Wert zu setzen. Das im Kern wuerde reichen.



  • Erhard Henkes schrieb:

    Weiß eigentlich jemand wo dieses copy heutzutage auf der Platte steckt? In win/system32 habe ich es nicht gefunden.

    COPY ist als "Funktion" in der cmd.exe integriert. Ist deshalb nur verfügbar in einer Konsole.



  • Erhard Henkes schrieb:

    Mich würde mal interessieren, wer alleine oder mit anderen an einem eigenen OS entwickelt, zu welchem Zweck und in welcher Sprache (ASS, C oder C++)? Links?

    hab auf dos einen multitasking aufsatz gecodet, damit konnte ich dann unter dos mehrere programme gleichzeitig laufen lassen (und ein TSR was im hintergrund per timer interrupt 92mal in 5sekunden die tasks gewechselt hat, wenn ich das noch recht im kopf habe.
    hab ein game os in flat-memory-mode geschrieben, darin funktionierten die 16bit treiber usw. leider nicht, war damit man auf den ganzen speicher zugreifen konnte ohne XMM usw.
    fuer gameboy ein simples OS das mir noch erlaubt hat primitiv den speicher zu begutachten nach nem absturz.
    fuer ps3 hab ich nen simples OS erweitert damit ich andere aufloesungen und den zweiten ppu-thread nutzen kann. hab dann noch ein lightweith task system geschrieben. 🙂


  • Mod

    Es waere uebrigens sinnvoll, die Initialisierung des Stacks nicht unter den Tisch fallen zu lassen.

    Ich hatte einen geschrieben, selbstverständlich ohne cli/sti, habe ihn aber im Tutorial gelöscht, weil es auch so lief (Didaktik ;)).
    Reicht es, wenn man den Stack erst im Kernel implementiert? Im BL (bewusst primitiv gehalten, weil ansonsten immer zu GRUB geraten wird) hat der Stack m.E. noch nichts zu suchen.

    Die al/ah vs. ax Geschichte macht Sinn (werde ich abändern auf ax), weil wir auf jeden Fall nicht unter 16 bit anfangen müssen.

    Welche Speicheradressen würdet ihr verwenden?
    0x0000:0x07C0 für den Bootloader ist klar
    0x1000:0x0000 für den Kernel?
    0x9000:0x0000 für den noch einzurichtenden Stack?



  • Erhard Henkes schrieb:

    Es waere uebrigens sinnvoll, die Initialisierung des Stacks nicht unter den Tisch fallen zu lassen.

    Ich hatte einen geschrieben, selbstverständlich ohne cli/sti, habe ihn aber im Tutorial gelöscht, weil es auch so lief (Didaktik ;)).
    Reicht es, wenn man den Stack erst im Kernel implementiert? Im BL (bewusst primitiv gehalten, weil ansonsten immer zu GRUB geraten wird) hat der Stack m.E. noch nichts zu suchen.

    Dafuer, dass er im BL "noch nichts zu suchen hat", machst du aber doch regen Gebrauch davon. 😉
    Wenn du es ganz sauber haben willst, kannst du ihn im Kern nochmal an eine ausgekluegeltere Stelle verfrachten.


  • Mod

    ; set parameters for reading function
        ; 8-bit-wise for better overview
        mov dl,[bootdrive] ; select boot drive
        mov al,10          ; read 10 sectors
        mov ch, 0          ; cylinder = 0
        mov cl, 2          ; sector   = 2
        mov dh, 0          ; head     = 0
        mov ah, 2          ; function "read"
    

    Das bleibt! Hier geht die Didaktik vor. 🙂
    Ansonsten danke für die Tipps!
    Ich habe alle Helfer bereits "namentlich" im Tutorial erwähnt. 👍

    http://www.henkessoft.de/OS_Dev/OS_Dev1.htm


  • Mod

    Dafuer, dass er im BL "noch nichts zu suchen hat", machst du aber doch regen Gebrauch davon. 😉

    OK, ich sehe es ein:

    org 0x7C00  ; set up start address 
    
        ; setup a stack 
        mov ax, 0x9000  ; address of the stack
        mov ss, ax      ; SS = 0x9000 
        xor sp, sp      ; SP = 0x0000 
    
        ; start
        mov [bootdrive], dl ; boot drive from DL
        call load_kernel    ; load kernel
    
        ; jump to kernel
        jmp 0x1000:0x0000   ; address of kernel
    


  • mit einem "OS" hat dieses x86er-bootloader gefrickel noch nicht viel zu tun. wann geht's denn endlich los mit multitasking, betriebsmittelverwaltung, hal, etc.
    🙂



  • 🤡
    Im Prinzip richtig. Nur duerfte es sehr schwierig werden, dieses weite, eher theorielastige Themengebiet geschickt mit Erhards praktisch ausgerichteter Didaktik unter einen Hut zu bringen.

    Erhard Henkes schrieb:

    Das bleibt! Hier geht die Didaktik vor. 🙂

    Ok, aber vielleicht kann ich dich noch ueberzeugen, der Sache einen kleinen Kommentar zur Effizienz beizufuegen. Ich kriege jedes Mal einen Krampf, wenn ich sowas sehe (zuletzt sogar in einem kommerziellen BL). 😃



  • Ins Kapitel "4.5.1 Bootloader" passt ev. noch einiges zur relativen (segmentierten) Schreibweise von Adressen:

    Relative Adresse -> Segment:Offset
    Absolute Adresse -> (Segment * 0x10) + Offset
    
    org 0x7C00 ; set up start address  
    
    ; setup a stack
    mov ax, 0x9000  ; address of the stack
    mov ss, ax      ; SS = 0x9000
    xor sp, sp      ; SP = 0x0000 
    (...)
    ; jump to kernel
    jmp 0x1000:0x0000   ; address of kernel
    

    Das Programm startet bei 0x7C00, legt einen Stack an bei 0x9000, lädt den Kernel nach 0x1000 und springt dorthin.

    Das Programm startet relativ bei 0000:7c00, absolut bei (00000x10)+7c00 = 0x7c00.
    Aber der Stack liegt relativ bei 9000:0000, absolut bei (9000
    0x10)+0 = 0x90000.
    Der Kernel liegt somit absolut bei 0x10000, weshalb der Bootloader alleine locker (0x10000 - 0x7c00 = 0x8400) über 33 KB groß sein kann.
    Prinzipiell eine nicht akzeptable Verschwendung von (einstmals) wertvollen Speicherplatz. 🙂
    Aber das sollte vorerst keine Rolle spielen. Didaktik geht vor! 👍


Anmelden zum Antworten