Eigenes OS?


  • Mod

    Bei mir erscheinen nur gelegentlich Tipps und Hinweise auf Updates, ich finde diese Passage zur Werbung daher etwas übertrieben...

    Akzeptiert, werde dies abmildern. Möchte sachlich bleiben, hat mich aber schon ziemlich genervt.


  • Mod

    Wir haben einen noch etwas ausbaubaren Real Mode, auch im PM könnte man in Assembler noch etwas machen, aber ich denke, nun ist es an der Zeit, zu einem in C geschriebenen Kernel zu wechseln.

    Als Compiler unter Windows verwende ich den allseits bekannten Compiler DJGPP (2.03 ohne C++). Ist dies das richtige Werkzeug? (Download bei Bona Fide OS Dev.) http://www.osdever.net/downloads/compilers/DJGPP-Installer-nocpp.exe

    Dazu habe ich eine technische Frage:

    Ich habe boot.bin (aufgefüllt auf 512 Byte mit Bootsignatur) und kernel.bin (aufgefüllt auf 1024 Byte).

    Dann habe ich ein k(ernel)_entry.asm und ckernel.asm, das via k_entry.o und ckernel.o mittels linker ld zu ckernel.bin gelinkt wird.

    Dann packe ich alles zu einer einzigen Binärdatei zusammen, die dann auf Floppy geschrieben wird: copy /b boot.bin + kernel.bin + ckernel.bin MyOS.bin

    Der Bootloader lädt sich selbst nach 0x7C00 und den Rest nach 0x8000. Der gelinkte Part mit dem C-Kernel fängt durch die HLT-Auffüllung ab 0x8400 an.

    so sieht k_entry.asm aus:

    [BITS 32]
    
    [global start]
    [extern _main] ; this is in the c file
    hlt
    hlt 
    hlt
    start:
      call _main
      jmp $
    

    Ich kann im bin-Format nicht [extern start] angeben und dann nach start springen: jmp 0x8:0x8000+start

    Daher habe ich mir erst mal die drei HLT vor start gebaut, dann kann ich die Startadresse im Hexeditor hinter den "f4f4f4" leicht finden 😃 und direkt anspringen, z.B.: jmp 0x8:0x86e3; goto C-Kernel (0x8400 + 0x02e3 ist natürlich eine variable Zielscheibe).
    Finde das didaktisch interessant, weil man hier zeigen kann, wie die Zusammenhänge im Speicher ganz genau sind und warum man eine Objektdatei benötigt, denn "start" kann man nur dort als externes Label verwenden.

    Was ist hier eurer Meinung nach der sauberste Weg?
    Benötigt man überhaupt noch eine Zwischen-Datei k(ernel)_entry.asm? (16/32-Bit-Problematik beim ersten Kernel? Außerdem haben wir im ersten Kernel "org 0x8000" verwendet, was wiederum nur im Binär-Format geht)

    Gibt es irgendwie einen Trick (außer dem Spicken nach einer optischen Erkennungsmarke in der Binärdatei), dass man aus kernel.bin nach "start" springen kann, also wie gesagt "extern start" klappt nicht im Binär-Format.

    Der Sprung von _main nach _main() {...} innerhalb der Objektdateien ist natürlich problemlos.

    Wie überwindet man die Grenze bin -> obj mit einem Sprung am saubersten?

    EDIT: Inzwischen über Linker und Linkerscript gelöst.



  • abc.w schrieb:

    xor cx, cx
    

    Warum nicht einfach:

    mov cx, 0
    

    Oder noch eine Zeile:

    or cl, cl     ; zero? (start of the string)
    

    Warum nicht einfach:

    cmp cl, 0
    

    das hat nichts mit philosophie zu tun, es generiert anderen code, der andere laufzeit und performance eigenschaften hat. das ist eben der unterschied zwischen cisc und risc.
    wenn man diese nicht kennt, macht es nicht soviel sinn etwas in assembler zu schreiben, und das ist kein ding der sprache, sondern der hardware.

    auf nem cell sieht du vielleicht

    NOP
    OR R1,R1,R1
    OR R2,R2,R2
    

    und wunderst dich, wieso nicht einfach immer NOP? Philosophie? vielleicht sogar im gcc?

    (und nein, ist kein CISC, ich hab mir das ja auch nicht ausgedacht es so zu machen :P)



  • abc.w schrieb:

    Zum Beispiel einfache Zeile aus kernel.asm:

    xor cx, cx
    

    Warum nicht einfach:

    mov cx, 0
    

    weil der erste befehl keinen operanden braucht -> kürzer/schneller
    das gehört zum guten ton der assemblerprogrammierung. meistens will man möglichst wenig und möglichst schnellen code haben. na gut, bei den heutigen giga-Hz und -byte x86ern ist sowas natürlich alles egal.
    🙂



  • volkard schrieb:

    wer

    xor cx, cx
    

    verschmäht, weil es angeblich unleserlich wäre, und

    mov cx, 0
    

    vorzieht, der muß in der hochsprache auch

    ++c;
    

    meiden und immer brav

    c = c + 1;
    

    schreiben.

    volkard, vielleicht war es gestern spät bei Dir, oder, nach deiner Schlussvorlgerung zu urteilen, hast Du den Unterschied zwischen Assembler und Compiler nicht verstanden...

    rapso schrieb:

    auf nem cell sieht du vielleicht

    NOP
    OR R1,R1,R1
    OR R2,R2,R2
    

    und wunderst dich, wieso nicht einfach immer NOP? Philosophie? vielleicht sogar im gcc?

    rapso, wahrscheinlich war es bei Dir auch spät gestern. Ich kenne mich mit ner Cell CPU natürlich nicht aus und finde diese drei Befehle, so wie sie da stehen, hm, ja, hübsch. Ich vermute aber, da es keinen Sinn macht, diese drei Befehle einfach so aus dem Kontext zu entreissen und ohne "Vorgeschichte" und "Nachgeschichte" einfach so zu posten, dass Du deren Bedeutung auch nicht verstanden hast...
    Wie dem auch sei, interessant, wie es abgeht, wenn man anfängt, beim Assembler-Gefrickel mal auf den einen oder den anderen Umstand hinzuweisen. Und ich habe mich noch nicht in den ganzen Code eingelesen...
    Trotzdem unverständlich, oder vielleicht übersehe ich irgendwas? Nehmen wir den Bootloader z.B., der Zugriff auf die Festplatte ist im ms-Bereich. Sagen wir mal 10 ms. Bei einer 1 GHz CPU entsprechen die 10 ms 10 Millionen Takte. Nehmen wir an, ein CPU Takt entspricht 1 Sekunde, 10 Millionen Takte sind also 10 Millionen Sekunden. Ergibt etwa 115 Tage... Ihr habt jetzt also mit xor ax, ax einen Takt gespart. Also eine Sekunde gespart, um dann 115 Tage zu warten... Kann mich bitte jemand aufklären?



  • abc.w schrieb:

    volkard schrieb:

    wer

    xor cx, cx
    

    verschmäht, weil es angeblich unleserlich wäre, und

    mov cx, 0
    

    vorzieht, der muß in der hochsprache auch

    ++c;
    

    meiden und immer brav

    c = c + 1;
    

    schreiben.

    volkard, vielleicht war es gestern spät bei Dir, oder, nach deiner Schlussvorlgerung zu urteilen, hast Du den Unterschied zwischen Assembler und Compiler nicht verstanden...

    ach, da ist ein unterschied? bestimmt nur philosophischer natur.
    zur sache:
    xor reg,reg ist einfach ein festes idiom, das sich mit ein wenig übung sogar schneller liest, als mov reg,0
    ++var ist einfach ein festes idiom, das sich mit ein wenig übung sogar schneller liest, als var=var+1
    diese parallele versuchte ich aufzuzeigen.


  • Mod

    Volkard hat im abstrakten Sinne Recht. Es kann nicht sein, dass man performant Assembler schreiben will und dann die Lesbarkeit in den Vordergrund rückt.

    Bezüglich der Linker-Geschichte habe ich es jetzt auch endlich geschafft. Ich hatte die Reihenfolge hinter ld falsch angegeben. Konnte auch weiter vereinfachen. Nun ist alles zu meiner Zufriedenheit.

    Jetzt gibt es nur noch: bootloader, (asm)kernel and ckernel.
    asm- und C-kernel werden zusammen gelinkt. Wichtig ist, dass der asm-kernel binär vorne bei 0x8000 zu liegen kommt. Daher die Reihenfolge ld ... kernel.o ckernel.o Anders hängen die ganzen Strings vorne bei 0x8000 herum.

    linker \1:

    OUTPUT_FORMAT("binary")
    ENTRY(RealMode)
    SECTIONS
    {
      .text  0x8000 : {
        *(.text)
      }
      .data  : {
        *(.data)
      }
      .bss  :  {                
        *(.bss)
      }
    }
    

    makefile:

    all:
       nasmw -O32 -f bin boot.asm -o boot.bin
       nasmw -O32 -f aout kernel.asm -o kernel.o
       gcc -c ckernel.c -o ckernel.o
       ld -T kernel.ld kernel.o ckernel.o
       rename a.out ckernel.bin
    
       cmd /c copy /b boot.bin + ckernel.bin MyOS.bin
       partcopy MyOS.bin 0 1000 -f0
    


  • abc.w schrieb:

    Wie dem auch sei, interessant, wie es abgeht, wenn man anfängt, beim Assembler-Gefrickel mal auf den einen oder den anderen Umstand hinzuweisen.

    das nennt man 'best practices' der asm-programmierung, keine umstände. genau so, wie der c++-frickler lieber ++x statt x++ verwendet, obwohl es in vielen fällen keinen unterschied macht.
    🙂



  • +fricky schrieb:

    das nennt man 'best practices' der asm-programmierung, keine umstände. genau so, wie der c++-frickler lieber ++x statt x++ verwendet, obwohl es in vielen fällen keinen unterschied macht.
    🙂

    Best practices... Lese grade das hier http://www.win.tue.nl/~aeb/linux/kbd/A20.html

    ...However, it failed to do this address truncation (a bug), and people found that there existed programs that actually depended on this truncation.

    Bestimmt hat einer damals beim Programmieren der 8086er gedacht, wozu in meinem Programm explizit eine Abfrage einbauen, wenn die CPU von selbst eine "address truncation" macht und ich so Takte sparen und dabei perfomant auf die unteren Adressen zugreifen kann... Gängige Praxis, eine Funktion nutzen, die irgendwas vollkommen anderes macht, das eine oder andere Zwischenergebnis in einem ganz bestimmten Sonderfall erzeugt, an den man normalerweise nicht denkt, dessen Zwischenergebnis ich dann doch in meinem Programm benutze und mich darauf verlasse.


  • Mod

    Also langsam wird's mir unheimlich mit Assembler. 😃
    Ich habe den Sprung zum "rettenden" C-Kernel nun mit einer rudimentären Ausgabe realisiert:

    http://www.henkessoft.de/OS_Dev/OS_Dev1.htm#mozTocId397878 (Text)
    http://www.henkessoft.de/OS_Dev/Downloads/20090326_eh_os.zip (Dateien; gcc/ld voraus gesetzt)
    Download DJGPP: http://www.osdever.net/downloads/compilers/DJGPP-Installer-nocpp.exe

    Nun haben wir bootloader, real mode (asm), protected mode (asm) und C als bare bone Gerüst. In C fühle ich mich doch gleich wohler. 😉 Erstmal vielen Dank, dass ihr mich bis hierher begleitet habt. Die weitere Entwicklung in C, die natürlich noch signifikant mit Assembler durchsetzt sein wird, passt eigentlich nicht in dieses Sub-Forum. 😕



  • Erhard Henkes schrieb:

    Die weitere Entwicklung in C, die natürlich noch signifikant mit Assembler durchsetzt sein wird, passt eigentlich nicht in dieses Sub-Forum. 😕

    Das wird dir niemand übel nehmen, dieser Thread ist das Beste seit langem. Weitermachen!



  • Professionelle Meinung schrieb:

    Das wird dir niemand übel nehmen, dieser Thread ist das Beste seit langem. Weitermachen!

    jup. muß ich auch mal sagen.
    gut.



  • volkard schrieb:

    Professionelle Meinung schrieb:

    Das wird dir niemand übel nehmen, dieser Thread ist das Beste seit langem. Weitermachen!

    jup. muß ich auch mal sagen.
    gut.

    ^^ich schliesse mich dem an, obwohl ich bisher nur am lästern war.
    🙂



  • abc.w schrieb:

    rapso, wahrscheinlich war es bei Dir auch spät gestern.

    waere es nicht einfacher und auch zielgenauer den fehler bei sich selbst statt bei allen anderen zu suchen?

    Ich kenne mich mit ner Cell CPU natürlich nicht aus und finde diese drei Befehle, so wie sie da stehen, hm, ja, hübsch.

    jap, genau das wollte ich damit verdeutlichen. du scheinst wenig ahnung zu haben und deswegen weichst du in philosophie aus, wie jemand der zum ersten mal in der schule von zahlen hoert und sich fragt, ob es nicht einfacher waere mit einem 8ter system, statt die daumen mit zu nutzen.

    Ich vermute aber, da es keinen Sinn macht, diese drei Befehle einfach so aus dem Kontext zu entreissen und ohne "Vorgeschichte" und "Nachgeschichte" einfach so zu posten, dass Du deren Bedeutung auch nicht verstanden hast...

    Es ist fast lustig dass du erst zugibst nicht zu wissen was diese befehle an sich haben, somit nichtmal weisst, dass jeder einzelne schon den ganzen kontext hat den man braucht um zu verstehen wozu sie dienen, aber vermutungen anstellst dass ich das nicht verstehe.

    Wie dem auch sei, interessant, wie es abgeht, wenn man anfängt, beim Assembler-Gefrickel mal auf den einen oder den anderen Umstand hinzuweisen. Und ich habe mich noch nicht in den ganzen Code eingelesen...

    klar, jemand der scheinbar nicht viel erfahrung hat wird natuerlich erstmal verwundert schauen was das alles soll und dass es nicht so laeuft wie es in seinem unerfahrenen und unbefangenen dasein als ideal gilt.
    jemand mit ahnung, der ein mox eax,0 statt xor eax,eax sieht, wird sich dagegen wundern und feststellen, entweder
    1. wurde das von nem anfaenger programmiert
    2. es hat einen trifftigen grund nicht der normalen vorgehensweise zu folgen, wollte er flags nicht veraendern? wollte er ein spezielles code allignment? wollte er eine spezielle code groesse? ueberschreibt er den code spaeter mit was anderem und schreibt deswegen erstmal 0 rein?

    es passiert also genau das gegenteil was du eigentlich bezwecken wolltest, statt es leserlicher zu machen, wirst du jedem erfahrenen programmierer einen stolperstein stellen.


  • Mod

    @ Professionelle Meinung, volkard, +fricky:
    Danke für das positive Feedback. Das wird mich sicher beflügeln, das bisherige Mini-OS nun mit C weiter auszubauen und bei entscheidenden Punkten hier nachzufragen. 🙂

    Didaktisch wird der Video-RAM erklärt:
    http://www.henkessoft.de/OS_Dev/OS_Dev1.htm#mozTocId483279

    und anschleißend die Funktionen des C-Kernels zerpflückt, damit herum gespielt und dann optimiert.
    http://www.henkessoft.de/OS_Dev/OS_Dev1.htm#mozTocId958376

    Mich würde auch eure Meinung zum kurzen Abschnitt "Gerätetreiber" interessieren:
    http://www.henkessoft.de/OS_Dev/OS_Dev1.htm#mozTocId220571

    Eine Design-Frage ist, was man "funktionell" in C-Module zusammen packt und welche Funktionen man in der Erstausbau-Stufe benötigt. Ich möchte nichts übertreiben, damit niemand den Überblick verliert, sonst kann ich meine Leser gleich zu Linux 0.01 schicken.



  • @rapso: Wollte an dieser Stelle ein Paar Kommentare abgeben, ich denke, ich lasse es lieber sein. Wir haben unsere Meinungen gesagt und jeder kann sich über das Gesagte eigene Meinung bilden.

    Ich versuche grade verzweifelt, mit meinen Werkzeugen mingw-gcc und dem Linker, der dabei ist, den Kernel zu kompilieren. Es geht leider nicht. Bekomme Fehlermeldung (habe --verbose beim Linker angegeben, damit die Ausgabe ausführlicher wird):

    C:\BochsMyOS\20090326_eh_os>make
    c:\nasm-2.06rc6\nasm.exe -O32 -f bin boot.asm -o boot.bin
    c:\nasm-2.06rc6\nasm.exe -O32 -f aout kernel.asm -o kernel.o
    gcc -c ckernel.c -o ckernel.o
    ld -T kernel.ld kernel.o ckernel.o --verbose
    GNU ld version 2.17.50 20060824
      Supported emulations:
       i386pe
    using external linker script:
    ==================================================
    OUTPUT_FORMAT("binary")
    ENTRY(RealMode)
    SECTIONS
    {
      .text  0x8000 : {
        *(.text)
      }
      .data  : {
        *(.data)
      }
      .bss  :  {
        *(.bss)
      }
    }
    
    ==================================================
    attempt to open kernel.o succeeded
    opened script file kernel.o
    kernel.o: file not recognized: File format not recognized
    make: *** [all] Error 1
    

    Anscheinend kennt der mingw Linker den aout Format nicht. 😞
    Kennt jemand einen Workaround?


  • Mod

    Anscheinend kennt der mingw Linker den aout Format nicht. 😞 Kennt jemand einen Workaround?

    Guter Hinweis. Ich verwende gcc 3.1 (im DJGPP-Paket)
    http://www.osdever.net/downloads/compilers/DJGPP-Installer-nocpp.exe , weil diese Version bei Bona Fide zum Download angeboten wird.

    Ich wusste, dass mit C das Gewurschtel mit den Tools los geht, aber dafür ist das Programmieren jetzt deutlich einfacher.


  • Mod

    Wie kann man den gcc umstellen auf Intel Syntax? War das -masm=intel beim gcc?

    Wie heisst das dann korrekt in Intel Syntax? 😃

    inline void outportb(unsigned int port,unsigned char value)
    {
        asm volatile ("outb %%al,%%dx"::"d" (port), "a" (value));
    };
    


  • Ich glaube so einfach geht das nicht.
    Allerdings sollte der NASM mit aout zurechtkommen (fette Zeile).

    Aus der DJGPP-FAQ:
    Unter Punkt 17.1 werden einige Problembehandlungen aufgezeigt. Dann:

    17.2 Converting between Intel ASM syntax and AT&T syntax

    Q: Where can I find an automated conversion tool to convert my Intel-style assembly code into a code acceptable by Gas?
    Q: Where can I find a converter from AT&T assembly to Intel style?
    A: A SED script which should do most of the conversion was posted to the {DJGPP news group}.

    A program called TA2AS which can convert TASM assembly source to AT&T style can be found {on the DJGPP server} and {on Oulu}. TA2AS was written by Frank van Dijk of the Spirit group; if you have questions about this tool, you may contact {Jan Oonk}. The authors say that the program is far from finished, but the sources are available with the package so you can fix whatever is broken for your needs.

    Another similar converter is Intel2Gas, available from its {Web page}.

    Beginning with Binutils 2.10, Gas has an option that causes it to accept the Intel syntax, so you can use Gas to assembly Intel-style code.

    Alternatively, here is what you can do to make your code linkable with DJGPP programs:
    * Get and install NASM, a portable x86 assembler which supports most of the Intel syntax and can generate DJGPP-compatible COFF object files (as well as lots of other formats, such as Microsoft 16-bit OBJ and Win32, a.out, and ELF). It also supports Pentium and Pentium Pro opcodes, and MMX. NASM is free for non-commercial use (see the docs for commercial use restrictions) and can be compiled with DJGPP. NASM can be found {on NASM Web site}, which has links to official download sites. The maintainers of NASM are {Jules} and {H. Peter Anvin}.

    Be warned that NASM is not 100% identical to MASM or TASM. Even experienced assembly programmers might need some time to adapt to the slightly different flavor of NASM. If you want something much more similar to TASM, get JAS. JAS is available {from OULU}.

    Also note that NASM, or at least some of its versions, doesn't produce debug info in the format understood by GDB, which makes debugging NASM-assemblied code tedious (you might not be able to display source lines and refer to local symbols by name). Latest versions of NASM might correct this deficiency.
    * For a small number of relatively short files, consider converting them with a smart editor (like Emacs or its work-alikes).
    * Obtain a copy of Microsoft MASM 6.11. It has a -coff option to generate object code in COFF format which can be submitted to GCC, so you can compile your original source. You can also use the LIB32 librarian from Microsoft C8 to convert object files to COFF by putting them into a .lib library, then extracting them as COFF files. {28} Note that, unless you link the MASM-generated object files with DJGPP's ld (as opposed to Microsoft's LINK /CO command), you won't be able to debug the resulting program, because the debug info is not in correct format. I'm also told that masm doesn't produce sections named ".text" and ".data", so you might need to hex-edit the section names in the object file manually.
    * Use a disassembler to disassemble the object code, then convert it to the AT&T format either by hand or using TA2AS. One place to look for such a disassembler is {on SimTel.NET mirrors}.
    Keep in mind that syntax is only one of the aspects of converting code written for DOS to DJGPP. You should also make sure your code doesn't violate any of the rules for protected-mode programming (see {GPF in asm code}).

    If you need to perform the opposite conversion, from the AT&T style to the Intel style, try the Att2Intl converter written by {Gregory Velichansky}. Its output is intended for NASM or TASM. Att2Intl is available {from Greg's home page}.

    Der komplette Punkt 18 der FAQ mit Informationen über low-level-programming könnte auch noch die eine oder andere wertvolle Information beinhalten.
    Wenn da noch die eine oder andere Komponente vom DJGPP fehlt, ich hab da noch Unmengen von Resourcen rumliegen.


  • Mod

    -- verbose ist ein guter Hinweis. Bei mir sieht dies so aus, noch ein problem mit partcopy.exe, aber es läuft alles:

    G:\OSDev\Test\13 C>make
    nasmw -O32 -f bin boot.asm -o boot.bin
    nasmw -O32 -f aout kernel.asm -o kernel.o
    gcc -c ckernel.c -o ckernel.o
    ld -T kernel.ld kernel.o ckernel.o --verbose
    [b]GNU ld version 2.13[/b]
      Supported emulations:
       i386go32
    using external linker script:
    ==================================================
    OUTPUT_FORMAT("binary")
    ENTRY(RealMode)
    SECTIONS
    {
      .text  0x8000 : {
        *(.text)
      }
      .data  : {
        *(.data)
      }
      .bss  :  {
        *(.bss)
      }
    }
    
    ==================================================
    attempt to open kernel.o succeeded
    kernel.o
    attempt to open ckernel.o succeeded
    ckernel.o
    rename a.out ckernel.bin
    cmd /c copy /b boot.bin + ckernel.bin MyOS.bin
    boot.bin
    CKERNEL.BIN
            1 Datei(en) kopiert.
    partcopy MyOS.bin 0 1000 -f0
    Failed to read source at offset 800make.exe: *** [all] Error -1
    

    Schwachstellen: letzte Zeile (jemand eine Idee?), und ich müsste ckernel.bin vorher löschen, damit a.out umbenannt werden kann. Gibt es da einen Parameter für rename, das auf jeden Fall zu machen, auch, wenn ckernel.bin existiert?

    Für das Problem von abc.w habe ich momentan keine Lösung, ich war schon froh, dass ich selbst das Linken geschafft habe. Aber vielleicht schaffen wir es gemeinsam, eine möglichst allgemeingültige Lösung zu finden.


Anmelden zum Antworten