Eigenes OS?


  • Mod

    Die Idee ist o.k. Ich wollte den Namen aber noch ändern können, ist nur der Entwicklungs-Deckname. 😉
    Habe mich vorerst für os.h entschieden.


  • Mod

    Ich benötige eure Unterstützung, weil ich die Lücke nicht finde. 🙄

    Exceptions und Timer Interrupt IRQ 0 gehen.
    Wenn ich den keyboard_handler (polling geht ja bestens) zum Testen auf den IRQ0 (18,222 Ticks/sec) umlege, funktioniert er wie beabsichtigt.

    Allerdings kommt der Keyboard Interrupt IRQ1 nicht an. 😕 EDIT: Er wird nicht ausgelöst, weil der Tastatur-Puffer nicht leer ist (das berühmte "flushen" hat mich wieder eingeholt).

    Hier der gesamte - noch nicht funktionierende - Sourcecode: http://www.henkessoft.de/OS_Dev/Downloads/14 C Test IRQ1 funktioniert nicht.zip

    Ich denke, dass der C-Code für die Interrupt-Behandlung in Ordnung ist, habe ihn mehrfach gecheckt. Vielleicht liegt es am Assemblercode isr.asm (Stack?) oder am Linker-Script? Hier ein Screenshot vom OS (timer IRQ geht, habe ihn auf eine Minute im Bildschirm-Ausdruck eingestellt, Zeit stimmt unter Bochs aber nicht): http://www.henkessoft.de/OS_Dev/Downloads/IRQ_Test.PNG

    Ich hoffe, das jemand den blöden Fehler bezüglich IRQ1 und vielleicht auch der anderen (welchen kann man am einfachsten testen?) findet, damit wir rasch weiter kommen. Wie gesagt, IRQ0 geht bestens (einfach mal auf key_handler umlegen).
    Wenn man mit

    __asm__("int $0x21");
    

    im Kernel Programm main()
    den IRQ1 (--> 33 = 0x21) selbst auslöst, kommt auch der keyboard_handler ins Spiel.

    Das heißt, warum wird der IRQ1 nicht weiter geleitet? (IDT Problem?)

    @Nobuo T, abc.w, Bitsy, +fricky et.al.:
    Lasst mich bitte nicht hängen! Wer diesen Mist-Fehler findet, wird im Tutorial extrem gelobt. EDIT: Hauptproblem inzwischen gelöst, aber ich würde mich freuen, wenn ihr trotzdem mal über den Code schaut. Das Thema IDT, IDTR, ISR, Exceptions, ... ist wichtig.

    EDIT: In einem anderen Forum habe ich erfahren, dass man den Tastatur-Puffer leeren muss, damit neue Interrupts gesendet werden. Klingt logisch. Daher habe ich einfach folgendes gemacht:

    Ein einmaliges

    __asm__("int $0x21");
    

    reicht bereits aus, um den Prozess zu starten.
    Soll ich ein

    keyboard_init(){__asm__("int $0x21");}
    

    schreiben, das ich einmal zu Beginn der main() aufrufe??

    Es gibt noch eine zweite - evtl. bessere - Lösung:

    void keyboard_init()
    {
        /* Wait until buffer is empty */
        while (inportb(0x64) & 0x01)
          inportb(0x60);
    
        // __asm__("int $0x21"); // ? 
    };
    

    Hier ist jetzt der funktionierende Code, allerdings alles noch im Experimentierstadium:

    http://www.henkessoft.de/OS_Dev/Downloads/14_C_Test_IRQ1_funktioniert.zip

    Welchen Interrupt würdet ihr nach IRQ0 (Zeit-Ticks) und IRQ1 (Tastatur) als nächstes probieren?

    Speichermanagement, Multitasking, ... warten. Allerdings muss ich zugeben, dass das gesamte Thema OSDEV ziemlich knifflig ist und man die Tools wirklich beherrschen muss. meine Tool-Schwachpunkte sind AT&T Syntax und Linker-Skript. Der verwendete DJGPP mit gcc 3.1 ist auf jeden Fall die richtige Wahl.



  • Probiere mal irgendeinen von 3-8, wobei man das anzusteuernde Device auch auf einen der oberen Interrupts legen können sollte, und dann kümmere Dich um den 2er, weil Du den brauchst, wenn Du an die oberen 8 ran willst. Wenn das Gerät dann auch z.B. mit dem 10er ansprechbar ist, stehen überhaupt erst mal alle IRQs zur Verfügung! Also - erst eine einfache Interruptgeschichte 'unten', und dann schauen, dass sie auch 'oben' läuft.

    Wenn der 2er Probleme macht - ich sollte noch einen Sourcecode haben, bei dem ich eine vierfache Serielle angesteuert habe, wobei der letzte Kanal eben nur über IRQ 10 erreichbar war. Die waren damals sehr dankbar, dass es überhaupt mal ein Stück Software gab, mit dem man den überhaupt ansprechen konnte 😉

    Freut mich übrigens, dass der 3er DJGPP soweit funktioniert - hatte Dir ja in der Mail die Problematik erklärt.


  • Mod

    @Bitsy:
    Das sind die Mechanismen, die versuchsweise eingebaut sind, muss aber noch prüfen, ob alles wirklich funktioniert, bisher wurden ja nur IRQ0 (clock) und IRQ1 (keyboard) behandelt:

    ..

    Daher kann ich mit

    __asm__("int $0x21"); // 0x21 = 33, gemappt auf IRQ1 (Keyboard)
    

    den Keyboard-Interrupt selbst anstoßen.

    Das mit den Interrupts finde ich ganz lustig. Die Keyboard-Ports und der Keyboard-Puffer haben mich fast aus der OS-Kreisbahn geworfen, weil man diesbezüglich viel unausgegorenes und verwirrendes Zeug im Internet findet.

    @Bitsy: Hast Du eine Idee, wie man das Linker-Problem (mit dem a.out Format) des mingw überwinden könnte? abc.w präferiert diese Toolchain. 😕



  • Bezüglich Linker-Problem mit mingw würde mich zwar interessieren, wie man es irgendwie hinkriegen könnte. Aber, wenn es nicht geht, aus welchen Gründen auch immer, ist auch nicht schlimm. Ich habe ja noch DJGPP.
    Ich habe mir den Quellcode 14_C_Test_IRQ1_funktioniert geholt und ausprobiert. Eines stört mich jetzt ein wenig. Zunächst, auf der einen Seite haben wir Assembler-Dateien, die mit nasm assembliert werden. Auf der anderen Seite gibt es c-Dateien, die aber hie und da eine Funktion mit Assembler Inline-Code enthalten. Das ist irgendwie nicht konsistent. Einmal nasm, dann gcc, dann inline-gcc. Wäre vielleicht sinnvoll, wenn c-Datei, dann C, wenn Assembler-Datei, dann Assembler nasm. Und die Funktionen mit Assembler Inline-Code rein in Assembler implementieren? Nur als Vorschlag. Optimal wäre natürlich, statt nasm GNU as zu verwenden, weiss aber jetzt nicht genau, ob man 16-Bit Assembler Quellcode mit as assemblieren kann. Vorteil davon wäre noch, dass man nur ein Tool bräuchte: DJGGP. GNU as ist nämlich in DJGPP mitenthalten.



  • Eine andere Sache, wollte ein Paar Ausnahmen provozieren mit diesem Code:

    .global _k_zero_divide
    .global _k_undefined_op
    .global _k_null_pointer
    
    .section .text
    
    _k_zero_divide:
        movl $0, %eax
        div %eax
        ret
    
    _k_undefined_op:
        ud2
        ret
    
    _k_null_pointer:
        movl $0, %eax
        movl %eax, (%eax)
        ret
    

    Division durch 0 und undefinierter Op 👍
    Die Funktion k_null_pointer() wir aber ausgeführt und das System läuft 😕 Oder ist Zugriff mit Offset Null ok 😕



  • Erhard:
    Irgendwie habe ich inzwischen ein wenig den Ueberblick verloren. Kannst du mal irgendwie ein Verzeichnis oder eine Seite einrichten, wo man waehrend der Entwicklung immer mal die aktuellen Codes runterladen kann?

    Bei deinem ersten KB-Polling hat mich ehrlich gesagt schon etwas gewundert, dass das ueberhaupt funktionierte. Klassischerweise macht man das AFAIR so, dass man einen Handler fuer IRQ 1 installiert. Dort checkt man erstmal, ob etwas im input buffer ist (port 64h, Bit0). Falls was dran liegt, port 60h auslesen und irgendwie verarbeiten (scancodes oder evtl. sonstige spezial-codes).
    Zum Abschluss immer ACK an das keyboard, indem ein reset ausgefuehrt wird: Kurz aus- und wieder einschalten ueber Port 61h, Bit7. Zum Ende natuerlich noch EOI an den PIC. Ist dann zwar immer noch ziemlich primitiv, sollte so aber zumindest keine Probleme geben...

    abc.w:
    Ich kann nur vermuten, dass einfach (noch?) kein Paging aktiviert ist, mit dem eine Ausnahme beim Zugriff auf einen 0-Pointer ausgeloest wuerde. An der physikalischen Adresse 0 ist nun mal einfach normaler RAM, also an sich kein Problem.


  • Mod

    nasm, dann gcc, dann inline-gcc. Wäre vielleicht sinnvoll, wenn c-Datei, dann C, wenn Assembler-Datei, dann Assembler nasm. Und die Funktionen mit Assembler Inline-Code rein in Assembler implementieren?

    Du hast völlig Recht. Die bisherige "gemischte" Lösung liegt an meinen Problemen mit der AT&T Syntax. Auf der einen Seite ist es wichtig, dass Einsteiger sehen, wie einfach man zwischen C- und ASM-Code hin und her springen kann. Man benötigt nur global, extern und den führenden Unterstrich. Auf der anderen Seite führt dies zu einem Hin- und Hergehüpfe. Ich werde daher versuchen, Deine Idee aufzunehmen. Vielleicht kann Nobuo T da etwas unterstützen. 🙂

    ... statt nasm GNU as zu verwenden, weiss aber jetzt nicht genau, ob man 16-Bit Assembler Quellcode mit as assemblieren kann. Vorteil davon wäre noch, dass man nur ein Tool bräuchte: DJGGP. GNU as ist nämlich in DJGPP mitenthalten.

    Habe mir die Syntax von as bisher noch nicht angeschaut. AT&T?

    Kannst du mal irgendwie ein Verzeichnis oder eine Seite einrichten, wo man waehrend der Entwicklung immer mal die aktuellen Codes runterladen kann?

    @Nobuo T:
    Ja, wird sofort gemacht: Den letzten Code erhält man ab jetzt immer unter diesem Link (wurde aber im Tutorial noch nicht beschrieben, weil er sich im Entwicklungs-/Testzustand befindet; daher sind gute Ideen - wie oben von abc.w zum Thema Exception - immer gerne gesehen. Bitte ausreichend kommentieren.):

    ➡ ➡ ➡ Link zur jeweils aktuellsten Test-Version:
    http://www.henkessoft.de/OS_Dev/Downloads/PrettyOS_last_version.zip

    Was abc.w anspricht ist mein Hin- und Herhüpfen zwischen C-Code (vor allem zum Thema Interrupts) und isr.asm.


  • Mod

    Die Funktion k_null_pointer() wird aber ausgeführt und das System läuft 😕 Oder ist Zugriff mit Offset Null ok 😕

    Ich kann nur vermuten, dass einfach (noch?) kein Paging aktiviert ist, mit dem eine Ausnahme beim Zugriff auf einen 0-Pointer ausgeloest wuerde. An der physikalischen Adresse 0 ist nun mal einfach normaler RAM, also an sich kein Problem.

    Stimmt, Paging wurde noch nicht aktiviert. Das gehört sinnvollerweise als Vorteil zu Multitasking, wenn ich das richtig sehe.

    Klassischerweise macht man das AFAIR so, dass man einen Handler fuer IRQ 1 installiert.

    Richtig, klassischerweise. Aber es geht eben auch ohne Interrupt. Dieses ineffiziente Polling wollte ich zuerst zeigen, damit die Notwendigkeit der Interrupts verständlich wird.

    Diese beiden Punkte bestätigen mich in meiner Vorgehensweise. Ich versuche die didaktische Reihenfolge im Tutorial so aufzubauen, wie man Funktionen/Strukturen in ASM oder C für die beabsichtigte Umsetzung wirklich benötigt. Also pyramidal.

    Die bisherige Reihenfolge war deshalb:

    - Bootbarer Minikernel
    - Bootloader + nachgeladenem Kernel
    - Auf Instruktionen im RM reagieren (dank BIOS einfache Handhabung)
    - A20-Gate und PM aktivieren, GDT/GDTR (das gehört wohl als Minimum zusammen)
    - Sprung vom ASM- zum C-Kernel
    - Rudimentäre Textausgabe auf Bildschirm
    - Rudimentäre Texteingabe mit Tastatur
    - IDT/IDTR, Interrupts, Exceptions allgemein
    - IRQ 0 (System Clock Tick jede 18,22 sec)
    - IRQ 1 (Keyboard, durch Port 0x60 u. 0x64 etwas komplizierter)
    - ... (immer spannend halten) 😉

    Ich kann nur hoffen, dass dieses Konzept sinnvoll ist. 😕

    Was würdet ihr als notwendige nächste "Blöcke" auf die Pyramide wuchten?



  • Erhard Henkes schrieb:

    Klassischerweise macht man das AFAIR so, dass man einen Handler fuer IRQ 1 installiert.

    Richtig, klassischerweise. Aber es geht eben auch ohne Interrupt. Dieses ineffiziente Polling wollte ich zuerst zeigen, damit die Notwendigkeit der Interrupts verständlich wird.

    ehrlich gesagt, würde ich als noob nicht einsehen, wozu man einen dedizierten keyboard-interrupt braucht, wenn man die tastatur auch pollen kann (auch wenn ich verstanden habe, wie interrupts funktionieren). warum sollte polling ineffizient sein? so lange ich die tastatur häufig genug polle, so daß ich mit der tastenfrequenz eines schnellschreibers klar komme, ist die welt doch in ordnung. und wenn mir einer erzählen würde, dass die cpu beim pollen sinnlos zyklen verballert, würde ich antworten: "na und? dann polle ich eben in der timer-isr. ca. 18 anschläge/s als samplingrate sollte doch reichen".
    🙂



  • Erhard Henkes schrieb:

    Was würdet ihr als notwendige nächste "Blöcke" auf die Pyramide wuchten?

    schwer zu sagen. der vollständigkeit halber vielleicht weitere hardwarekomponenten eines 0815-pc's behandeln, wie z.b. ansteuerung/benutzung der RTC, des piezo-piepsers, bustreiber bauen usw. allerdings besteht die gefahr, dass das alles langwierig, kompliziert und langweilig werden kann. vielleicht, um die spannung zu erhalten, das os soweit ausbauen, dass die erste, einfache anwendung laufen kann, z.b ein tetris clone oder so.
    🙂



  • Naja, PIT (und damit auch den speaker) und PIC vielleicht mal kurz anschneiden. Das sollte zur Standard-Peripherie IMHO dann aber wirklich erstmal reichen. Was die OS-Thematik betrifft hast du ja bis jetzt eigentlich wirklich nur ganz vorsichtig an der Oberflaeche gekratzt, weitere Nebensaechlichkeiten wie die RTC oder irgendwelche BUS-Geschichten sollte man da IMHO vermeiden.

    Mein Vorschlag waere als Anschluss an deine Pyramide dann mal langsam in die abstrakteren, eigentlich interessanten Gefilde des OS-Designs einzusteigen. Da ist das Grundlegenste als naechstes vermutlich die dynamische Verwaltung des RAM: Knappe Einfuehrung in die Problematik und Vorstellung einfacher Loesungsansaetze wie Unterteilung des Speichers in Bloecke statischer Groesse (vielleicht praktischerweise gleich 4KB gross 😉 ) und Verwaltung in statischen Arrays, Bitmaps oder dynamischen Free-Lists, Reservierungsstrategie next fit, evtl. ganz knapp noch Wiedereingliederungsproblematik (das weiter auszuwaelzen waere dann IMHO wirklich eher Stoff fuer ein richtiges Buch).
    Weitermachen kannst du dann evtl. mit einer knappen Einfuehrung in Paging. Das spielt schliesslich eigentlich auf fast jeder Multitaskingmaschine eine Rolle.

    Danach koenntest du dich dann vorsichtig an den Komplex Programme, Prozesse und Multitasking herantasten, mit einem knappen Ueberblick zum Thema Prozesserzeugung und Kontextwechsel, Scheduling und Verdraengung.
    Dann vielleicht quasi zum Abschluss noch einen Abstecher zu Privilegien, Adressraumtrennung/Schutzmechanismen und Micro- vs Macro-Kern sowie IPC.


  • Mod

    "na und? dann polle ich eben in der timer-isr. ca. 18 anschläge/s als samplingrate sollte doch reichen".

    EDIT: Die korrekte Frequenz wurde eingefügt, um keinen Verwirrung zu stiften. (thanks to +fricky)
    Der Timer "feuert" mit einer Frequenz von 1193182 Hz / 65536 = 18,2065 Hz. Wenn man nichts anderes machen will als Tippen, ist das Polling sicher o.k. 😃
    Dann stößt man den Keyboard_handler einfach beim Timer_handler mit an. Ich habe das in der Tat genau so gemacht, als ich blöderweise den Tastaturpuffer nicht gelöscht hatte und dadurch kein IRQ1 kam, um zu sehen, ob der Keyboard_handler korrekt funktioniert. Ging tadellos. Von daher könnte man ein IRQ0-gesteuertes Keyboard-Polling als Alternative in Betracht ziehen.
    Wirklichen Sinn macht es aber keinen, weil der Interrupt am Master PIC bereits fest vergeben ist. 😉


  • Mod

    Ich fasse mal die wirklich interessante Taskliste von Nobuo T zusammen:

    - PIC (die beiden PICs wurden bereits beim remapping von IRQ 0-15 verarbeitet)
    - PIT (programmable interval timer)
    - Speaker (ja, BEEP (frequence)! Seit Win NT kaputt wegen Behinderung.)
    - (video.c vernünftig ausbauen, bisher nur rudimentär)
    - OS-Thematik (generell)
    - Dynamische RAM Verwaltung:
    - Unterteilung des Speichers in Blöcke statischer Groesse (4KB)
    - Verwaltung (stat. Arrays, Bitmaps, dynam. Free-Lists)
    - Reservierungsstrategie next fit
    - Wiedereingliederungsproblematik
    - Paging
    - Programme & Prozesse
    - Multitasking vs Singletasking
    - Prozesserzeugung, Kontextwechsel, Scheduling, Verdrängung
    - Privilegien / Schutzmechanismen / Adressraumtrennung
    - Micro- vs Macro-Kernel
    - inter-process communication

    Das wird aber noch einige Zeit brauchen, denn ich möchte dies ja auch praktisch fassbar machen.

    Kleines Übersichtsbild aus Internet:
    http://ezs.kr.hs-niederrhein.de/TreiberBuch/html/os_aufbau.png


  • Mod

    @Nobuo T: Könnte man die isr.asm komplett in die C-Module packen? Ich schaffe das leider syntax-mäßig nicht. Mir gefällt es eigentlich auch so, hier wurde aber mehr Ordnung verlangt. Wie siehst Du das?



  • Ich glaube, der vorrangigste Punkt wäre nun eine Diskussion über die Architektur des OS an sich - und ob und wie solch ein 'Wunsch-OS' auf dem PC realisierbar ist.

    Ich erinnere dabei an den absolut linearen Speicher eines guten alten 68000er-Systems, bei dem z.B. dem Videochip (des Atari) einfach zwei Adressen für den physikalischen und den logischen Bildschirmspeicher mitgeteilt wurden.
    Da gibt es einen massiven Unterschied zum Realmode-PC.
    (Über die Wichtigkeit eines VBL-interrupts möchte ich mich hier gar nicht auslassen - scheinen einige Grafikkartenhersteller immer noch nicht begriffen zu haben.)

    An dieser Stelle spielt nämlich durchaus bereits DMA mit rein.

    Ich möchte die Komplexität an einem kleinen Beispiel festmachen:
    Nehmen wir mal an, ich möchte einen virtuellen Synthesizer mit dem Rechner realisieren. (Etwas, was auf dem Atari kein Problem war, unter DOS gerade noch ging, und unter Windows schon unter die höheren Künste der Programmierung fällt).
    Da gibt es nämlich ein kleines Latenzproblem, dass je nach OS immer größer wird.
    Wir haben einmal ein MIDI IN (könnte ja auch was anderes sein), also einen seriellen Stream, der mir Daten über gewünschte Änderungen (im Sound) liefert.
    Die Software muss nun hingehen, und das Sample berechnen - und diese Daten so schnell wie möglich in den Speicher schreiben, und zwar - und nun kommt's - unmittelbar vor den derzeitigen Lesepunkt des Sound-DMAs!
    Wenn ich - wie ab dem XP - eine 'geschützte' Hardware habe, bin ich auf ein OS angewiesen, was mir entsprechende Funktionen zur Verfügung stellt, die mir solche Aktionen erlauben. Fehlt sowas, kann der Rechner vielleicht wunderbar zig Medien abspielen, aber für diese Anwendung ist er unbrauchbar.

    Oder nehmen wir den Fall Robotik:
    Da wäre es sinnvoller der Applikation die Masse der Rechenzeit zu geben, weil es einfach wichtig ist, dass nichts durch die Lichtschranke flutscht, anstatt nachzuschauen, was gerade im Internet los ist... Also - dynamische konfigurierbare Prioritäten? Sollten vielleicht auch schon im OS stecken.



  • Erhard Henkes schrieb:

    Könnte man die isr.asm komplett in die C-Module packen?

    musstu die doku deines compilers lesen. oft muss man isr's mit einem pseudo-keyword '__interrupt' beginnen. machmal geht's mit einer #pragma-anweisung. ist halt compiler-abhängig.
    🙂

    Bitsy schrieb:

    Da gibt es nämlich ein kleines Latenzproblem, dass je nach OS immer größer wird.
    Wir haben einmal ein MIDI IN (könnte ja auch was anderes sein), also einen seriellen Stream, der mir Daten über gewünschte Änderungen (im Sound) liefert.
    Die Software muss nun hingehen, und das Sample berechnen - und diese Daten so schnell wie möglich in den Speicher schreiben...

    MIDI ist doch ein sau-langsamer bus (32 kbits/s oder so), da kann der computer kaum die bremse sein. eher haste delays, weil der bus selber zu lahm ist. gibt's nicht schon was aktuelleres, als dieses steinzeit-protokoll?
    🙂



  • Erhard Henkes schrieb:

    "na und? dann polle ich eben in der timer-isr. ca. 18 anschläge/s als samplingrate sollte doch reichen".

    Da der Timer alle 18,2 ms mit IRQ0 Alarm schlägt

    wieso haste ihn so schnell eingestellt? normerweise tickert der mit 18.2Hz, nicht kHz.
    🙂


  • Mod

    @+fricky: Ja, Du hast völlig Recht, das habe ich durcheinander gebracht:

    By default, this channel of the timer is set to generate an IRQ0 18.222 times per second.

    Habe das oben korrigiert, um niemanden zu verwirren.

    Im Sourcecode timer.c läuft das zur Zeit so (muss in Bochs nicht exakt stimmen):

    #include "os.h"
    
    int timer_ticks = 0;
    
    /* timer fires 18.222 times per second.*/
    void timer_handler(struct regs* r)
    {
        ++timer_ticks;
        static int z=10;
        if ((timer_ticks % 1093) == 0) // 1093 = 60*18.222
        {
            k_printf("One minute has passed", ++z, 0x0B);
        }
    }
    
    void timer_wait(int ticks)
    {
        unsigned long eticks;
        eticks = timer_ticks + ticks;
        while(timer_ticks < eticks);
    }
    
    void timer_install()
    {
        /* Installs 'timer_handler' to IRQ0 */
        irq_install_handler(0, timer_handler);
    }
    

  • Mod

    dynamische konfigurierbare Prioritäten?

    Das ist ein interessantes Thema. Aber da wird noch viel Code den Compiler durchfließen, bevor ich da anlange.

    Momentan denke ich darüber nach, welche völlig neuen Impulse man setzen könnte, aber zuerst muss man das alte praktisch nachempfinden, sonst fehlt das Verständnis. Die Verschaltung der Hardware macht ja auch gewisse Vorgaben, die man leider akzeptieren muss.


Anmelden zum Antworten