Eigenes OS?



  • Nobuo T schrieb:

    +fricky schrieb:

    Nobuo T schrieb:

    Kann GAS nicht inzwischen auch Intel-Syntax? Das wuerde die Sache sicher vereinfachen.

    echt, ne? diese bescheuerte at&t-syntax würde mich auch völlig nerven. welcher gehirnamputierte hat sich das bloss ausgedacht?
    🙂

    Total! 😃 👍
    Wahrscheinlich irgendein weltfremder, idealistischer, langhaariger Linux-Frickler mit Strick-Polunder und Taxischein. 🕶

    Ich glaube, AT&T Syntax ist älter als Linux und Intel und GNU zusammen. Ich sehe es so, dass allein aus der Tatsache, dass diese Syntax noch nicht "ausgestorben" ist und eine so lange Periode überdauert hat, muss etwas dahinter stecken. Ich hatte mich am Anfang auch wegen % und $ unbehaglich gefühlt, aber jetzt, sooo schlimm ist das nicht und die Syntax finde ich durchdachter und konsequenter in jeder Hinsicht als jede andere. Wenn wir hier in diesem Thread zurückschauen, wieviele Probleme gab es mit jmp und wieviel Ungewissheit, zumindest bei mir, ob jmp word oder jmp dword, nur weil nasm sowohl das eine als auch das andere erlaubt...


  • Mod

    @abc.w: Danke für die Übersetzung von Intel nach AT&T und für das DJGPP / mingw makefile. Es sollte nicht von zu vielen Kleinigkeiten und/oder Geschmacksachen abhängen, ob jemand den Zugang zu OSDEV findet. 🙂


  • Mod

    Nun können System Clock und Keyboard unabhängig den Bildschirm beschmutzen. Die vielen Polling-Endlosschleifen wurden nun gebrochen. Bisher habe ich die Vorschläge von Nobuo T noch nicht eingebaut, sehe aber auch (noch!) keinen Nachteil.

    ..



  • Erhard Henkes schrieb:

    @abc.w: Danke für die Übersetzung von Intel nach AT&T und für das DJGPP / mingw makefile. Es sollte nicht von zu vielen Kleinigkeiten und/oder Geschmacksachen abhängen, ob jemand den Zugang zu OSDEV findet. 🙂

    Bitte, aber ich glaube, ich habe da noch den einen oder anderen Fehler eingebaut und es wundert mich, dass es funktioniert:

    print_string: 
        movb $0x0E, %ah 
    1: 
        lodsb               # grab a byte from SI 
        testb %al, %al      # test AL 
        jz 2b               # if the result is zero, get out 
        int $0x10           # otherwise, print out the character! 
        jmp 1b 
    2: 
        ret
    

    Die Zeile jz 2b soll natürlich nicht 2 back sonder 2 forward heissen...

    print_string: 
        movb $0x0E, %ah 
    1: 
        lodsb               # grab a byte from SI 
        testb %al, %al      # test AL 
        jz 2f               # if the result is zero, get out 
        int $0x10           # otherwise, print out the character! 
        jmp 1b 
    2: 
        ret
    

    Entschuldigung... werde es oben gleich korrigieren...


  • Mod

    Ich denke das nächste, was nun notwendig wird, ist eine vollständige Datei video.c - da gibt es ja nicht viel zu erklären - mit verbesserten Darstellungsmöglichkeiten, denn im jetzigen Zustand kann man das Programm nicht auf die Menschheit los lassen. In keyboard.c fehlt eigentlich auch noch die Abfrage auf weitere Kombinationen mit Sondertasten neben Shift, aber ich bin erstmal froh, dass es nun generell läuft. Mit dem Keyboard Handling stehe ich echt auf Kriegsfuß. 🙄



  • Naja, so ganz sinnvoll sieht mir deine fetchandsigsbums-Funktion noch nicht aus... Meine Vorschlaege (eigentlich waere das ja nur noch eine Sache?) noch nicht drin, dafuer die grosse Endlosschleife noch immer. :p
    Mehr als einen Durchgang solltest du in dieser Funktion einfach nicht machen. Auch fuer erweiterte Codes, die aus mehreren Scancodes bestehen, wird jedes Mal ein IRQ ausgeloest.
    Mal morgen nochmal drueber schauen...

    video.c ... Mein Vorschlag waere erstmal globale Variablen fuer die character attributes und die aktuelle Bildschirmposition, etc. festzulegen. Evtl. sogar in der BDA im entsprechenden Format, wie es das VGA-BIOS tut.
    Dann erstmal grundlegende Funktionen zum Positionieren des Cursors, Ausgabe von chars und Strings, setzen der attributes, etc. implementieren.
    Darauf kannst du dann vielleicht eine abgespeckte Version von printf aufbauen, die zumindest mit 0-terminierten Strings vernuenftig arbeiten kann und evtl. auch sowas wie %x, %d, %c und %s unterstuetzt.


  • Mod

    Eine Design-Frage: Wie geht man mit den "Handlern" bezüglich IRQ am besten um?
    Der nachstehende keyboard_handler(...) holt ein Zeichen in einen lokalen Puffer.
    Wie geht man mit all diesen Informationen egal ob Tick-Info oder ein Zeichen von der Tastatur schnittstellenmäßig am besten um? Hier die zwei bisherigen Beispiele für IRQ0 und IRQ1:

    void timer_handler(struct regs* r)
    {
        ++timer_ticks;
        if (eticks)
            --eticks;
    }
    
    void keyboard_handler(struct regs* r)
    {
       // k_printf("keyboard handler works!", 8, 0x2);
    
       unsigned char bufferKEY[10];
       bufferKEY[0] = k_getch();
       k_printf(bufferKEY, 10,0xA); // the ASCII character
    }
    

    Die Ergebnisse sind in einem Fall timer_ticks und eticks, im anderen Fall bufferKEY[0]. Was macht man damit sinnvollerweise, um überall damit umgehen zu können? Gibt es diesbezüglich eine allgemeine Theorie?


  • Mod

    video.c ... Mein Vorschlag waere erstmal globale Variablen fuer die character attributes und die aktuelle Bildschirmposition, etc. festzulegen. Evtl. sogar in der BDA im entsprechenden Format, wie es das VGA-BIOS tut.
    Dann erstmal grundlegende Funktionen zum Positionieren des Cursors, Ausgabe von chars und Strings, setzen der attributes, etc. implementieren.
    Darauf kannst du dann vielleicht eine abgespeckte Version von printf aufbauen, die zumindest mit 0-terminierten Strings vernuenftig arbeiten kann und evtl. auch sowas wie %x, %d, %c und %s unterstuetzt.

    Genau so habe ich mir das auch vorgestellt, ist ja auch das übliche Verfahren bei x*y Textausgabe. Das bisherige k_printf(...) war nur eine Quick&Dirty-Notlösung, um rasch vorwärts zu gelangen.

    dafuer die grosse Endlosschleife noch immer.

    Das ist ja keine "echte" Endlosschleife, da ich dort mit continue oder break in die jeweils richtige Richtung ein-/aussteigen kann. Habe bisher noch keine perfekte Lösung für die passende Kontrollstruktur gefunden, weil man die while-Schleife verschieden durchlaufen muss. Ich hatte erst while (loopflag) geschrieben und das flag entsprechend gesetzt, dann aber festgestellt, dass man das auch durch ein einfaches break und Tschüss ersetzen kann.

    Meine Vorschlaege (eigentlich waere das ja nur noch eine Sache?) noch nicht drin

    Ich denke noch darüber nach, habe aber bisher keinen Nachteil im bisherigen Ablauf erkannt, ist aber auch alles noch zu chaotisch auf dem Bildschirm. Ab und zu tauchen da merkwürdige Zeichen an unerwarteten Stellen auf. 😃

    Auf jeden Fall vielen Dank an alle, die mich hier unterstützen! 👍
    Dieses Tutorial ist ein lang gehegter Wunsch. Bisher hatte ich mich aber nicht an die Materie heran gewagt, bin ja kein Informatiker, sondern Autodidakt. Nun bin ich aber bereits soweit eingedrungen in das Thema, dass es kein Zurück mehr gibt, nur noch vorwärts. 😉 😃


  • Mod

    @Nobuo T:
    Noch zu den Themen: Port 0x61 Bit7, EOI und IF

    ..

    http://wiki.osdev.org/PS2_Keyboard

    Nobuo T:

    Kurz aus- und wieder einschalten ueber Port 61h, Bit7.

    Ich gehe davon aus, dass für das MSB 0->1 u. 1->0 der korrekte Weg ist.

    Wann muss man dieses ACK(nowledgement) abgeben?

    ..

    Ich sehe allerdings noch keinen Effekt mit/ohne ACK? 😕
    Es heisst ja auch: "without waiting for another IRQ".
    Wir reagieren doch jetzt immer auf einen IRQ1. Also brauchen wir das nun nicht?
    Wir hätten es wohl eher beim Polling benötigt??? Da ist es mir aber nicht negativ aufgefallen.

    Das EOI wird in irq.c gesendet.

    ..

    Da war doch noch die Rede von einem IF? Ist dies das IF bei der CPU im Flag-Register?
    http://de.wikipedia.org/wiki/Statusregister#Interrupt-Enable-Flag



  • Erhard Henkes schrieb:

    Eine Design-Frage: Wie geht man mit den "Handlern" bezüglich IRQ am besten um?
    Der nachstehende keyboard_handler(...) holt ein Zeichen in einen lokalen Puffer.
    Wie geht man mit all diesen Informationen egal ob Tick-Info oder ein Zeichen von der Tastatur schnittstellenmäßig am besten um? Hier die zwei bisherigen Beispiele für IRQ0 und IRQ1:

    void timer_handler(struct regs* r)
    {
        ++timer_ticks;
        if (eticks)
            --eticks;
    }
    
    void keyboard_handler(struct regs* r)
    {
       // k_printf("keyboard handler works!", 8, 0x2);
    
       unsigned char bufferKEY[10];
       bufferKEY[0] = k_getch();
       k_printf(bufferKEY, 10,0xA); // the ASCII character
    }
    

    Die Ergebnisse sind in einem Fall timer_ticks und eticks, im anderen Fall bufferKEY[0]. Was macht man damit sinnvollerweise, um überall damit umgehen zu können? Gibt es diesbezüglich eine allgemeine Theorie?

    oft wird sowas objektorientiert gemacht (betriebssysteme schreien förmlich nach einem objektorientiertes design). für frei laufende countdown-timer machste dir z.b. ein paar funktionen:

    // erzeugt einen neuen timer und meldet ihn beim OS an. gibt 0 zurück, wenn kein timer alloziert werden konnte
    timer_t *timer_create(void);
    
    // startet den timer, der ab jetzt vom OS von 'timeout' bis 0 runtergezählt wird
    void timer_start (timer_t *timer, unsigned long timeout);
    
    // prüft ob der timer abgelaufen ist, gibt 0 zurück, wenn der timer noch läuft, sonst 1
    int timer_expired (timer *t);
    
    // beseitigt den timer (trägt ihn z.b. aus der liste des OS aus und gibt seinen speicher frei) 
    void timer_free (timer_t *timer);
    

    ^^die hardware-isr schaut bei jedem tick in eine verkettete liste, ob timer drin sind, die sie runterzählen muss.

    zu den I/O-daten: am besten du benutzt FIFO-buffer für sowas. die ISR (z.b. von der tastatur) trägt empfangene daten in den FIFO ein und die anwendung holt sie sich raus. ein tastatur-FIFO kann recht klein sein und darf ältere daten überschreiben, weil uralte tastendrücke ja nicht mehr interessieren. ein benutzer-prozess kann dann einfach zeichen aus dem FIFO holen, z.b. so:

    // hole ein zeichen aus dem tastatur-buffer. wenn c -1 (oder EOF) ist, war der FIFO leer
    int c = getchar();
    

    🙂


  • Mod

    @+fricky: Danke für die Ratschläge, kommt in die Planungs-/TODO-Liste. 👍 Momentan fehlt dem OS noch ein solches C-Modul mit Strukturen wie Stack (LIFO), Pipe/Queue/Cache (FIFO), verketteter Liste usw. In C++ wäre dies wirklich leichter zu realisieren, muss mal in meinen alten C-Büchern schmökern. 🙄



  • Erhard Henkes schrieb:

    Momentan fehlt dem OS noch ein solches C-Modul mit Strukturen wie Stack (LIFO), Pipe/Queue/Cache (FIFO), verketteter Liste usw.

    hier hast schon mal was für listen: http://www.c-plusplus.net/forum/viewtopic-var-t-is-160978-and-start-is-5.html

    Erhard Henkes schrieb:

    In C++ wäre dies wirklich leichter zu realisieren...

    auf den ersten blick vielleicht, aber letztlich würdest du dich mehr mit c++'s hausgemachten problemen, als mit deinem OS auseinandersetzen. mach den kernel besser in C, c++ (und beliebige andere sprachen) kannst du ja dann für usermode-anwendungnen nehmen.
    🙂


  • Mod

    Danke für den Link, habe aber meine alten C-Bücher bereits von der zweiten in die erste Reihe gerückt: Kernighan/Ritchie, Willms, Schröder. Zum Glück habe ich die nicht bereits verschenkt. 😉

    c++ (und beliebige andere sprachen) kannst du ja dann für usermode-anwendungnen nehmen.

    Ja, so wird's gemacht. 👍 Hoffentlich erlebt mein PrettyOS diesen Zeitpunkt noch und bleibt nicht bei 0.42 o.ä. wegen explodierender Taskliste stecken wie so viele Entwürfe. 😃

    Immer im Hinterkopf behalten, dass das ein Internet-Tutorial und kein Kompendium zur OS-Entwicklung werden soll - das wird wie ich das sehe schon so eine ordentliche Portion Stoff. 😃

    Ich habe den bisherigen Text mal als pdf gedruckt, da waren es schon fast 100 Seiten, ist schon ein umfassendes Thema, und dabei sind wir noch in der Overtüre. 😃


  • Mod

    @Nobuo T:

    Evtl. sogar in der BDA im entsprechenden Format, wie es das VGA-BIOS tut.

    Könntest Du diesen Punkt bitte genauer ausführen?



  • http://heim.ifi.uio.no/~stanisls/helppc/bios_data_area.html
    ab offset 449h. Mir scheint diese Struktur eigentlich im Grossen und Ganzen recht praktisch.

    Erhard Henkes schrieb:

    dafuer die grosse Endlosschleife noch immer.

    Das ist ja keine "echte" Endlosschleife, da ich dort mit continue oder break in die jeweils richtige Richtung ein-/aussteigen kann. Habe bisher noch keine perfekte Lösung für die passende Kontrollstruktur gefunden, weil man die while-Schleife verschieden durchlaufen muss. Ich hatte erst while (loopflag) geschrieben und das flag entsprechend gesetzt, dann aber festgestellt, dass man das auch durch ein einfaches break und Tschüss ersetzen kann.

    Ok, mal anders gefragt:
    Was fuer Faelle siehst du konkret, bei denen es noetig oder sinnvoll waere, in dieser Schleife mehrere Male hintereinander Scancodes von Port 60h zu lesen?
    Ich sehe hoechstens 2, naemlich erweiterte 2-Byte codes (Pfeiltasten, etc.) oder andere, laengere Status codes, die du aber saemtlichst noch nicht beruecksichtigst.
    Dagegen springst du so immer noch beim Druecken der Shift-Taste in eine Endlosschleife ohne Sinn und Wiederkehr.
    Deshalb mein Tipp: Weg mit der Schleife. Versuche moeglichst pro IRQ nur einmal Port 60h auszulesen, das gelesene direkt zu verarbeiten und gleich wieder zurueckzukehren.
    Dazu kannst du zB. in dieser Fetch-Funktion einen speziellen Rueckgabewert fuer Codes einfuehren, die sich nicht direkt in ASCII-Codes umsetzen lassen und zur Bearbeitung der Codes globale Variablen und Flags benutzen (zB. fuer shift, strg, alt down, usw. siehe auch meinen Link zur BDA oben als groben Denkansatz).

    Erhard Henkes schrieb:

    Kurz aus- und wieder einschalten ueber Port 61h, Bit7.

    Ich gehe davon aus, dass für das MSB 0->1 u. 1->0 der korrekte Weg ist.

    AFAIR: Ja.

    Erhard Henkes schrieb:

    Wann muss man dieses ACK(nowledgement) abgeben?

    Wenn du das Gelesene nicht mehr brauchst. In dem KB-Treiber, den ich vor Jahren mal geschrieben habe, hatte das direkt vor dem EOI stehen.

    Erhard Henkes schrieb:

    Würde das so passen?

    unsigned int FetchAndAnalyzeScancode()
    {
      unsigned int scancode; // For getting the keyboard scancode
      while(TRUE) // Loop until a key (w/o shift key) has been pressed
      {
        scancode = inportb(0x60);   // 0x60: get scan code from the keyboard
        // ACK: toggle bit 7 at port 0x61
        outportb(0x61,inportb(0x61) |  0x80); // 0->1
        outportb(0x61,inportb(0x61) &~ 0x80); // 1->0
    
        if ( scancode & 0x80 ) // Key released? ...
        { //...
    

    oder ist es so besser? (wie oben bei osdev.org)

    // ACK: toggle bit 7 at port 0x61
        unsigned char port_value = inportb(0x61);
        outportb(0x61,port_value |  0x80); // 0->1
        outportb(0x61,port_value &~ 0x80); // 1->0
    

    Die 2. Version ist eindeutig besser. Es schadet uebrigens sicher nicht, vor dem Lesen von Port 60h, den Port 64h, Bit 0 zu pruefen, wie du es urspruenglich auch gemacht hast (das meinte ich eigentlich gar nicht mit Endlosschleife, sondern deine aeussere while (true)-Schleife :D).
    Vielleicht den Port 64h statt in einer while-Schleife in einer for-Schleife mit Zaehler als zusaetzliche Abbruchbedingung pruefen. Im Fehlerfall springst du dann halt sofort zurueck.

    Erhard Henkes schrieb:

    Ich sehe allerdings noch keinen Effekt mit/ohne ACK? 😕
    Es heisst ja auch: "without waiting for another IRQ".
    Wir reagieren doch jetzt immer auf einen IRQ1. Also brauchen wir das nun nicht?
    Wir hätten es wohl eher beim Polling benötigt??? Da ist es mir aber nicht negativ aufgefallen.

    Theoretisch richtig, aber praktisch macht dieses x86-Gefrickel eben doch wieder alles anders. 😃
    Wenn du mit einer Runde Keyboard-Auslesen sicher fertig bist, kann es ganz bestimmt nicht verkehrt sein, mit diesem ACK sicher zu gehen.

    Erhard Henkes schrieb:

    Da war doch noch die Rede von einem IF? Ist dies das IF bei der CPU im Flag-Register?
    [...]
    isr.asm: dort findet sich bei jedem IRQ ein cli

    Richtig. Und genau deshalb ist eine [laengere/endlose] Schleife in einem IRQ-Handler ziemlich fatal. Damit blockierst du das komplette System. Ein IRQ-Handler muss moeglichst schnell sein: Rein, Hardware auslesen, das absolut Wichtigste kurz aufbereiten und im RAM abladen, ACK und wieder raus.
    Im Extremfall kannst du bei einem Micro-Kernel so weit gehen, dass du nur den Scancode in eine FIFO-queue im RAM schiebst und die Interpretation einem eigenen Treiber-Prozess ueberlaesst (wobei ich meine, dass man es nicht so uebertreiben braucht und sich der Keyboard-Input auch noch sehr gut weitgehend komplett im IRQ-Handler interpretieren/in ASCII-Codes umwandeln laesst).


  • Mod

    @Nobuo T: Vielen Dank für diese umfassenden Ausführungen. Du hast mich überzeugt. 🙂 Ich werde dies Schritt für Schritt umsetzen.

    In utils.c habe ich auch noch eine Änderung bei k_i2hex (h und NUL anghängt). Nobuo T hat dankenswerterweise k_itoa weiter optimiert:
    ..

    video.c sieht momentan wie folgt aus (noch ohne brauchbare printf(...), wie von Nobuo T vorgeschlagen):
    ..


  • Mod

    Leider noch nicht ganz perfekt, aber läuft prinzipiell schon wie es soll:
    ..


  • Mod

    Das ist - denke ich - noch nicht o.k.:

    case 'u':
                    u = va_arg (ap, UINT);
                    k_itoa(u, buffer);
                    puts(buffer);
                    break;
    

    Kennt einer ein utoa(...)?

    Bei %d fehlt noch das %i, das auch verwendet wird.
    Kleines %x barauche ich auch noch. Da muss ich wohl ein zweites k_i2hex schreiben.



  • Erhard Henkes schrieb:

    Kennt einer ein utoa(...)?

    bau doch dein 'itoa' um, aber statt 'int' mit 'nem 'unsigned' input.

    Erhard Henkes schrieb:

    Kleines %x barauche ich auch noch. Da muss ich wohl ein zweites k_i2hex schreiben.

    unnötiger luxus. wer hexziffern als gross/kleinbuchstaben braucht, kann doch toupper() oder tolower() bemühen.
    🙂



  • +fricky schrieb:

    Erhard Henkes schrieb:

    Kennt einer ein utoa(...)?

    bau doch dein 'itoa' um, aber statt 'int' mit 'nem 'unsigned' input.

    👍 Keine Ahnung, wie die entsprechende Standard-Funktion in C heisst, aber vom Prinzip her ist der Unterschied zu dem itoa minimal.

    Ansonsten ist da erstmal wohl alles Wichtige gut dabei. Nur einige Kleinigkeiten noch:

    video.c:
    k_clear_screen:
    Warum nicht gleich

    k_memsetw (vidmem, blank, 80 * 25);
    

    statt die Funktion 25* aufzurufen?

    putch:
    Grosse if/else-Schlacht... na egal 😃
    Ist es wirklich normales C-Verhalten, nur chars >= 0x20 auszugeben...?
    DOS ist da zB. recht kompromisslos und gibt alles aus, was nicht als Steuerzeichen gewertet wird. Wuerde IMHO auch einen Vergleich sparen. :p

    puts:
    Wozu extra mit strlen zaehlen, wenn du das Array danach eh nochmal Zeichen fuer Zeichen abwanderst?
    Mach's doch gleich so:

    void puts(UCHAR* text)
    {
        for (; *text; text++) putchar (*text);
    }
    

    (ich liebe es, for-Schleifen derart zu missbrauchen 🕶 )

    printf:
    Ist die Behandlung von \n, \t, usw. hier nicht doppelt? Das wird doch in putch auch schon geprueft...
    In dem Zusammenhang nehme ich auch mal an, dass du als default-Fall nicht wirklich "puts" nehmen wolltest?


Anmelden zum Antworten