Eigenes OS?



  • 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! 👍


  • Mod

    ... weshalb der Bootloader alleine locker (0x10000 - 0x7c00 = 0x8400) über 33 KB groß sein kann.

    Guter Hinweis! Ich erkläre jetzt mal, warum mir die Didaktik und ein umfassendes Verständnis wichtiger ist als ein schnelles Vorwärtsschreiten, bei dem man viele verliert und wichtige Details auf der Strecke bleiben.

    So findet man dies nun im Tutorial:

    0h *  16^4 + 8h * 16^3 + 4h * 16^2 + 0h * 16^1 + 0h * 16^0  =
    0  * 65536 + 8  * 4096 + 4  * 256  + 0  * 16   + 0  * 1     =
                 32768     + 1024                               =  33792
    

    33792 Byte / 1024 = 33 KByte

    Korrekt ist also:
    Der Bootloader darf höchstens 33 KB groß sein.

    Würdet ihr den Kernel auf 7E00h setzen? Dann passt kein Byte mehr dazwischen.
    Eigentlich könnte man sogar die beiden Boot-Signatur-Bytes überschreiben?
    Also 7DFEh als Minimum? Didaktisch interessante Frage, finde ich.

    Experiment mit 7DFEh: Absturz (bleibt "hängen", lädt keinen Kernel)
    Experiment mit 7E00h: läuft ständig im Kreis (Bootloader Message erscheint und wird wieder gelöscht)
    Also: Wieviel Abstand wird genau benötigt und warum?

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

    Ich habe ein Kap. 4.5.4 "Background-Wissen: Speicheradressierung im Real Mode (RM)" aufgenommen und in 4.5.1 darauf verwiesen:

    Das Programm startet bei 07C00h, legt einen Stack an bei 90000h, lädt den Kernel nach 10000h und springt dorthin.
    Adressierung im Real Mode siehe Abschnitt 4.5.4


  • Mod

    vielleicht kann ich dich noch ueberzeugen, der Sache einen kleinen Kommentar zur Effizienz beizufuegen. Ich kriege jedes Mal einen Krampf, wenn ich sowas sehe

    @Nobuo T:
    Das möchte ich nicht, dass Dir schlecht wird bei dem Code. 😃
    Wir brauchen Dich noch zum Überprüfen/Optimieren.
    Ich habe auf Deinen Wunsch hin folgende Passage ergänzt:

    Hinweis: Die Verwendung des High und Low Byte eines Registers in Verbindung mit dem Befehl mov, wenn genau so gut das gesamte 16-Bit-Register mit einem Befehl bedient werden könnte, ist eine Verschwendung von Prozessortakten! Dies geschieht in obigem Fall nur der besseren Übersicht wegen. Performanter Programmierstil ist dies nicht.

    Also anstelle

    mov al,10 (mov al, 0x0A)
    mov ah, 2

    verwendet man performant

    mov ax, 0x020A

    Nun wollte ich schreiben, wieviele Takte man oben und unten benötigt, habe aber keine Übersichtslisten gefunden. Kennt sich da jemand aus? Link?


  • Mod

    Muss man eigentlich auch auf das Little Endian-Format bei Intel Prozessoren ("Lowest Byte first") eingehen? Ich denke ja, denn bereits, wenn man

    times 510-($-$$) db 0
    dw 0xAA55
    

    schreiben wollte, anstelle

    times 510-($-$$) db 0
    db 0x55
    db 0xAA
    

    Dann muss man die veränderte Reihenfolge erklären. Im Hexeditor sieht man ja auch:
    aus dw 0xAA55 wird im Speicher 55AA

    Zur Vermeidung habe ich zwei Mal db verwendet. 😃

    wann geht's denn endlich los mit multitasking, betriebsmittelverwaltung, hal, etc.

    Hoffentlich bist Du noch jung. Das kann noch etwas dauern. 😃 Nein, im Ernst. Ich möchte jeden Schritt genau ausloten, niemand verlieren.



  • Erhard Henkes schrieb:

    Muss man eigentlich auch auf das Little Endian-Format bei Intel Prozessoren ("Lowest Byte first") eingehen? Ich denke ja

    das solltest du unbedingt tun, mit begründung, warum die x86'er sowas machen. und dann nicht nur für 2-byte werte.

    Erhard Henkes schrieb:

    wann geht's denn endlich los mit multitasking, betriebsmittelverwaltung, hal, etc.

    Hoffentlich bist Du noch jung. Das kann noch etwas dauern. 😃

    hoffentlich bist du noch jung. bei den themen kannste dir 'nen wolf schreiben.
    🙂



  • Also wenn schon, denn schon:

    Hinweis: Die Verwendung des High und Low Byte eines Registers in Verbindung mit dem Befehl mov, wenn genau so gut das gesamte 16-Bit-Register mit einem Befehl bedient werden könnte, ist eine Verschwendung von Speicherplatz und Prozessortakten (in diesem Fall 1 Byte und max. z.B. auf einem 486 1 Takt zusätzlich)! Dies geschieht in obigem Fall nur der besseren Übersicht wegen. Performanter Programmierstil ist dies nicht.

    Also anstelle

    mov al,10 (mov al, 0x0A)
    mov ah, 2

    verwendet man performant

    mov ax, 0x020A

    Erhard Henkes schrieb:

    Nun wollte ich schreiben, wieviele Takte man oben und unten benötigt, habe aber keine Übersichtslisten gefunden. Kennt sich da jemand aus? Link?

    Ist alles ziemlich undurchsichtig. Quellen waeren Docs aktueller CPU von den Herstellern. Wie aber bereits erwaehnt, braucht schon der 486 max. 1 Takt fuer so ein mov - auf neueren CPU werden das vermutlich nicht mehr sein.
    Zumindest AMDs fruehere 64Bitter liessen sich von sowas AFAIR auch gern mal die Pipeline durcheinander bringen, also wahrscheinlich dann auch 2 Takte.


  • Mod

    Also wenn schon, denn schon:

    Was meinst Du damit?



  • Ich habe mir erlaubt, deinen Text hauptsaechlich um die Erwaehnung des zusaetzlichen Speicherplatzverbrauchs zu erweitern (was in einem BL wichtiger sein duerfte als 1 verpulverter CPU-Takt), falls das nicht aufgefallen sein sollte... 🙂



  • Bezüglich Takte gibt es hier http://www.agner.org/optimize/ unter "4. Instruction tables: Lists of instruction latencies, throughputs and micro-operation breakdowns for Intel and AMD CPU's" eine schöne Übersicht über die ganzen Befehle und deren Takte, uOps und was weiss der Teufel noch was... falls es jemand genauer wissen möchte.


  • Mod

    Ich habe mir erlaubt, deinen Text hauptsaechlich um die Erwaehnung des zusaetzlichen Speicherplatzverbrauchs zu erweitern (was in einem BL wichtiger sein duerfte als 1 verpulverter CPU-Takt), falls das nicht aufgefallen sein sollte... 🙂

    Sorry! Danke für die Ergänzung. Jedes Byte im ersten Sektor ist wertvoll.


  • Mod

    Ich wollte mal auf die Schnelle in den PM umschalten, macht aber einen Reset. Ich finde momentan den Fehler einfach nicht. Liegt es an GDTR/GDT, am fehlenden IDT oder am far jump?

    Vielleicht sollte ich doch auf C umsatteln? Mir gefällt Assembler aber didaktisch besser, weil es klarer ist. Kann bitte jemand nachhelfen?

    ..


Anmelden zum Antworten