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?
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
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?
Ich entwickel mit ein paar anderen zusammen ein eigenes OS, finden kannst du es hier. Ist in C geschrieben und der Zweck ist denke ich klar :???:
zu welchem Zweck und in welcher Sprache (ASS, C oder C++)?
Ich habe mich auch mal mit einem eigenen OS in Assembler beschäftigt zwecks endlich mal den Protected Mode der Intel CPUs zu verstehen. Habe aber diesbezüglich versagt und daraus wurde nichts. Hm, vertane Lebenszeit... hätte lieber in was anderes und sinnvolleres investieren sollen.
Alleine ist sicher schwierig. Zu zweit oder dritt macht das mehr Sinn. Wichtig ist, dass man alles genau dokumentiert, wenn man zwischenzeitlich pausieren will/muss.
Ich denke, dass es auch teilweise an der mangelnden Erklärung der Vorbilder und an langatmigen Theorie-Erklärungen liegt, wenn es in der Praxis schief geht. Beim Einstieg haben die wenigsten den kompletten Durchstieg durch alle Facetten. Also ist es wichtig, die Dinge detailliert zu erklären und vor allem auch Anregungen für eigene Experimente zu geben, damit die Dinge wirklich "glasklar" werden.
Bevor man bei einem Hobby- oder Experimental-OS auf den Protected Mode umschaltet, sollte man erst den Real Mode didaktisch komplett ausloten, damit der PM nicht zur Belastung wird, sondern zur Bereicherung.
Ich habe mir vorgenommen, didaktisch den Einstieg in die OS-Entwicklung zu erleichtern, denn es hilft, sowohl Assembler, C und auch die Hardware richtig zu verstehen, zumindest dann, wenn man dies möchte. Daher bitte ich euch mit zu helfen.
Bitte um konstruktive Kommentare/Ideen. Mir ist volles Verständnis und ein stufenweiser und leichter Einstieg wichtig. Kein blitzschnelles Voranhuschen einer Elite. Die brauchen solche Seiten nicht, sondern nur Datenblätter, Tabellen, ....
Wenn jemand mitmachen möchte, gerne. Bitte hier oder per mail. Im Kopf der Seite ist Platz für viele Namen. Hauptziel: Spaß und wirkliches Verständnis vermitteln. Ich möchte auch niemanden zwingen, zunächst Linux zu installieren, um zu beginnen.
Bisher haben sich für mich gvim [EDIT: notepad++ bringt mehr Komfort], nasm und bochs als wirklich brauchbar heraus kristallisiert. partcopy.exe kann sicher durch etwas anderes ersetzt werden. Wichtig ist, dass sich niemand die Festplatten "zerschießt".
Ich habe eine Floppy Disk zum Booten verwendet. Heutige PCs haben dies nicht mehr ohne weiteres. Viele haben aber noch alte PCs mit Floppy Disk und wollen wirklich "physisch booten". Didaktisch muss das einfach sein.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 00:52:55 11.07.2009, insgesamt 1-mal bearbeitet
Euer Problem ist, daß ihr immer nur in x86 denkt. x86 ist nicht unbedingt die optimale Plattform für einen Einsteiger mit dem ganzen 20 Jahre alten Schrott wie Realmode, Callgates, Segmentregistern und dem ganzen anderen Mist. Ich verstehe nicht, warum immer alle Welt sowas immer unbedingt für die x86 Architektur machen muss. Die ist nicht nur hässlich, sondern auch klobig und extrem Noobunfreundlich.
Euer Problem ist, daß ihr immer nur in x86 denkt. x86 ist nicht unbedingt die optimale Plattform für einen Einsteiger mit dem ganzen 20 Jahre alten Schrott wie Realmode, Callgates, Segmentregistern und dem ganzen anderen Mist. Ich verstehe nicht, warum immer alle Welt sowas immer unbedingt für die x86 Architektur machen muss. Die ist nicht nur hässlich, sondern auch klobig und extrem Noobunfreundlich.
da bin ich voll bei dir. erhard: mach was für 'ne ARM-plattform. ich wette es gibt mehr ARM-basierte systeme auf der welt als PC(x86)-kisten.
Euer Problem ist, daß ihr immer nur in x86 denkt. x86 ist nicht unbedingt die optimale Plattform für einen Einsteiger mit dem ganzen 20 Jahre alten Schrott wie Realmode, Callgates, Segmentregistern und dem ganzen anderen Mist. Ich verstehe nicht, warum immer alle Welt sowas immer unbedingt für die x86 Architektur machen muss. Die ist nicht nur hässlich, sondern auch klobig und extrem Noobunfreundlich.
da bin ich voll bei dir. erhard: mach was für 'ne ARM-plattform. ich wette es gibt mehr ARM-basierte systeme auf der welt als PC(x86)-kisten.
wie jetzt?!?
ach ja, erhard: wie wär's mit 'nem OS für nintendo-DS? die plattform ist weit verbreitet, einigermaßen gut 'reverse engineered' und es gibt, meines wissens, bisher nur ein hobbyisten-OS dafür.
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.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
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.
@+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:
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:
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^^
_________________ MCPD, MCTS and more! | "It's 7:05am. I have not slept." | www.google.com
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:
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.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 14:07:00 28.03.2009, insgesamt 5-mal bearbeitet
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.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 18:44:24 15.03.2009, insgesamt 1-mal bearbeitet
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:
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:
Code:
...
copy /b bootloader.bin + kernel.bin MyOS.bin
Code:
...
copy /b bootloader.bin + kernel.bin MyOS.bin
Code:
...
copy /b bootloader.bin + kernel.bin MyOS.bin
Da kommt bei mir immer folgende Fehlermeldung:
Code:
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
Code:
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
Code:
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
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:
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.
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.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 23:56:04 15.03.2009, insgesamt 1-mal bearbeitet
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.
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.
_________________ Kilo Byte=1000,Kilobyte=1024 ANSI/IEEE Standard 1084-1986 rapso
-Mod im Spiele-/Grafikprogrammierung|rapsoo@hotmail.com| #dionysos irc.quakenet.org |amazon stole my PS3
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?
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 21:49:08 17.03.2009, insgesamt 3-mal bearbeitet
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.
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).
; setup a stack
movax, 0x9000 ; address of the stack
movss, ax; SS = 0x9000
xorsp, sp; SP = 0x0000
(...)
; jump to kernel
jmp 0x1000:0x0000 ; address of kernel
Assembler Code:
1 2 3 4 5 6 7 8 9
org 0x7C00 ; set up start address
; setup a stack
movax, 0x9000 ; address of the stack
movss, ax; SS = 0x9000
xorsp, sp; SP = 0x0000
(...)
; jump to kernel
jmp 0x1000:0x0000 ; address of kernel
Assembler Code:
1 2 3 4 5 6 7 8 9
org 0x7C00 ; set up start address
; setup a stack
movax, 0x9000 ; address of the stack
movss, ax; SS = 0x9000
xorsp, sp; SP = 0x0000
(...)
; jump to kernel
jmp 0x1000:0x0000 ; address of kernel
Zitat:
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 (0000*0x10)+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!
... 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.
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?
Zitat:
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:
Zitat:
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
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 23:49:17 17.03.2009, insgesamt 5-mal bearbeitet
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:
Zitat:
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?
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 23:19:47 17.03.2009, insgesamt 1-mal bearbeitet
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.
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.
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.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 00:17:22 19.03.2009, insgesamt 1-mal bearbeitet
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?
PutStr_32:
.nextchar:
lodsb
or al, al
jz .end
mov byte [ds:ecx], al
inc ecx
mov byte [ds:ecx], dl
inc ecx
jmp .nextchar
.end:
ret
; You load the address you want to output to in ecx(0x0B8000), the address of the string (has to be null terminated)
; you want to print in esi and the attributes in dl (personally 0x04, red char on black background) ,
; the lodsb, loads one byte from esi into al then increments esi, then it checks for a null terminator
; then it moves the char into the write position in vid mem and then increments ecx and writes the attributes,
; the loops until it finds a null pointer at which point it breaks....
PutStr_32:
.nextchar:
lodsb
or al, al
jz .end
mov byte [ds:ecx], al
inc ecx
mov byte [ds:ecx], dl
inc ecx
jmp .nextchar
.end:
ret
; You load the address you want to output to in ecx(0x0B8000), the address of the string (has to be null terminated)
; you want to print in esi and the attributes in dl (personally 0x04, red char on black background) ,
; the lodsb, loads one byte from esi into al then increments esi, then it checks for a null terminator
; then it moves the char into the write position in vid mem and then increments ecx and writes the attributes,
; the loops until it finds a null pointer at which point it breaks....
PutStr_32:
.nextchar:
lodsb
or al, al
jz .end
mov byte [ds:ecx], al
inc ecx
mov byte [ds:ecx], dl
inc ecx
jmp .nextchar
.end:
ret
; You load the address you want to output to in ecx(0x0B8000), the address of the string (has to be null terminated)
; you want to print in esi and the attributes in dl (personally 0x04, red char on black background) ,
; the lodsb, loads one byte from esi into al then increments esi, then it checks for a null terminator
; then it moves the char into the write position in vid mem and then increments ecx and writes the attributes,
; the loops until it finds a null pointer at which point it breaks....
Wenn ich org 0x10000 vorne rein setze, funktionieren die normalen Befehle im Real Mode. Bei PM stürzt er aber sofort ab.
Wenn ich org 0x7c00 vorne rein setze, funktionieren die normalen Befehle im Real Mode natürlich nicht mehr. Bei PM stürzt er aber nicht mehr ab, aber er zeigt auch nichts an (z.B. 'A'). Man kann dann noch Zeichen eingeben, Puffer von 31 Zeichen läuft noch.
org 0x10000 ist richtig. Wie muss ich den far jump schreiben?
Was ist hier der richtige Selektor für den Jump?
Code:
db 0xea ;instruction for FAR JUMP
dw do_pm ;Offset for jump
dw 0x8 ;selector of the segment for the jump
; jmp code_gdt:do_pm does not work?!
Code:
db 0xea ;instruction for FAR JUMP
dw do_pm ;Offset for jump
dw 0x8 ;selector of the segment for the jump
; jmp code_gdt:do_pm does not work?!
Code:
db 0xea ;instruction for FAR JUMP
dw do_pm ;Offset for jump
dw 0x8 ;selector of the segment for the jump
; jmp code_gdt:do_pm does not work?!
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 01:41:08 19.03.2009, insgesamt 5-mal bearbeitet
Tja, das ist eben so eine Sache mit den Labels in Asm. Praktisch sind das ja beim x86 eigentlich immer offsets. Da muss auch jeweils die Basis-Adresse zu stimmen (wird auch beim nasm mittels org festgelegt).
Kurze Ueberlegung:
Wenn du deinen Code nach 0x10000 laedst und die Segmentregister dann mit 0x1000 laedst, klappt das ohne, bzw. mit org 0 natuerlich erstmal im RM problemlos, da dein Code an Offset 0 des Segments direkt beginnt. Im GDTR muss dann aber die physikalische mit absoluter Basis 0 stehen! Das selbe beim far jump zum PM - der offset-Teil muss dort dann relativ zur Basisadresse, die im angegebenen Code-Selector steht, passen.
Wenn du stattdessen einfach so die labels da rein schreibst, ohne zu beruecksichtigen, dass sich deren Adresse so ohne Weiteres nur auf das RM-Segment 0x1000 beziehen, geht die Sache schief.
Sorry, ich bin gerade auch zu bematscht in der Birne, um konkreter zu werden. Da kann ich mich erst nach einer Muetze Schlaf konzentriert mit befassen...
Der "Kernel" bzw. "PM-Umschalter" sitzt auch bei 0x10000. Er verwendet keine org-Direktive, sondern setzt die GTD ganz vorne hin und springt drüber.
Beim far jump kommt aber auch so ein merkwürdiger Kunstgriff (NASM-Problem?).
Geht das nicht einfacher als unten im Code?
Ist schwierig zu verstehen.
Ich möchte da im Tut etwas richtig Glasklares zusammen basteln, dass diese Verständnishürde verschwindet. Please help towards excellence.
Das ist nicht unbedingt ein Problem von NASM (bzw. mir faellt kein Assembler ein, mit dem das wirklich geschickter ginge), sondern eine Eigenart des x86, dass hier in einem Code einfach verschiedene Adressierungsmechanismen zum Zugriff auf die selben Labels benutzt werden.
Am saubersten waere es IMHO, wenn du dafuer sorgst, dass dieses Phaenomen der unterschiedlichen Basisadressen gar nicht erst auftritt. Dazu musst du in diesem Code-Abschnitt immer die Segment-Basis 0 benutzen (im RM also alle Segmentregister auf 0 setzen, dh. der Code muss auch in den ersten 64K Speicher liegen, im PM die Basisadresse fuer Zugriffe auf diesen Code auch 0) und an den Anfang des Codes entsprechend eine org-Anweisung mit dem Start-Offset des Codes zur Basis 0 (also das Offset innerhalb der ersten 64k, wo der Code anfaengt).
32Bit-Segmente mit Basis 0 und bis zu 4GB Groesse zu verwenden, bietet sich dann im PM spaeter natuerlich so oder so auch an...
So kannst du dir dann auch zB. den extra-Descriptor fuer die Textausgabe sparen und hast einen leichteren Uebergang zu C-Code (wie wuerde man direkt aus c heraus ueberhaupt mit verschiedenen Selectoren umgehen?).
So solltest du keine Probleme mit irgendwelchen "org"ien oder "Kunstgriffen" mit addierten Basisadressen o.Ae. bekommen.
Falls du deinen Code unbedingt im Segment 1000 lassen willst, kommst du um einiges Gebastel mit Addition der phys. Basisadresse (10000) zur Adresse der GDT und evtl. die Differenz der Basisadressen der Code-Segmente in RM und PM zum Far-Jump in den PM wahrscheinlich nicht umhin.
Das waere dann immerhin evtl. eine gute Gelegenheit, die Adressierungsmechanismen des RM, rein physikalischer Adressen und PM ohne paging genauer zu beleuchten... wenn du das wirklich sauber kommentierst und erklaerst.
Noch zu etwas Anderem:
In meinem letzten OS-Projekt in der Uni, und nun in deinem Code, wurde mir mal wieder deutlich vor Augen gefuehrt, dass es nicht "cool", sondern hoechstens verwirrend ist, irgendwelche weitgehend unkommentierten Zahlen zur Darstellung von Bit-flags hinzuklatschen. Du tust deinen Lesern sicher einen Gefallen, wenn du flags in den Deskriptoren genauer kommentierst.
Mich wuerde auch interessieren, weshalb du meinen Tipp zu deinem strcmp nicht uebernummen hast?
Mich wuerde auch interessieren, weshalb du meinen Tipp zu deinem strcmp nicht uebernummen hast?
@Nobuo T:
Sorry, hatte vergessen, es auch im Tutorial einzutippen. Dein Tipp ist hervorragend! Code ist jetzt kürzer: Carry-Flag weg und nur noch eine Rücksprungadresse '.done'
Ich hoffe zumindest, dass ich Dich so richtig verstanden habe.
Wie hättest Du dies mit 'test op,op' (AND-Verknüpfung ohne Speicherung, nur Flags setzen) gemacht? Das nimmt man doch eher, um zu testen, ob ein bestimmtes Bit gesetzt ist? z.B. test AL, 64 (check auf Bit 6)
Bezüglich RM -> PM bin ich noch am Grübeln. da muss ich noch einige Hobby-OS analysieren. Vielleicht kann man die besten Ideen aus allen heraus kristallisieren. Die Idee von Nobuo T ist auf jeden Fall bedenkenswert. Welchen offset würde man da mittels org wählen und warum? Mir ist noch nicht klar, warum die meisten den Kernel (oder ersten Teil des Kernels) nach 10000h setzen.
Zitat:
... dass es nicht "cool", sondern hoechstens verwirrend ist, irgendwelche weitgehend unkommentierten Zahlen zur Darstellung von Bit-flags hinzuklatschen. Du tust deinen Lesern sicher einen Gefallen, wenn du flags in den Deskriptoren genauer kommentierst.
Völlig richtig! So darf man das auch nicht machen, war nur Test.
Besser: binäre Darstellung im Assemblercode mit Kommentar, der die einzelnen Bits bezüglich ihrer Aufgabe beschreibt/erklärt.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 19:22:12 19.03.2009, insgesamt 3-mal bearbeitet
Zunächst mal vielen Dank an alle, die mich bisher nicht entmutigten, sondern mich mit Rat und Tat unterstützen. IMHO ein wirklich konstruktives Subforum.
Für die Ungeduldigen:
Nun wollen wir mal versuchsweise von RM nach PM schalten (ist noch nicht im Tutorial, weil didaktisch noch nicht klar genug), indem man den Befehl "pm" eingibt.
Ich habe zunächst die Methode der Kalkulation für die GDTR-Werte nach dem Umschalten verwendet, um zu sehen, ob das alles gut klappt. Funktioniert wirklich. Didaktisch ist dieser Weg nicht perfekt, lässt sich aber dennoch erklären. Vielleicht kann Nobuo T seine Idee daneben stellen zum Vergleich.
Nun fehlt didaktisch nur noch ein Command Line Interpreter und ein Befehl "RM" im Protected Mode, der den Rücksprung nach Real Mode gewährt.
In Zeile 277 steht noch nichts Sinnvolles als Zieladresse (zur Zeit wird der Code "gedumpt", ich habe mal in DL mit den Farben gespielt). Hat jemand eine kleine gute Idee für den Einstieg?
Typisch wäre jetzt ein Sprung in einen C-Kernel. Aber inzwischen gefällt mir Assembler viel besser. Man gewöhnt sich an alles.
Code für kernel.asm, der binär via bootloader nach 10000h geladen wird:
welcome db 'HenkesSoft 0.02 (version from Mar 19, 2009)', 13, 10, 0
msg_helloworld db 'Hello World!', 13, 10, 0
badcommand db 'Command unknown.', 13, 10, 0
prompt db '>', 0
cmd_hi db 'hi', 0
cmd_help db 'help', 0
cmd_questionmark db '?', 0
cmd_exit db 'exit', 0
cmd_pm db 'pm', 0
msg_help db 'Commands: hi, help, ?, pm, exit', 13, 10, 0
msg_exit db 'Reboot starts now.', 13, 10, 0
msg_pm db 'Switch-over to Protected Mode.', 13, 10, 0
msg_pm2 db 'OS currently uses Protected Mode.', 13, 10, 0
buffer times 32 db 0
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; global descriptor table (GDT) ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
NULL_Desc:
dd 0
dd 0
CODE_Desc:
dw 0xFFFF ; segment length bits 0-15 ("limit")
dw 0 ; segment base byte 0,1
db 0 ; segment base byte 2
db 10011010b ; access rights
db 11001111b ; bit 7-4: 4 flag bits: granularity, default operation size bit, 2 bits available for OS
; bit 3-0: segment length bits 16-19 ("limit")
db 0 ; segment base byte 3
DATA_Desc:
dw 0xFFFF ; segment length bits 0-15
dw 0 ; segment base byte 0,1
db 0 ; segment base byte 2
db 10010010b ; access rights
db 11001111b ; bit 7-4: 4 flag bits: granularity, big bit (0=USE16-Segm., 1=USE32-Segm.), 2 bits avail.
; bit 3-0: segment length bits 16-19
db 0 ; segment base byte 3
gdt:
Limit dw 0 ; length of GDT
Base dd 0 ; base of GDT
;;;;;;;;;;;;;;;
;; Real Mode ;;
;;;;;;;;;;;;;;;
RealMode:
mov ax, 0x1000 ; set up segments
mov ds, ax
mov es, ax
mov si, welcome
call print_string
loop:
mov si, prompt
call print_string
mov di, buffer
call get_string
mov si, buffer
cmp byte [si], 0 ; blank line?
je loop ; yes, ignore it
PutStr_32:
.nextchar:
lodsb
or al, al
jz .end
mov byte [ds:ecx], al
inc ecx
mov byte [ds:ecx], dl
inc ecx
jmp .nextchar
.end:
ret
; You load the address you want to output to in ecx(0x0B8000), the address of the string (has to be null terminated)
; you want to print in esi and the attributes in dl (personally 0x04, red char on black background) ,
; the lodsb, loads one byte from esi into al then increments esi, then it checks for a null terminator
; then it moves the char into the write position in vid mem and then increments ecx and writes the attributes,
; the loops until it finds a null pointer at which point it breaks....
welcome db 'HenkesSoft 0.02 (version from Mar 19, 2009)', 13, 10, 0
msg_helloworld db 'Hello World!', 13, 10, 0
badcommand db 'Command unknown.', 13, 10, 0
prompt db '>', 0
cmd_hi db 'hi', 0
cmd_help db 'help', 0
cmd_questionmark db '?', 0
cmd_exit db 'exit', 0
cmd_pm db 'pm', 0
msg_help db 'Commands: hi, help, ?, pm, exit', 13, 10, 0
msg_exit db 'Reboot starts now.', 13, 10, 0
msg_pm db 'Switch-over to Protected Mode.', 13, 10, 0
msg_pm2 db 'OS currently uses Protected Mode.', 13, 10, 0
buffer times 32 db 0
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; global descriptor table (GDT) ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
NULL_Desc:
dd 0
dd 0
CODE_Desc:
dw 0xFFFF ; segment length bits 0-15 ("limit")
dw 0 ; segment base byte 0,1
db 0 ; segment base byte 2
db 10011010b ; access rights
db 11001111b ; bit 7-4: 4 flag bits: granularity, default operation size bit, 2 bits available for OS
; bit 3-0: segment length bits 16-19 ("limit")
db 0 ; segment base byte 3
DATA_Desc:
dw 0xFFFF ; segment length bits 0-15
dw 0 ; segment base byte 0,1
db 0 ; segment base byte 2
db 10010010b ; access rights
db 11001111b ; bit 7-4: 4 flag bits: granularity, big bit (0=USE16-Segm., 1=USE32-Segm.), 2 bits avail.
; bit 3-0: segment length bits 16-19
db 0 ; segment base byte 3
gdt:
Limit dw 0 ; length of GDT
Base dd 0 ; base of GDT
;;;;;;;;;;;;;;;
;; Real Mode ;;
;;;;;;;;;;;;;;;
RealMode:
mov ax, 0x1000 ; set up segments
mov ds, ax
mov es, ax
mov si, welcome
call print_string
loop:
mov si, prompt
call print_string
mov di, buffer
call get_string
mov si, buffer
cmp byte [si], 0 ; blank line?
je loop ; yes, ignore it
PutStr_32:
.nextchar:
lodsb
or al, al
jz .end
mov byte [ds:ecx], al
inc ecx
mov byte [ds:ecx], dl
inc ecx
jmp .nextchar
.end:
ret
; You load the address you want to output to in ecx(0x0B8000), the address of the string (has to be null terminated)
; you want to print in esi and the attributes in dl (personally 0x04, red char on black background) ,
; the lodsb, loads one byte from esi into al then increments esi, then it checks for a null terminator
; then it moves the char into the write position in vid mem and then increments ecx and writes the attributes,
; the loops until it finds a null pointer at which point it breaks....
welcome db 'HenkesSoft 0.02 (version from Mar 19, 2009)', 13, 10, 0
msg_helloworld db 'Hello World!', 13, 10, 0
badcommand db 'Command unknown.', 13, 10, 0
prompt db '>', 0
cmd_hi db 'hi', 0
cmd_help db 'help', 0
cmd_questionmark db '?', 0
cmd_exit db 'exit', 0
cmd_pm db 'pm', 0
msg_help db 'Commands: hi, help, ?, pm, exit', 13, 10, 0
msg_exit db 'Reboot starts now.', 13, 10, 0
msg_pm db 'Switch-over to Protected Mode.', 13, 10, 0
msg_pm2 db 'OS currently uses Protected Mode.', 13, 10, 0
buffer times 32 db 0
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; global descriptor table (GDT) ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
NULL_Desc:
dd 0
dd 0
CODE_Desc:
dw 0xFFFF ; segment length bits 0-15 ("limit")
dw 0 ; segment base byte 0,1
db 0 ; segment base byte 2
db 10011010b ; access rights
db 11001111b ; bit 7-4: 4 flag bits: granularity, default operation size bit, 2 bits available for OS
; bit 3-0: segment length bits 16-19 ("limit")
db 0 ; segment base byte 3
DATA_Desc:
dw 0xFFFF ; segment length bits 0-15
dw 0 ; segment base byte 0,1
db 0 ; segment base byte 2
db 10010010b ; access rights
db 11001111b ; bit 7-4: 4 flag bits: granularity, big bit (0=USE16-Segm., 1=USE32-Segm.), 2 bits avail.
; bit 3-0: segment length bits 16-19
db 0 ; segment base byte 3
gdt:
Limit dw 0 ; length of GDT
Base dd 0 ; base of GDT
;;;;;;;;;;;;;;;
;; Real Mode ;;
;;;;;;;;;;;;;;;
RealMode:
mov ax, 0x1000 ; set up segments
mov ds, ax
mov es, ax
mov si, welcome
call print_string
loop:
mov si, prompt
call print_string
mov di, buffer
call get_string
mov si, buffer
cmp byte [si], 0 ; blank line?
je loop ; yes, ignore it
PutStr_32:
.nextchar:
lodsb
or al, al
jz .end
mov byte [ds:ecx], al
inc ecx
mov byte [ds:ecx], dl
inc ecx
jmp .nextchar
.end:
ret
; You load the address you want to output to in ecx(0x0B8000), the address of the string (has to be null terminated)
; you want to print in esi and the attributes in dl (personally 0x04, red char on black background) ,
; the lodsb, loads one byte from esi into al then increments esi, then it checks for a null terminator
; then it moves the char into the write position in vid mem and then increments ecx and writes the attributes,
; the loops until it finds a null pointer at which point it breaks....
habe mal den Code in bochs ausprobiert, weiss nicht, ist es normal, dass nach dem Kommando pm lauter farbiger Zeichen erscheinen...
Bezüglich der drei Zeilen:
Code:
db 0xea ; instruction code for FAR JUMP
dw ProtectedMode ; offset for jump
dw 0x8 ; selector of the segment for the jump
Code:
db 0xea ; instruction code for FAR JUMP
dw ProtectedMode ; offset for jump
dw 0x8 ; selector of the segment for the jump
Code:
db 0xea ; instruction code for FAR JUMP
dw ProtectedMode ; offset for jump
dw 0x8 ; selector of the segment for the jump
Man kann sich die Binärdatei mit dem Programm ndisasm, das mit nasm mit dabei ist, disassemblieren lassen, z.B. so:
Code:
c:\nasm-2.06rc6\ndisasm.exe -b 16 kernel.bin
Code:
c:\nasm-2.06rc6\ndisasm.exe -b 16 kernel.bin
Code:
c:\nasm-2.06rc6\ndisasm.exe -b 16 kernel.bin
Dann sieht man, wie ndisasm es interpretiert:
Code:
000001E4 EA57020800 jmp word 0x8:0x257
Code:
000001E4 EA57020800 jmp word 0x8:0x257
Code:
000001E4 EA57020800 jmp word 0x8:0x257
Also kann man die oberen 3 Zeilen durch eine ersetzen:
Code:
jmp word 0x8:ProtectedMode
Code:
jmp word 0x8:ProtectedMode
Code:
jmp word 0x8:ProtectedMode
Damit wird scheinbar auch ein FAR JUMP generiert. Man kann es auch hier nachlesen: http://www.nasm.us/doc/nasmdo10.html#section-10.1
Ich hab es mal ausprobiert und es kommen immer noch die farbigen Zeichen - also wahrscheinlich funktioniert es wie vorher. Und die ndisasm Ausgabe stimmt auch mit der vorherigen überein.
Die Subroutine ClrScr32 bewirkt einen Reboot. Kommentiert man den Befehl aus, läuft der String 'OS currently uses Protected Mode.' oben links in 16 Farben durch. Ich möchte nur jeweils vorher den Bildschirm löschen. Code macht aber Probleme. Vielleicht hat jemand eine Idee?
Im Tutorial könnte man den clrscr zunächst noch im RM ausführen. GDT habe ich
nach gdt.inc ausgelagert. Inzwischen alles schon ganz schön unübersichtlich.
So sieht momentan eine zumindest in Bochs binär von Platte funktionierende Version (von floppy im PC booten klappt momentan nicht?) aus:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; global descriptor table (GDT) ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
NULL_Desc:
dd 0
dd 0
CODE_Desc:
dw 0xFFFF ; segment length bits 0-15 ("limit")
dw 0 ; segment base byte 0,1
db 0 ; segment base byte 2
db 10011010b ; access rights
db 11001111b ; bit 7-4: 4 flag bits: granularity, default operation size bit, 2 bits available for OS
; bit 3-0: segment length bits 16-19 ("limit")
db 0 ; segment base byte 3
DATA_Desc:
dw 0xFFFF ; segment length bits 0-15
dw 0 ; segment base byte 0,1
db 0 ; segment base byte 2
db 10010010b ; access rights
db 11001111b ; bit 7-4: 4 flag bits: granularity, big bit (0=USE16-Segm., 1=USE32-Segm.), 2 bits avail.
; bit 3-0: segment length bits 16-19
db 0 ; segment base byte 3
gdtr:
Limit dw 0 ; length of GDT
Base dd 0 ; base of GDT
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; global descriptor table (GDT) ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
NULL_Desc:
dd 0
dd 0
CODE_Desc:
dw 0xFFFF ; segment length bits 0-15 ("limit")
dw 0 ; segment base byte 0,1
db 0 ; segment base byte 2
db 10011010b ; access rights
db 11001111b ; bit 7-4: 4 flag bits: granularity, default operation size bit, 2 bits available for OS
; bit 3-0: segment length bits 16-19 ("limit")
db 0 ; segment base byte 3
DATA_Desc:
dw 0xFFFF ; segment length bits 0-15
dw 0 ; segment base byte 0,1
db 0 ; segment base byte 2
db 10010010b ; access rights
db 11001111b ; bit 7-4: 4 flag bits: granularity, big bit (0=USE16-Segm., 1=USE32-Segm.), 2 bits avail.
; bit 3-0: segment length bits 16-19
db 0 ; segment base byte 3
gdtr:
Limit dw 0 ; length of GDT
Base dd 0 ; base of GDT
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; global descriptor table (GDT) ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
NULL_Desc:
dd 0
dd 0
CODE_Desc:
dw 0xFFFF ; segment length bits 0-15 ("limit")
dw 0 ; segment base byte 0,1
db 0 ; segment base byte 2
db 10011010b ; access rights
db 11001111b ; bit 7-4: 4 flag bits: granularity, default operation size bit, 2 bits available for OS
; bit 3-0: segment length bits 16-19 ("limit")
db 0 ; segment base byte 3
DATA_Desc:
dw 0xFFFF ; segment length bits 0-15
dw 0 ; segment base byte 0,1
db 0 ; segment base byte 2
db 10010010b ; access rights
db 11001111b ; bit 7-4: 4 flag bits: granularity, big bit (0=USE16-Segm., 1=USE32-Segm.), 2 bits avail.
; bit 3-0: segment length bits 16-19
db 0 ; segment base byte 3
gdtr:
Limit dw 0 ; length of GDT
Base dd 0 ; base of GDT
cli ; clear interrupts
mov eax, cs ; CS ---> EAX
mov ds, ax ; CS ---> DS
shl eax, 4 ; CS * 0x10 = linear address
mov [CODE_Desc+2], ax ; linear address ---> base of code descriptor
mov [DATA_Desc+2], ax ; linear address ---> base of data descriptor
shr eax, 16 ;
mov [CODE_Desc+4], al ;
mov [DATA_Desc+4], al ;
mov eax, cs ; base of GDT
shl eax, 4
add eax, NULL_Desc
mov [Base], eax ; base of GDT ---> GDTR
mov [Limit], WORD gdtr - NULL_Desc - 1 ; limit of GDT ---> GDTR
lgdt [gdtr] ; load GDT via GDTR
mov eax, cr0 ; switch-over to Protected Mode
or eax, 1 ; set bit 0 of CR0 register
mov cr0, eax ;
jmp word 0x8:ProtectedMode
; db 0xea ; instruction code for FAR JUMP
; dw ProtectedMode ; offset for jump
; dw 0x8 ; selector of the segment for the jump
; http://www.nasm.us/doc/nasmdo10.html#section-10.1
;;;;;;;;;;;
;; Calls ;;
;;;;;;;;;;;
print_string:
lodsb ; grab a byte from SI
or al, al ; logical or AL by itself
jz .done ; if the result is zero, get out
mov ah, 0x0E
int 0x10 ; otherwise, print out the character!
jmp print_string
.done:
ret
get_string:
xor cl, cl
.loop:
mov ah, 0x00
int 0x16 ; wait for keypress
cmp al, 8 ; backspace pressed?
je .backspace ; yes, handle it
cmp al, 13 ; enter pressed?
je .done ; yes, we're done
cmp cl, 31 ; 31 chars inputted?
je .loop ; yes, only let in backspace and enter
mov ah, 0x0E
int 0x10 ; print out character
stosb ; put character in buffer
inc cl
jmp .loop
.backspace:
or cl, cl ; zero? (start of the string)
jz .loop ; if yes, ignore the key
dec di
mov byte [di], 0 ; delete character
dec cl ; decrement counter as well
mov ax, 0x0E08
int 0x10 ; backspace on the screen
mov al, ' '
int 0x10 ; blank character out
mov al, 8
int 0x10 ; backspace again
jmp .loop ; go to the main loop
.done:
mov al, 0 ; null terminator
stosb
mov ax, 0x0E0D
int 0x10
mov al, 0x0A
int 0x10 ; newline
ret
strcmp:
.loop:
mov al, [si] ; grab a byte from SI
cmp al, [di] ; are SI and DI equal?
jne .done ; nope, we're done.
or al, al ; zero?
jz .done ; yes, we're done.
inc di ; increment DI
inc si ; increment SI
jmp .loop ; loop!
.done:
ret
clrscr:
mov ax, 0x0600
xor cx, cx
mov dx, 0x174F
xor bh, bh
int 0x10
ret
Waitingloop:
pushad
mov ebx,0x9FFFF
.loop:
dec ebx
jnz .loop
popad
ret
PutStr_32:
pushad
.nextchar:
lodsb
or al, al
jz .end
mov byte [ds:ecx], al
inc ecx
mov byte [ds:ecx], dl
inc ecx
jmp .nextchar
.end:
popad
ret
; You load the address you want to output to in ecx(0x0B8000), the address of the string (has to be null terminated)
; you want to print in esi and the attributes in dl (personally 0x04, red char on black background) ,
; the lodsb, loads one byte from esi into al then increments esi, then it checks for a null terminator
; then it moves the char into the write position in vid mem and then increments ecx and writes the attributes,
; the loops until it finds a null pointer at which point it breaks....
cli ; clear interrupts
mov eax, cs ; CS ---> EAX
mov ds, ax ; CS ---> DS
shl eax, 4 ; CS * 0x10 = linear address
mov [CODE_Desc+2], ax ; linear address ---> base of code descriptor
mov [DATA_Desc+2], ax ; linear address ---> base of data descriptor
shr eax, 16 ;
mov [CODE_Desc+4], al ;
mov [DATA_Desc+4], al ;
mov eax, cs ; base of GDT
shl eax, 4
add eax, NULL_Desc
mov [Base], eax ; base of GDT ---> GDTR
mov [Limit], WORD gdtr - NULL_Desc - 1 ; limit of GDT ---> GDTR
lgdt [gdtr] ; load GDT via GDTR
mov eax, cr0 ; switch-over to Protected Mode
or eax, 1 ; set bit 0 of CR0 register
mov cr0, eax ;
jmp word 0x8:ProtectedMode
; db 0xea ; instruction code for FAR JUMP
; dw ProtectedMode ; offset for jump
; dw 0x8 ; selector of the segment for the jump
; http://www.nasm.us/doc/nasmdo10.html#section-10.1
;;;;;;;;;;;
;; Calls ;;
;;;;;;;;;;;
print_string:
lodsb ; grab a byte from SI
or al, al ; logical or AL by itself
jz .done ; if the result is zero, get out
mov ah, 0x0E
int 0x10 ; otherwise, print out the character!
jmp print_string
.done:
ret
get_string:
xor cl, cl
.loop:
mov ah, 0x00
int 0x16 ; wait for keypress
cmp al, 8 ; backspace pressed?
je .backspace ; yes, handle it
cmp al, 13 ; enter pressed?
je .done ; yes, we're done
cmp cl, 31 ; 31 chars inputted?
je .loop ; yes, only let in backspace and enter
mov ah, 0x0E
int 0x10 ; print out character
stosb ; put character in buffer
inc cl
jmp .loop
.backspace:
or cl, cl ; zero? (start of the string)
jz .loop ; if yes, ignore the key
dec di
mov byte [di], 0 ; delete character
dec cl ; decrement counter as well
mov ax, 0x0E08
int 0x10 ; backspace on the screen
mov al, ' '
int 0x10 ; blank character out
mov al, 8
int 0x10 ; backspace again
jmp .loop ; go to the main loop
.done:
mov al, 0 ; null terminator
stosb
mov ax, 0x0E0D
int 0x10
mov al, 0x0A
int 0x10 ; newline
ret
strcmp:
.loop:
mov al, [si] ; grab a byte from SI
cmp al, [di] ; are SI and DI equal?
jne .done ; nope, we're done.
or al, al ; zero?
jz .done ; yes, we're done.
inc di ; increment DI
inc si ; increment SI
jmp .loop ; loop!
.done:
ret
clrscr:
mov ax, 0x0600
xor cx, cx
mov dx, 0x174F
xor bh, bh
int 0x10
ret
Waitingloop:
pushad
mov ebx,0x9FFFF
.loop:
dec ebx
jnz .loop
popad
ret
PutStr_32:
pushad
.nextchar:
lodsb
or al, al
jz .end
mov byte [ds:ecx], al
inc ecx
mov byte [ds:ecx], dl
inc ecx
jmp .nextchar
.end:
popad
ret
; You load the address you want to output to in ecx(0x0B8000), the address of the string (has to be null terminated)
; you want to print in esi and the attributes in dl (personally 0x04, red char on black background) ,
; the lodsb, loads one byte from esi into al then increments esi, then it checks for a null terminator
; then it moves the char into the write position in vid mem and then increments ecx and writes the attributes,
; the loops until it finds a null pointer at which point it breaks....
cli ; clear interrupts
mov eax, cs ; CS ---> EAX
mov ds, ax ; CS ---> DS
shl eax, 4 ; CS * 0x10 = linear address
mov [CODE_Desc+2], ax ; linear address ---> base of code descriptor
mov [DATA_Desc+2], ax ; linear address ---> base of data descriptor
shr eax, 16 ;
mov [CODE_Desc+4], al ;
mov [DATA_Desc+4], al ;
mov eax, cs ; base of GDT
shl eax, 4
add eax, NULL_Desc
mov [Base], eax ; base of GDT ---> GDTR
mov [Limit], WORD gdtr - NULL_Desc - 1 ; limit of GDT ---> GDTR
lgdt [gdtr] ; load GDT via GDTR
mov eax, cr0 ; switch-over to Protected Mode
or eax, 1 ; set bit 0 of CR0 register
mov cr0, eax ;
jmp word 0x8:ProtectedMode
; db 0xea ; instruction code for FAR JUMP
; dw ProtectedMode ; offset for jump
; dw 0x8 ; selector of the segment for the jump
; http://www.nasm.us/doc/nasmdo10.html#section-10.1
;;;;;;;;;;;
;; Calls ;;
;;;;;;;;;;;
print_string:
lodsb ; grab a byte from SI
or al, al ; logical or AL by itself
jz .done ; if the result is zero, get out
mov ah, 0x0E
int 0x10 ; otherwise, print out the character!
jmp print_string
.done:
ret
get_string:
xor cl, cl
.loop:
mov ah, 0x00
int 0x16 ; wait for keypress
cmp al, 8 ; backspace pressed?
je .backspace ; yes, handle it
cmp al, 13 ; enter pressed?
je .done ; yes, we're done
cmp cl, 31 ; 31 chars inputted?
je .loop ; yes, only let in backspace and enter
mov ah, 0x0E
int 0x10 ; print out character
stosb ; put character in buffer
inc cl
jmp .loop
.backspace:
or cl, cl ; zero? (start of the string)
jz .loop ; if yes, ignore the key
dec di
mov byte [di], 0 ; delete character
dec cl ; decrement counter as well
mov ax, 0x0E08
int 0x10 ; backspace on the screen
mov al, ' '
int 0x10 ; blank character out
mov al, 8
int 0x10 ; backspace again
jmp .loop ; go to the main loop
.done:
mov al, 0 ; null terminator
stosb
mov ax, 0x0E0D
int 0x10
mov al, 0x0A
int 0x10 ; newline
ret
strcmp:
.loop:
mov al, [si] ; grab a byte from SI
cmp al, [di] ; are SI and DI equal?
jne .done ; nope, we're done.
or al, al ; zero?
jz .done ; yes, we're done.
inc di ; increment DI
inc si ; increment SI
jmp .loop ; loop!
.done:
ret
clrscr:
mov ax, 0x0600
xor cx, cx
mov dx, 0x174F
xor bh, bh
int 0x10
ret
Waitingloop:
pushad
mov ebx,0x9FFFF
.loop:
dec ebx
jnz .loop
popad
ret
PutStr_32:
pushad
.nextchar:
lodsb
or al, al
jz .end
mov byte [ds:ecx], al
inc ecx
mov byte [ds:ecx], dl
inc ecx
jmp .nextchar
.end:
popad
ret
; You load the address you want to output to in ecx(0x0B8000), the address of the string (has to be null terminated)
; you want to print in esi and the attributes in dl (personally 0x04, red char on black background) ,
; the lodsb, loads one byte from esi into al then increments esi, then it checks for a null terminator
; then it moves the char into the write position in vid mem and then increments ecx and writes the attributes,
; the loops until it finds a null pointer at which point it breaks....
times 1024-($-$$) db 0
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 13:16:24 20.03.2009, insgesamt 5-mal bearbeitet
Ich habe das OS auf einem alten PC (AMD 1400 MHz) getestet. Dort rebootet er bei Eingabe "pm", während bochs das OS brav von Diskette schluckt. Weiß jemand, wo der "real wirksame" Fehler liegt?
Didaktisch steckt das "OS" momentan im Schlamm, da ich momentan nicht sicher bin, wie man das Ganze am besten in kleine Häppchen verpacken kann. Was würdet ihr in welche inc-Dateien packen? Man kann das ja auch kaum noch sinnvoll hier posten (warum gibt es keine scroll-Funktion für solche Dateien wie in anderen Foren?).
lgdt [gdt] findet man in google 216 mal.
lgdt [gdtr] findet man in google 740 mal.
Letzteres ist ja auch richtiger, werde das anpassen.
Ich hoffe zumindest, dass ich Dich so richtig verstanden habe.
Wie hättest Du dies mit 'test op,op' (AND-Verknüpfung ohne Speicherung, nur Flags setzen) gemacht? Das nimmt man doch eher, um zu testen, ob ein bestimmtes Bit gesetzt ist? z.B. test AL, 64 (check auf Bit 6)
Ja, so war das gedacht, sieht ok.
"test" kannst du sowohl, wie du schreibst, benutzen, um einzelne Bits zu testen, allerdings erzielst du mit "test al, al" praktisch das gleiche Ergebnis wie mit "or al, al" (oder wohl auch "and al, al", wenn wir schon dabei sind), ist aber IMHO zumindest konsequenter.
Erhard Henkes schrieb:
Welchen offset würde man da mittels org wählen und warum?
Das offset, an den du den Code im RAM bzgl. Segment 0 kopierst. Da bietet sich IMHO zB. 8000h an...
Erhard Henkes schrieb:
Mir ist noch nicht klar, warum die meisten den Kernel (oder ersten Teil des Kernels) nach 10000h setzen.
...aus dem selben Grund, aus dem wohl auch viele Frickler ihren Code nach 10000h laden: Es ist einfach eine schoene runde Adresse. Und bei 10000h laeuft man praktisch auch keine Gefahr mehr, irgendwo mit den Untiefen der BDA in Konflikt zu geraten. 8000h sollte da AFAIK aber auch sicher und "rund" genug sein.
Erhard Henkes schrieb:
Ich habe das OS auf einem alten PC (AMD 1400 MHz) getestet. Dort rebootet er bei Eingabe "pm", während bochs das OS brav von Diskette schluckt. Weiß jemand, wo der "real wirksame" Fehler liegt?
Ohne zu testen, kommt mir folgendes eigenartig vor:
Assembler Code:
;z234-237, kernel.asm
mov WORD [CODE_Desc+2], 0 ; code segment base address = 0
mov WORD [DATA_Desc+2], 0 ; data segment base address = 0
mov BYTE [CODE_Desc+4], 0 ; code segment base address = 0
mov BYTE [DATA_Desc+4], 0 ; data segment base address = 0
Assembler Code:
;z234-237, kernel.asm
mov WORD [CODE_Desc+2], 0 ; code segment base address = 0
mov WORD [DATA_Desc+2], 0 ; data segment base address = 0
mov BYTE [CODE_Desc+4], 0 ; code segment base address = 0
mov BYTE [DATA_Desc+4], 0 ; data segment base address = 0
Assembler Code:
;z234-237, kernel.asm
mov WORD [CODE_Desc+2], 0 ; code segment base address = 0
mov WORD [DATA_Desc+2], 0 ; data segment base address = 0
mov BYTE [CODE_Desc+4], 0 ; code segment base address = 0
mov BYTE [DATA_Desc+4], 0 ; data segment base address = 0
Halte ich durchaus fuer problematisch, mit den RM-Segmenten im PM auf den Speicher zuzugreifen. -> Mein Tipp waere, das Ganze als initialisierte Daten anzulegen, dann kannst du dir diesen Code (wie die restliche Initialisierung in z 104 eigentlich auch) sparen.
Ausserdem bekommst du potentiell Probleme, wenn du auf Speicher >1MB zugreifst (stack), ohne die A20 eingeschaltet zu haben.
...
Erhard Henkes schrieb:
Vielleicht kann Nobuo T seine Idee daneben stellen zum Vergleich.
...seufz... Das wuerde einiges an Arbeit, da ich deinen Code erst recht nicht ohne Testen umfangreich umschreiben und hier einstellen will. Mal sehen, ob ich heute Abend Zeit und Nerv finde.
Zu deinem clearscreen32:
32Bit loop und rep benutzen ecx... Vielleicht liegt es daran.
Zu test op,op:
Ich dachte, Du würdest dies als Ersatz für cmp op,op einsetzen wollen.
Ich habe leider noch nicht genau verstanden, wie Du das mit den initialisierten Daten als Vereinfachung meinst. Ein konkreter Vorschlag würde hier echt helfen.
Wie kann man in Bochs eigentlich die Belegung des Speichers visualisieren?
Kenne bisher nur den Bochs Debugger mit der Register-Visualisierung.
Ist das biedere Bochs eigentlich das ideale Tool bezüglich Visualisierung oder hat jemand bessere Erfahrung mit anderen Emulationen gemacht? Einer der didaktischen Probleme ist ja, dass man überhaupt nicht "sieht", in welchem Mode (RM, PM) man sich befindet, ob A20 aktiviert ist, usw. Da ist eines der Probleme beim Einstieg in die OS-Entwicklung.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 19:46:51 20.03.2009, insgesamt 1-mal bearbeitet
...seufz... Das wuerde einiges an Arbeit, da ich deinen Code erst recht nicht ohne Testen umfangreich umschreiben und hier einstellen will. Mal sehen, ob ich heute Abend Zeit und Nerv finde.
Es ist wichtig, dass man die Erklärungen und Zeichnungen möglichst einfach und richtig versteht. Ansonsten versteht man die praktische Abfolge hinterher falsch. Könnt ihr das bitte mal checken?
Die nächste geistige Reihefolge wäre dann z.B.:
1) Selektor (hier kommen GDT und LDT sowie die Privilege Levels 0-3 ins Spiel)
2) Aufbau GDT
3) Welche Deskriptoren gibt es noch?
4) Interrupts
5) Programmablauf in PM
6) Multitasking
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 17:02:19 21.03.2009, insgesamt 1-mal bearbeitet
Ich habe deine Quellcodes mal etwas umgebaut. Um die Scrollerei einzudaemmen, habe ich die Dateien mal hier hochgeladen.
Waere praktisch, wenn du die Quellcodes in deinem Tutorial auch direkt als Downloads haettest (habe sie zumindest nicht gefunden).
Eine aufschlussreiche Seite zum A20-Kram ist zB. hier zu finden.
Zu deinem Text:
Begriffe im Deutschen werden meist ziemlich nach Lust und Laune verwendet, aber
lineare und physikalische Adresse ist AFAIK nicht das Gleiche. Spaetestens beim Paging nicht: Da wird aus der linearen (zT. dann auch logische Adresse genannt) Adresse mit der PageTable erst die physikalische Adresse gewurstet.
IMHO koenntest du noch etwas ausfuehrlicher darauf eingehen, warum fuer Multitasking nun auf einmal Schutzmechanismen noetig sind, und warum fuer DOS nicht, bzw. was unter Multitasking grob zu verstehen ist.
Ansonsten bin ich mir nicht sicher, ob das so schon "leicht verstaendlich" ist.
IMHO ist dein Fazit noch etwas verwirrend und deinen "5)" in der praktischen Umsetzung kann nicht mal ich so nachvollziehen.
lineare und physikalische Adresse ist AFAIK nicht das Gleiche.
Ja, da hast Du ab dem 386er völlig Recht. Man findet in der Literatur ständig einen Mischmasch aus 286er 16-Bit-Protected-Mode und 386er 32-Bit-Protected-Mode. Beim 286er ist die "lineare" Adresse identisch mit der physikalischen Adresse, beim 386er muss letztere aus der ersten wegen des Pagings berechnet werden. Auch die Deskriptoren sind beim Sprung vom 286er zum 386er von sechs auf acht Byte gewachsen. Erst durch direkten Vergleich beider Systeme versteht man, warum das heute so chaotisch angeordnet ist.
Nobuo, vielen Dank für Deinen Quellcode! Ich werde eine Seite mit Download-Links einbauen. Dort wird es dann auch einen Bereich "Erlkönig" geben, wo wir alphas und betas ablegen können.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 18:24:36 21.03.2009, insgesamt 1-mal bearbeitet
IMHO koenntest du noch etwas ausfuehrlicher darauf eingehen, warum fuer Multitasking nun auf einmal Schutzmechanismen noetig sind, und warum fuer DOS nicht, bzw. was unter Multitasking grob zu verstehen ist.
Akzeptiert.
Das "Fazit" werde ich ebenfalls überdenken.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Test 1: Auf Floppy schreiben und "echt" booten.
Resultat: Perfekt!
Erstes Code-Studium:
Kernel auf 0x08000 gesetzt wie diskutiert,
buffer weg,
A20 eingeschaltet (die berühmte 21. Adressleitung),
ansprechende Ausgabe der Meldung im PM
@Nobuo T: Danke für die fundierte Unterstützung!
@all: bitte den Code checken, gute Ideen werden sofort angenommen. Vielleicht kann man auch im Real Mode noch etwas Interessantes ergänzen, z.B. den CPUID-Befehl?!
Zitat:
Waere praktisch, wenn du die Quellcodes in deinem Tutorial auch direkt als Downloads haettest (habe sie zumindest nicht gefunden).
Ich werde diesen m.E. gelungenen Einstieg in den PM nun didaktisch aufarbeiten, damit das Tutorial Einsteigern das Leben so leicht wie möglich macht. Da fehlen noch einige gute Abbildungen. Die Unterscheidung zwischen 16-Bit- und 32-Bit-Protected Mode ist didaktisch wichtig. Paging, Unterschied lineare/phys. Adresse ebenso.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 21:19:48 22.03.2009, insgesamt 6-mal bearbeitet
Vielleicht kann man auch im Real Mode noch etwas Interessantes ergänzen
Vielleicht folgendes:
Wenn man im PM ein Segmentregister initialisiert und dann wieder in den RM wechselt, bleibt die Segmentgröße des Selektors für das initialisierte Segmentregister erhalten. Dadurch hat man über dieses Segmentregister Zugriff auf die gesamte Segmentgröße auch im RM.
Was heißt, die Begrenzung auf 1MB RAM im RM kann relativ einfach außer Kraft gesetzt werden. Das Segmentregister darf nur nicht im RM wieder verändert werden. Sonst ist der Spaß vorbei. Aber man ja beliebig oft hin und her wechseln.
@+gjm+:
Das klingt ja echt abenteuerlich!
Findet man dazu bereits einen Link im Internet oder ist das Geheimwissen?
Wie läuft denn da die genaue Adressierung im zurück geschalteten RM ab?
@Nobuo T: Deine konstruktiven Vorschläge wurden aufgenommen und hoffentlich weitgehend umgesetzt. Multitasking und A20 Gate wird jetzt auch breiter besprochen. Das ist schon ganz schön viel Text, hoffentlich brauchbare Abbildungen und ziemlich viele Zeilen Assembler, nur um die Basis PM zu erreichen. Didaktisch ist es wirklich nicht ganz einfach die x86 Historie verständlich zu erklären, ohne allzu langatmig zu werden.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 00:26:05 23.03.2009, insgesamt 2-mal bearbeitet
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; read kernel from floppy disk ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
load_kernel:
xor ax, ax ; mov ax, 0 => function "reset"
int 0x13
jc load_kernel ; trouble? try again
mov bx, 0x8000 ; set up start address of kernel
; set parameters for reading function
; 8-bit-wise for better overview
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"
int 0x13
jc load_kernel ; trouble? try again
; show loading message
mov si,loadmsg
call print_string
;;;;;;;;;;;;;;;;;;
; jump to kernel ;
;;;;;;;;;;;;;;;;;;
jmp bx ; address of kernel
;;;;;;;;;
; calls ;
;;;;;;;;;
; print string
print_string:
mov ah, 0x0E ; VGA BIOS fnct. 0x0E: teletype
.loop:
lodsb ; grab a byte from SI
test al, al ; NUL?
jz .done ; if the result is zero, get out
int 0x10 ; otherwise, print out the character!
jmp .loop
.done:
ret
;;;;;;;;;;;;;
; constants ;
;;;;;;;;;;;;;
loadmsg db "bootloader message: loading kernel ...",13,10,0
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; read kernel from floppy disk ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
load_kernel:
xor ax, ax ; mov ax, 0 => function "reset"
int 0x13
jc load_kernel ; trouble? try again
mov bx, 0x8000 ; set up start address of kernel
; set parameters for reading function
; 8-bit-wise for better overview
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"
int 0x13
jc load_kernel ; trouble? try again
; show loading message
mov si,loadmsg
call print_string
;;;;;;;;;;;;;;;;;;
; jump to kernel ;
;;;;;;;;;;;;;;;;;;
jmp bx ; address of kernel
;;;;;;;;;
; calls ;
;;;;;;;;;
; print string
print_string:
mov ah, 0x0E ; VGA BIOS fnct. 0x0E: teletype
.loop:
lodsb ; grab a byte from SI
test al, al ; NUL?
jz .done ; if the result is zero, get out
int 0x10 ; otherwise, print out the character!
jmp .loop
.done:
ret
;;;;;;;;;;;;;
; constants ;
;;;;;;;;;;;;;
loadmsg db "bootloader message: loading kernel ...",13,10,0
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; read kernel from floppy disk ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
load_kernel:
xor ax, ax ; mov ax, 0 => function "reset"
int 0x13
jc load_kernel ; trouble? try again
mov bx, 0x8000 ; set up start address of kernel
; set parameters for reading function
; 8-bit-wise for better overview
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"
int 0x13
jc load_kernel ; trouble? try again
; show loading message
mov si,loadmsg
call print_string
;;;;;;;;;;;;;;;;;;
; jump to kernel ;
;;;;;;;;;;;;;;;;;;
jmp bx ; address of kernel
;;;;;;;;;
; calls ;
;;;;;;;;;
; print string
print_string:
mov ah, 0x0E ; VGA BIOS fnct. 0x0E: teletype
.loop:
lodsb ; grab a byte from SI
test al, al ; NUL?
jz .done ; if the result is zero, get out
int 0x10 ; otherwise, print out the character!
jmp .loop
.done:
ret
;;;;;;;;;;;;;
; constants ;
;;;;;;;;;;;;;
loadmsg db "bootloader message: loading kernel ...",13,10,0
times 510-($-$$) db 0
db 0x55
db 0xAA
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 02:19:24 23.03.2009, insgesamt 5-mal bearbeitet
Cortex-M3 Fan
Unregistrierter
Cortex-M3 Fan Unregistrierter
10:04:23 23.03.2009 Titel:
Destruktive (Troll-)Beiträge werden mich nicht aufhalten oder irritieren.
Bessere Ideen bezüglich der Umsetzung oder Gesasmtkonstruktion sind dennoch gerne gesehen.
Begründe mir lieber mal jemand, warum IBM das Boot-Programm genau nach 7C00h gesendet hat. Die exakte Erklärung habe ich bisher noch nirgends gefunden.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Naja, mit dem Wegoptimieren muss man immer aufpassen. Das ist sehr oft auch ein Kompromiss zwischen Stabilitaet, Kompatibilitaet und Optimierung, der einen gewisser "Verhaeltnisbereich" IMHO nicht verlassen sollte.
Erhard Henkes schrieb:
Wir schreiben den Offset der Sprung-Adresse des Kernels nachs BX.
Später springen wir ohne Verwendung dieses Registers.
Code:
mov bx, 0x8000 ; set up start address of kernel
;...
jmp 0x0000:0x8000 ; address of kernel
Code:
mov bx, 0x8000 ; set up start address of kernel
;...
jmp 0x0000:0x8000 ; address of kernel
Code:
mov bx, 0x8000 ; set up start address of kernel
;...
jmp 0x0000:0x8000 ; address of kernel
ES:BX ist notwendig für int 0x13 als "Buffer Address Pointer".
So läuft es auf jeden Fall auch:
Code:
mov bx, 0x8000 ; set up start address of kernel
;...
jmp bx ; address of kernel
Code:
mov bx, 0x8000 ; set up start address of kernel
;...
jmp bx ; address of kernel
Code:
mov bx, 0x8000 ; set up start address of kernel
;...
jmp bx ; address of kernel
Wäre letzteres o.k.? Ist das didaktisch nicht besser? Man verwendet BX auch im Sprungbefehl sofort sofort.
Im Code vor dem long jump zum Kern, kommen wir ganz gut zurecht, ohne genau zu wissen, welchen Wert cs hat, da alle Spruenge etc. nur relativ zum aktuellen Offset (IP) adressieren. Wenn du aber den Sprung ueber bx machst, hast du ein absolutes Offset zu CS und musst dir daher sicher sein, dass cs auch 0 ist. Sonst schiesst du am Ziel vorbei.
Kann also sein, dass das funktioniert. Die Chancen stehen aber auch nicht schlecht, dass es das auf anderen PCs nicht tut.
Man könnte den Kernel wohl auch von anderer Stelle laden?
Da unser System dies nicht macht, könnte man [bootdrive] doch auch völlig weg lassen?
Bei der ersten Verwendung hatte ich die Sache in deinem alten Code auch auskommentiert (z. 16). Beim 2. Mal bin ich nicht sicher, ob dl vom Interrupt nicht geschrottet wird.
Ansonsten ist das wieder eine Kompatibilitaetsfrage. Du kannst natuerlich die Info des BIOS wegschmeissen, dl einfach immer auf 0 (fdd A setzen und damit ausschliessen, dass jemand den Code jemals von was Anderem (2. Floppy, USB oder was auch immer) startet, aber IMHO sind es diese 2 Zeilen irgendwie nicht wert.
Deshalb habe ich sie drin gelassen.
Erhard Henkes schrieb:
Label load_kernel1 kann jetzt ja auch entfallen nach Nobuos Änderung?
Da hast du allerdings recht.
Erhard Henkes schrieb:
@+gjm+:
Das klingt ja echt abenteuerlich!
Findet man dazu bereits einen Link im Internet oder ist das Geheimwissen?
Wie läuft denn da die genaue Adressierung im zurück geschalteten RM ab?
Nunja, IMHO eher ein glitch denn ein echtes feature. Gelegentlich findet man die Sache als "Flat Real Mode", "Unreal Mode" oder auch "Voodoo Mode" bezeichnet und in alten, frickeligen DOS-Spielen oder Demos verwendet.
Hier gibt es etwas darueber zu lesen.
Erhard Henkes schrieb:
Begründe mir lieber mal jemand, warum IBM das Boot-Programm genau nach 7C00h gesendet hat. Die exakte Erklärung habe ich bisher noch nirgends gefunden.
Vermutung:
Historische Ursachen. Es war einfach das obere Ende des RAMs (32kb).
Hallo Nobuo T, danke für deine Antwort. Bei mir wurde DL nicht geschrottet (sind die BIOS-Interrupts nicht gleich realisiert?), der Sprung mit BX alleine klappte ebenfalls. OK, ich werde auf Dich hören und das nur als Kommentar angeben. Load_kernel1 wird gestrichen. Danke für die Links! Voodoo ...
Noch eine andere didaktische Frage: Die Bezeichnungen KB, MB etc. sind ja inzwischen nicht mehr eindeutig, vor allem bei MB, GB. Würdet ihr auf KiB, MiB, GiB etc. umsteigen? Die Akzeptanz dieses neuen Vorschlages ist ja noch gering.
http://de.wikipedia.org/wiki/Kibibyte#IEC-Pr.C3.A4fixe_zur_Basis_2
Als Alternative zu Bochs und VirtualPC kann ich noch VirtualBox empfehlen (kostenlos).
Funktionierte bei mir auf Anhieb. Um die Bootdiskette einzubinden muss diese nochnichtmal in ein Image verpackt sein...
... Das Tutorial gefällt mir sehr gut ... Als Alternative zu Bochs und VirtualPC kann ich noch VirtualBox empfehlen (kostenlos). Funktionierte bei mir auf Anhieb. Um die Bootdiskette einzubinden muss diese noch nicht mal in ein Image verpackt sein ...
Danke für das positive Feedback und den Tipp mit VirtualBox. Das werde ich mir anschauen. Denn gerade diese Dinge sind didaktisch wichtig, denn jedes Emulations-Programm hat seine Schwächen und Stärken. Nur wenn Theorie, Praxis und Tools Hand in Hand gehen, macht die Sache Freude und treibt die Kreativität vorwärts.
You zero SS and SP and then you use the stack (implicitly by CALL). That looked strange first. It's not broken, but it is not very intuitive. Maybe you want to consider to note this in your text (I didn't see anything about that, maybe I just missed it) and explain why and how that works.
Vielleicht kannst Du da noch etwas zu Deiner Idee erläutern. Da klafft noch eine "didaktische Lücke".
Das Thema Stack würde ich gerne auch noch beschreiben (Prinzip, SS, SP, Nutzung durch call, ...). Ich war allerdings über deine Lösung so verblüfft, dass ich erst mal genauer hinschauen wollte.
Auch diese Zeile wirkt auf den ersten Blick merkwürdig.
add sp, -0x40 ; make room for input buffer (64 chars)
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 20:33:49 23.03.2009, insgesamt 1-mal bearbeitet
Zum Thema Emulatoren könntest Du doch ein eigenes Kapitel machen, indem kurz beschrieben wird, wie man damit das OS zum laufen kriegt und welche Vor-/Nachteile das Programm hat, anstatt gelegentlich zwischendurch weitere Emulatoren vorzustellen...
@fricky: SP dürfte aber zu dem zeitpunkt auf 0 sein, weshalb das subtrahieren von 40h (= addieren von -40h?) komisch ist.
Zumindestens kommt es mir komisch vor, wo liegt der (mein) denkfehler?
Zum Thema Emulatoren könntest Du doch ein eigenes Kapitel machen, indem kurz beschrieben wird, wie man damit das OS zum laufen kriegt und welche Vor-/Nachteile das Programm hat, anstatt gelegentlich zwischendurch weitere Emulatoren vorzustellen...
Ja, ist mir auch aufgefallen. Ursprünglich wollte ich nur Bochs verwenden, allerdings sind die Emulatoren so wichtig, dass man alle vorstellen sollte. Werde das konzentrieren. Allerdings erst hinten, denn vorne lenkt das doch stark ab. Nicht jeder arbeitet gerne gleichzeitg mit 10-15 Tools.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Das man den Stackpointer nach "unten" wandern lässt, um Platz zu schaffen, ist klar. Aber wie gesagt, ist der Wert vorher bei 0, wenn ich nicht irre. Also hinterher -40. Warten wir auf den Kommentar von Nobuo T. Das hat er genial "verbrochen". Vorher hatten wir den Stack bei 0x90000 (http://www.henkessoft.de/OS_Dev/Bilder/Speicherbelegung.JPG), wo viele ihn hin knallen. Unten bei 0 ist doch auch die IVT. Aber es läuft, das ist die Hauptsache, die theor. Erklärung kommt sicher noch. Vielleicht ein genialer "wrap"
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 23:50:03 23.03.2009, insgesamt 6-mal bearbeitet
Richtig, +fricky. Push macht Platz auf dem Stack, indem 2 (oder 4, oder wie gross der Operand auch immer ist) Byte von (e)sp subtrahiert werden und schreibt dann den Operand dann nach ss:(e)sp.
Hier wird halt nur Platz auf dem Stack reserviert, erstmal ohne was rein zu schreiben.
Zu ss und sp mit 0 initialisieren:
Ist vielleicht nicht sofort intuitiv, aber hier IMHO ziemlich praktisch.
Wie gesagt hat man es beim x86 mit einem full descending stack zu tun. Wenn also bei sp=0 etwas auf den Stack gepackt wird, gibt es beim Subtrahieren von sp erstmal einen schoenen Ueberlauf, bzw. wrap-arround: zB. 0 - 2 = 0xFFFE. Genau da landet dann der erste Wert.
Der Code sieht ansonsten beim ersten drueber schauen ganz gut aus. Aber meine msg00 haettest du eigentlich auch gleich ganz loeschen koennen, statt sie nur zu verschieben. War da nur zu debug-zwecken. Die Ausgabe habe ich wie es aussieht auch wieder entfernt, aber den Text vergessen.
Und zum Teil ueber die A20 zunaechst noch: Ueber die Ports 60h und 64h sprichst du erstmal nur mit dem Controller auf dem Mainboard. Darueber kannst du dann (seriell) etwas an die Tastatur schicken. Und D1 an 64h ist einfach ein Kommando, das bewirkt, dass das naechste Byte auf Port 60h beim outputport landet, statt bei der Tastatur.
edit: Hui, haette nicht gedacht, dass einfache Integer-Arithmetik (signed/unsigned Addition vs. Subtraktion und Ueberlaeufe) hier fuer solche Verwirrung sorgt.
Oder hat das damit zu tun, dass da der Stack involviert ist? Das spielt wie gesagt in dem Zusammenhang ueberhaupt keine Rolle.
Habe mal die zip Datei 20090321_eh_os.zip runtergeladen, Makefile ein wenig angepasst (wegen meiner nasm Installation), das ganze mit meinen Werkzeugen gebaut und mit den binär Dateien in der zip-Datei verglichen. Alles gleich
Unter bochs ausprobiert - es läuft.
Nun wollte ich mal mit ndisasm ein wenig disassemblieren, hie und da mal schauen und schon gibt es ein kleines "Problem" mit kernel.bin. In der kernel.asm gibt es ja mal 16-Bit, mal 32-Bit Code, was mit [Bits 32] und [BITS 16] "umgeschaltet" wird. Nun, ndisasm kommt damit nicht klar oder ich kenne noch nicht die Möglichkeit, wie man es hinkriegt, dass alles zusammen sauber disassembliert wird... Wäre vielleicht ein Splittern der kernel.asm in zwei Dateien sinnvoll? Eine mit [BITS 16], die andere mit [BITS 32]?
Eine Sache bezüglich Disassemblieren noch: Am Ende des Bootloaders und des Kernels wird mit Nullen aufgefüllt (X ist 510 oder 1024):
Code:
times X-($-$$) db 0
Code:
times X-($-$$) db 0
Code:
times X-($-$$) db 0
Was würde gegen z.B. so was sprechen:
Code:
times X-($-$$) hlt
Code:
times X-($-$$) hlt
Code:
times X-($-$$) hlt
Also Auffüllen mit HLT-Befehlen... Ist nur ein Vorschlag, aber ich persönlich sehe ein Paar Vorteile:
- beim Disassemblieren sieht man sofort, wo das Ende ist (ok, mit Nullen sieht man es auch, aber man hat lauter add [bx+si],al stehen, was irgendwie unschön ist)
- zumindest psychologische Wirkung, man fühlt sich ruhiger, weil, wenn die CPU dort warum auch immer ankommt, bleibt sie dort stehen und es passiert nichts
Nun wollte ich mal mit ndisasm ein wenig disassemblieren, hie und da mal schauen und schon gibt es ein kleines "Problem" mit kernel.bin. In der kernel.asm gibt es ja mal 16-Bit, mal 32-Bit Code, was mit [Bits 32] und [BITS 16] "umgeschaltet" wird. Nun, ndisasm kommt damit nicht klar oder ich kenne noch nicht die Möglichkeit, wie man es hinkriegt, dass alles zusammen sauber disassembliert wird...
Ja, ist ein Problem. Die Situation hat man auch nicht all zu haeufig, dass man 16- und 32-Bit code in einem binary hat.
Ehrlich gesagt bin ich in die Verlegenheit auch noch nicht wirklich gekommen, bzw. hoechstens in einem Debugger - den kann man oft bei Bedarf umschalten.
abc.w schrieb:
Wäre vielleicht ein Splittern der kernel.asm in zwei Dateien sinnvoll? Eine mit [BITS 16], die andere mit [BITS 32]?
Problem beim Aufteilen waere, dass man entweder beim 2. Teil mit dem Start-Offet (org) aufpassen muesste, oder Verschnitt haette, wenn man es an eine groessere, festgelegte Adresse packt.
Da ist die uebersichtliche Disassemblierbarkeit mit einem einfachen disasm IMHO irgendwie nachrangig.
abc.w schrieb:
Eine Sache bezüglich Disassemblieren noch: Am Ende des Bootloaders und des Kernels wird mit Nullen aufgefüllt (X ist 510 oder 1024):
Code:
times X-($-$$) db 0
Code:
times X-($-$$) db 0
Code:
times X-($-$$) db 0
Was würde gegen z.B. so was sprechen:
Code:
times X-($-$$) hlt
Code:
times X-($-$$) hlt
Code:
times X-($-$$) hlt
Also Auffüllen mit HLT-Befehlen... Ist nur ein Vorschlag, aber ich persönlich sehe ein Paar Vorteile:
- beim Disassemblieren sieht man sofort, wo das Ende ist (ok, mit Nullen sieht man es auch, aber man hat lauter add [bx+si],al stehen, was irgendwie unschön ist)
- zumindest psychologische Wirkung, man fühlt sich ruhiger, weil, wenn die CPU dort warum auch immer ankommt, bleibt sie dort stehen und es passiert nichts
Naja, grundsaetzlich vielleicht nicht schlecht, was die Uebersicht beim Disassemblieren betrifft, hat aber ansonsten nicht viel Zweck. Ob der Rechner nun stehen bleibt bis zum naechsten IRQ oder gleich ein wenig dummes Zeug rechnet ist bei so einem gravierenden Problem dann wohl auch ein wenig egal.
Jungs, ihr seid richtig kreativ! Macht richtig Spaß mit euch. Der eine baut einen Wrap in den Stack, dass den Profis vom osdev.org der Verstand stehen bleibt und ein anderer schlägt "times X-($-$$) hlt" vor.
Das mit dem Stack auf 0 habe ich bisher noch nicht gesehen, finde es aber richtig gut. Ich habe es mir einfach als wrap vorgestellt, wie damals beim A20-Gate.
Ich habe mal "times X-($-$$) hlt" in Google eingegeben. Null Fundstellen!
Das ist doch echt ein Grund, dies zu verwenden. Die massenweise F4 im Hex-Editor sehen richtig lustig aus: http://www.henkessoft.de/OS_Dev/Bilder/boot_bin_hex.JPG
@abc.w: die "hlt" sind schon im Tut eingebaut und hochgeladen.
Die msg00 ist schon entfernt (ich hatte sie mal aufgehoben, weil ich dachte vielleicht bauen wir sie didaktisch noch ein, so mit keystroke und weiter ...).
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 00:28:32 24.03.2009, insgesamt 3-mal bearbeitet
Jungs, ihr seid richtig kreativ! Macht richtig Spaß mit euch. Der eine baut einen Wrap in den Stack, dass den Profis vom osdev.org der Verstand stehen bleibt und ein anderer schlägt "times X-($-$$) hlt" vor.
Tja, in der Tat. So viel war hier schon laenger nicht mehr los. Ist AFAIR uebrigens inzwischen auch der laengste Thread in diesem Forum.
Ich habe mal "times X-($-$$) hlt" in Google eingegeben. Null Fundstellen!
Das X muss man natürlich ersetzen:
"times 510-($-$$) db 0" 380 Fundstellen (auch nicht gerade viel, wir sind schon dabei)
"times 510-($-$$) hlt" 1 Fundstelle: http://kldp.org/node/89199 (leider sind wir nicht Erster! )
Und zum Teil ueber die A20 zunaechst noch: Ueber die Ports 60h und 64h sprichst du erstmal nur mit dem Controller auf dem Mainboard. Darueber kannst du dann (seriell) etwas an die Tastatur schicken. Und D1 an 64h ist einfach ein Kommando, das bewirkt, dass das naechste Byte auf Port 60h beim outputport landet, statt bei der Tastatur.
Es verwirrt beim Lesen wenn ein Labelname wie ein Opcode klingt. Es wäre vielleicht besser, Opcodenamen wie "Reservierte Worte" nicht als Bezeichner zu verwenden.
Noch was zum "Voodoo-Mode":
Drauf verlassen kann man sich ja, solange eben nichts und niemand die Segmentregister verändert. Das wäre z.B. der Fall, wenn man vorhat, ein RM-OS zu schreiben *wegduck* und dann das Format für ausführbare Programme selbst festlegt. Oder (viel einfacher) bei bootfähigen Programmen, die die Hardware testen, oder wirklich schnell mal eine Zahl ausrechnen sollen.
Es verwirrt beim Lesen wenn ein Labelname wie ein Opcode klingt. Es wäre vielleicht besser, Opcodenamen wie "Reservierte Worte" nicht als Bezeichner zu verwenden.
Sehr guter Hinweis. Wird geändert.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
@Erhard Henkes:
Die Screenshots auf deiner Webseite würde ich eher im PNG-Format abspeichern und nicht als JPEG. Das hätte folgende Vorteile gegenüber JPEG:
1) Keine unscharfen Kanten, Text bleibt daher gut lesbar.
2) Oft kleinere Dateigröße.
_________________ Wenn Word für Längeres geeignet wäre, würde es nicht Word, sondern Sentence, Page oder Article heißen.
Noch etwas zur Integer-Arithmetik (signed/unsigned Addition vs. Subtraktion).
Weitergeführt bis zum "Ende" würde die "RealMode" dann in etwa aussehen wie folgt:
Assembler Code:
RealMode:
addsp, -0x40 ; space for input buffer (64 chars)
(...)
addsp, 0x40 ; clean it, please
ret
Assembler Code:
RealMode:
addsp, -0x40 ; space for input buffer (64 chars)
(...)
addsp, 0x40 ; clean it, please
ret
Assembler Code:
RealMode:
addsp, -0x40 ; space for input buffer (64 chars)
(...)
addsp, 0x40 ; clean it, please
ret
Besser wäre da die "klassische" (und verständliche) Logik:
Assembler Code:
RealMode:
subsp, 0x40 ; space for input buffer (64 chars)
(...)
addsp, 0x40 ; clean it, please
ret
Assembler Code:
RealMode:
subsp, 0x40 ; space for input buffer (64 chars)
(...)
addsp, 0x40 ; clean it, please
ret
Assembler Code:
RealMode:
subsp, 0x40 ; space for input buffer (64 chars)
(...)
addsp, 0x40 ; clean it, please
ret
Ob nun sub oder add ist in diesem Fall wohl reine Geschmackssache. Ich versuche sub wenn moeglich aus dem Weg zu gehen, da ich das uebersichtlicher finde (es gehen auch Mythen ueber einen Geschwindigkeitsvorteil um...).
Und wer nicht erkennt, dass
+(-40) == -(+40)
ist, hat noch andere Probleme als sich mit OS-Entwicklung zu befassen.
Was hat das mit "klassischer Logik" zu tun?
Was das "clean please" betrifft: Hast natuerlich prinzipiell recht. So wuerde/koennte man das sauber in einer Prozedur mit lokalen Variablen loesen. Evtl. koennte man einen entsprechenden Kommentar platzieren.
Der Code fuer den Prompt (was du wohl "RealMode" nennst?) ist allerdings keine klassische Prozedur und hat deshalb auch keinen Ausgang, bei dem das irgendwie sinnvoll unterzubringen waere. Anders ausgedrueckt: Wenn der Prompt verlassen und der angelegte Puffer damit nicht mehr gebraucht wird, also freigegeben werden koennte, wird jedes Mal der RM-Stack unmittelbar danach eh eingestampft.
Assembler ist in der Hinsicht nun mal einfach keine Sprache fuer Sauberkeitsfanatiker.
Zunächst habe ich die Code-Abschnitte erklärt, z.B. die "Zeigermechanik" bezüglich des Selektors auf die Deskriptoren-Tabelle. In vielen anderen Artikeln findet man
Code:
jmp 0x8:ProtectedMode
Code:
jmp 0x8:ProtectedMode
Code:
jmp 0x8:ProtectedMode
und
Code:
mov ax, 0x10
mov ds, ax ; data descriptor --> data, stack and extra segment
mov ss, ax
mov es, ax
Code:
mov ax, 0x10
mov ds, ax ; data descriptor --> data, stack and extra segment
mov ss, ax
mov es, ax
Code:
mov ax, 0x10
mov ds, ax ; data descriptor --> data, stack and extra segment
mov ss, ax
mov es, ax
, und niemand erklärt, woher die 0x8 oder 0x10 eigentlich kommen, fallen einfach vom Himmel. Die Berechnung habe ich mittels Abbildung zum Selektoraufbau und Linksshift mit dem wissenschaftlichen "Calculator" nun hoffentlich klar genug dargestellt.
http://www.henkessoft.de/OS_Dev/OS_Dev1.htm#mozTocId533818 please check
Didaktisch bin ich noch nicht völlig zufrieden, da muss noch einiges klarer gezeichnet und umgestellt werden.
Von dieser Seite ( http://forum.osdev.org/viewtopic.php?f=1&t=19451 ) kommen auch noch einge Randbemerkungen.
Nun ist aber immerhin eine ansprechende Basis mit eingeschaltetem A20 Gate und Protected Mode entstanden, die sich allerdings noch völlig autistisch verhält.
Die nächsten Schritte werden dem Kernel die Innen- und Außenwelt systematisch öffnen. Ich denke hier gerade über das Was, Wie, Wann und ASM vs C und die Aufgliederung der Module nach. Auf jeden Fall möchte ich den didaktischen Aspekt ganz vorne sehen. Daher würde mich interessieren, wie Einsteiger ohne Erfahrungen in OS Development das Tutorial bisher einschätzen.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 16:59:20 24.03.2009, insgesamt 4-mal bearbeitet
Die nächsten Schritte werden dem Kernel die Innen- und Außenwelt systematisch öffnen. Ich denke hier gerade über das Was, Wie, Wann und ASM vs C und die Aufgliederung der Module nach.
ok, als faustformel: alles was in C nicht so toll geht, machste in asm (z.b. umschaltung der tasks), alles andere in C. weil du ja x86 benutzt, sollteste dann auch auf die 'task state segmente' eingehen.
Contiki is designed for microcontrollers with small amounts of memory. A typical Contiki configuration is 2 kilobytes of RAM and 40 kilobytes of ROM.
Ist mir schon klar, wo ich mich bei dem x86-Gerödel befinde. Das Programm oder OS wird dann eben ins EEPROM geflasht (mache ich beim Asuro oder Nibo auch: http://www.henkessoft.de/Roboter/Bilder/Nibo_mit_STK500_flashen_Ausschnitt_small.jpg ) anstelle von Floppy oder Hard Disk gebootet. Übrigens sehe ich ASM nicht als Nachteil für den bisherigen OS Start.
Mal eine andere Frage: Kennt sich jemand mit Qemu aus? Ich habe es bisher nicht geschafft, das OS damit zu booten. Lohnt es sich, sich damit herum zu quälen?
Zum Debuggen erscheint mir der Bochs Debugger momentan ideal.
@Nobuo T: Wie debuggst Du bei solchen Entwicklungen genau?
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 23:08:16 24.03.2009, insgesamt 3-mal bearbeitet
Im Abschnitt über die A20-Leitung ist mir folgendes aufgefallen:
Es fehlt der definitive Grund, warum sie unbedingt im PM aktiviert sein muß. Ich will es mal so formulieren: Im PM kann ich auf die paar Byte, die die A20-Leitung adressiert, auch gerne verzichten. Aber warum sollte ich das lieber nicht?
naja, das waren ja nur beispiele, für nicht-preemptive kernels, die entweder continuations oder state machines für's task-switching benutzen. sowas muss nicht unbedingt ins flash, man kann's auch vom laufwerk booten.
Zitat:
Übrigens sehe ich ASM nicht als Nachteil für den bisherigen OS Start.
nachteil ist, je mehr asm du einsetzt, desto mehr nagelst du dein OS auf einen spezifischen prozessor fest. und wenn du so, wie bis jetzt, weiter machst, kannste dein tutorial bald in 'die geheimen tricks der x86-assembler gurus' umtaufen.
Es fehlt der definitive Grund, warum sie unbedingt im PM aktiviert sein muß. Ich will es mal so formulieren: Im PM kann ich auf die paar Byte, die die A20-Leitung adressiert, auch gerne verzichten. Aber warum sollte ich das lieber nicht?
Im Protected Mode kann man problemlos mit 32 Bit arbeiten. Daher muss man das A20-Gate wieder aktivieren, da ansonsten bei bestimmten Speicherzugriffen Fehler auftreten, da die 21. Adressleitung deaktiviert ist. Das bedeutet, das man auf eine andere Speicherstelle zugreifen kann als beabsichtigt. Daher schaltet man A20 ein, bevor man den Kernel und weitere Programmteile startet. Ist das so o.k.?
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 20:59:59 25.03.2009, insgesamt 1-mal bearbeitet
@Erhard Henkes: Inwiefern nervt Sund VirtualBox eigentlich bei dir mit Werbung?
Bei mir erscheinen nur gelegentlich Tipps und Hinweise auf Updates, ich finde diese Passage zur Werbung daher etwas übertrieben...
wenn du so, wie bis jetzt, weiter machst, kannste dein tutorial bald in 'die geheimen tricks der x86-assembler gurus' umtaufen.
Ja, da hast Du evtl. Recht.
Ich denke, jeder verfolgt beim Programmieren seine eigene Philosophie. Und in Assembler ist es irgendwie so, je länger man sich damit beschäftigt, desto verschwommener wird die Grenze "tricks" und "normal".
Zum Beispiel einfache Zeile aus kernel.asm:
Code:
xor cx, cx
Code:
xor cx, cx
Code:
xor cx, cx
Warum nicht einfach:
Code:
mov cx, 0
Code:
mov cx, 0
Code:
mov cx, 0
Oder noch eine Zeile:
Code:
or cl, cl ; zero? (start of the string)
Code:
or cl, cl ; zero? (start of the string)
Code:
or cl, cl ; zero? (start of the string)
Warum nicht einfach:
Code:
cmp cl, 0
Code:
cmp cl, 0
Code:
cmp cl, 0
Und man braucht nicht einmal ein Kommentar dazu. Jeder, der es liest, sieht sofort: compare irgendwas mit Null.
Ich habe bei mir persönlich festgestellt, je "idiotensicherer" der Code geschrieben, desto weniger Kommentare und noch weniger Zeit beim Debuggen. Aber das ist wiederum meine "Philosophie".
IMHO, wer mit so einer Philosophie anfaengt (x86)Asm zu programmieren, kann irgendwo nicht wirkliche viel Ahnung von der Plattform haben, fuer die er da programmiert und ist daher wohl besser beraten, es bleiben zu lassen und auf eine abstrakte Hochsprache umzusteigen, in der derart "idiotensichererer Code" in der Performance nicht so kontraproduktiv, und auch allgemein viel effektiver uebersichtlicher zu gestalten ist.
So ist das doch einfach eine billige Ausrede fuer schlechten Code.
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.
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:
Code:
1 2 3 4 5 6 7 8 9 10
1 2 3 4 5 6 7 8 9 10
[BITS 32]
[global start]
[extern _main] ; this is in the c file
hlt
hlt
hlt
start:
call _main
jmp $
Code:
1 2 3 4 5 6 7 8 9 10
[BITS 32]
[global start]
[extern _main] ; this is in the c file
hlt
hlt
hlt
start:
call _main
jmp $
Code:
1 2 3 4 5 6 7 8 9 10
[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.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 14:42:29 28.03.2009, insgesamt 6-mal bearbeitet
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
Code:
NOP
OR R1,R1,R1
OR R2,R2,R2
Code:
NOP
OR R1,R1,R1
OR R2,R2,R2
Code:
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)
_________________ Kilo Byte=1000,Kilobyte=1024 ANSI/IEEE Standard 1084-1986 rapso
-Mod im Spiele-/Grafikprogrammierung|rapsoo@hotmail.com| #dionysos irc.quakenet.org |amazon stole my PS3
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.
verschmäht, weil es angeblich unleserlich wäre, und
Code:
mov cx, 0
Code:
mov cx, 0
Code:
mov cx, 0
vorzieht, der muß in der hochsprache auch
Code:
++c;
Code:
++c;
Code:
++c;
meiden und immer brav
Code:
c = c + 1;
Code:
c = c + 1;
Code:
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
Code:
NOP
OR R1,R1,R1
OR R2,R2,R2
Code:
NOP
OR R1,R1,R1
OR R2,R2,R2
Code:
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?
verschmäht, weil es angeblich unleserlich wäre, und
Code:
mov cx, 0
Code:
mov cx, 0
Code:
mov cx, 0
vorzieht, der muß in der hochsprache auch
Code:
++c;
Code:
++c;
Code:
++c;
meiden und immer brav
Code:
c = c + 1;
Code:
c = c + 1;
Code:
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.
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.
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.
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.
...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.
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.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 00:59:20 27.03.2009, insgesamt 3-mal bearbeitet
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?
Zitat:
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.
Zitat:
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.
Zitat:
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.
_________________ Kilo Byte=1000,Kilobyte=1024 ANSI/IEEE Standard 1084-1986 rapso
-Mod im Spiele-/Grafikprogrammierung|rapsoo@hotmail.com| #dionysos irc.quakenet.org |amazon stole my PS3
@ 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.
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.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
@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):
==================================================
attempt to open kernel.o succeeded
opened script file kernel.o
kernel.o: file not recognized: File format not recognized
make: *** [all] Error 1
==================================================
attempt to open kernel.o succeeded
opened script file kernel.o
kernel.o: file not recognized: File format not recognized
make: *** [all] Error 1
==================================================
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?
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.
Zuletzt bearbeitet von Bitsy am 06:57:44 28.03.2009, insgesamt 4-mal bearbeitet
==================================================
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.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 12:12:22 28.03.2009, insgesamt 1-mal bearbeitet
In unserem OS müssen wir uns nun zumindest auf die rudimentärsten Operationen konzentrieren, nämlich Videoausgabe (monochrome/farbige Text/Grafik-Ausgabe), Dateneingabe über Tastatur und Daten lesen/schreiben von/auf formatierte(r) Floppy Disk. Maus, Drucker, Festplatte könnten später folgen, wenn notwendig.
ich würde schon mal versuchen, das ganze modular aufzubauen, so dass z.b. die text-ein/ausgabe, gerätesteuerung usw. über einheitliche schnittstellen gehen, also funktionen wie 'putchar()' und 'getchar()' umgeleitet werden können, indem man z.b. intern pointer auf strukturen umsetzen kann. dazu könntest du dir eine generische 'driver' struktur definieren, die bestandteil spezialisierter strukturen ist (objektorientierung also (nein, nicht c++ benutzen!!), so dass später leicht andere geräte eingehängt werden können). bewährt hat sich auch ein 'handle'-konzept, sowas wie stdin, stdout, file, raw, etc. der bastelfreudigkeit sind keine grenzen gesetzt.
@abc.w: Konntest Du das Problem lösen? Möchte niemanden verlieren.
Zitat:
The other main snag concerns object-file formats. There are two variants of the 32-bit COFF format: one used by Microsoft Win32 tools, and one by the rest of the world. They are only subtly different, and linkers which expect one format will happily link the other. The difference comes in the relocations: if you write code in, say, NASM, and link it using the Microsoft linker along with some modules compiled using Visual C++, the addresses in the final output will come out wrongly. There’s no real workaround for this, but luckily most tools that work with the PE format will allow you to emit files in the Win32 format: NASM has its -f win32 option, and Cygwin has the pei-i386 output format.
Zitat:
For LD (the linker that is most often used with DJGPP and gcc) use the option --oformat binary.
EDIT: delete (gelöst): del (anstelle delete; einer meiner Uraltfehler) u. -o beim Linker
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 02:01:14 29.03.2009, insgesamt 3-mal bearbeitet
generische 'driver' struktur definieren, die bestandteil spezialisierter strukturen ist
hast du einen Link oder ein beispiel-OS vor Augen. würde mir das gerne mal an einem konkreten beispiel anschauen. thema passt momentan genau, bin da dran.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
generische 'driver' struktur definieren, die bestandteil spezialisierter strukturen ist
hast du einen Link oder ein beispiel-OS vor Augen. würde mir das gerne mal an einem konkreten beispiel anschauen. thema passt momentan genau, bin da dran.
was konkretes wüsste ich jetzt auch nicht. such mal im internet nach 'i/o model' oder so ähnlich. z.b. in der linux doku müsste sowas zu finden sein und in irgendwelchen büchern über betriebssysteme natürlich auch.
ach, vielleicht hilft das:
http://cespc1.kumoh.ac.kr/~juyoon/lecture/osd/2006/osd08.pdf
Komischerweise wird das /Y im makefile nicht akzeptiert (unter MS-DOS sollte es gehen). Ich habe mich nun an die -o Option beim Linker erinnert. Die macht alles platt.
So geht es, zumindest bei mir mit gcc 3.1:
Code:
1 2 3 4 5 6 7 8 9 10 11
1 2 3 4 5 6 7 8 9 10 11
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 -o ckernel.bin
cmd /c copy /b boot.bin + ckernel.bin MyOS.bin
partcopy MyOS.bin 0 800 -f0
del kernel.o
del ckernel.o
del ckernel.bin
del boot.bin
Code:
1 2 3 4 5 6 7 8 9 10 11
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 -o ckernel.bin
cmd /c copy /b boot.bin + ckernel.bin MyOS.bin
partcopy MyOS.bin 0 800 -f0
del kernel.o
del ckernel.o
del ckernel.bin
del boot.bin
Code:
1 2 3 4 5 6 7 8 9 10 11
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 -o ckernel.bin
cmd /c copy /b boot.bin + ckernel.bin MyOS.bin
partcopy MyOS.bin 0 800 -f0
del kernel.o
del ckernel.o
del ckernel.bin
del boot.bin
@+Fricky: danke für die Links, aber teilweise schon etwas weit in den Details, dafür aber wieder zu komplex und gleichzeitg theoretisch. Das verwirrt momentan mehr als es beim Design hilft.
Zur Zielsetzung ist eine abstrakte theoretische Diskussion auf der Meta-Ebene notwendig. Die Herausforderung besteht darin, dass es keine Übereinstimmung darin gibt, wie man ein OS optimal aufsetzt (cf. Tanenbaum, Modern Operating Systems, 2nd Ed., Chap. 12: Operating System Design). Das finde ich nach diesen vielen Jahren schon interessant.
Einige Dinge weiß man aber inzwischen:
Monolithische Kernel (Windows, Linux) sind erfolgreicher als Micro Kernels. Das hat aber lediglich Effizienzgründe und keine strukturelle Basis, kann uns also egal sein.
Präemptives Multitasking (Modernes Windows) ist aus Sicht eines Prozesses besser als kooperatives (klassisches Windows).
Tanenbaum sieht vor allem vier Aufgabenfelder eines OS:
1) Abstraktion schaffen (schwierigste Aufgabe)
2) Primitive Operationen bereit stellen
3) Isolation sicher stellen
4) Hardware Management
Files, Prozesse, Synchronisation, Speichermodelle, I/O System, System calls, Fehlerseparation durch voneinander unabhängige Systeme, ... da stehen wir ja praktisch nicht mehr am Anfang, aber ob wir bereits im Optimum sind, das ist noch offen. Die Kontrolle der Hardware ist eher eine Fleissaufgabe.
Ein OS ist ein gigantisches Software-Paket. Unix hat mehr als 1 Mio., Linux 2.6 ca. 5,7 Mio., Win 2000 ca. 29 Mio., Win XP ca. 35 Mio. und MS Vista ca. 50 Mio. Zeilen Sourcecode. So etwas kann also keinesfalls in der Zielgeraden liegen. Daher muss man sich von diesen Systemen als Zielbild lösen.
Das Entscheidende dabei ist, dass niemand in wenigen Monaten ein neues OS schreiben kann. Diese Illusion sollte man rasch aufgeben. Bei einem Hobby-/Lehr-Projekt geht es also vor allem darum - didaktisch möglichst klar dargestellt - ein Verständnis für die Zusammenhänge und Möglichkeiten auf der untersten Ebene zu schaffen.
Es macht m.E. mehr Sinn, zunächst in der Historie anzuknüpfen und von dort aus neue Wege zu suchen. Das Original MS-DOS 1.0 (1981) besteht z.B. aus rund 4.000 Zeilen Assembler-Code (nicht offen gelegt). Die grundlegenden Funktionen dieses Systems lassen sich leicht nachvollziehen und verstehen. Es gibt inzwischen freie Clones.
MS DOS hatte vier Bereiche und arbeitete nur im RM:
1) Bootcode
2) io.sys = Geräteroutinen (Monitor, Tastatur, Festplatte, Schnittstellen)
3) command.com = Befehlsinterpreter
4) Ausführung von Programmen (COM, EXE)
Befehle:
"intern":
del, erase - Dateien löschen
rd, rmdir - Verzeichnis löschen
dir - Verzeichnisinhalt anzeigen
cd, chdir - Verzeichnis wechseln
cls - löscht den Bildschirminhalt
md, mkdir - Verzeichnis erstellen
copy - Kopieren einer oder mehrerer Dateien
ren, rename - Umbenennen von Dateien oder Verzeichnissen
type - Anzeigen von Textdateien
set - zeigt DOS Umgebungsvariablen oder legt eine neue fest
ver - zeigt die DOS Versionsnummer
vol - zeigt die Datenträgerbezeichnung an
"extern":
attrib - Zeigt Attribute von Dateien oder legt diese fest
fdisk - Partitionierung der Festplatte erstellen oder verändern
move - Verschieben von Dateien
mem - Anzeigen der Belegung des Arbeitsspeicher
tree - Zeigt die Verzeichnisstruktur an
format - Formatieren eines Datenträger
EDIT: MS-DOS hatte auch einige Systemaufrufe wie den berühmten int 21h... (Anmerkung von abc.w)
Es macht m.E. mehr Sinn, zunächst in der Historie anzuknüpfen und von dort aus neue Wege zu suchen.
Meine Rede! Man muß sich nur trennen von der Ansicht, daß im RM nur 1 MB RAM adressierbar sind. Dann müssten die Ideen eigentlich nur so hervorsprudeln. Vorausgesetzt natürlich, das Ziel ist nicht, DOS einfach nur zu kopieren.
Man muß sich nur trennen von der Ansicht, daß im RM nur 1 MB RAM adressierbar sind.
Gibt es hierzu bereits ein stabiles Hobby-/Lehr-OS, das man sich mal anschauen könnte?
Ich denke nicht, dass der Protected Mode das zentrale Problem ist, eher was daraus gemacht wurde. Die Adressierung ist beherrschbar (eine Zeigerebene mehr und Paging, dafür hat man flat Speicher-Adr.). Multitasking mit allen Problemen muss ja z.B. nicht sein. Es geht doch eher darum, die enorme Leistung eines heutigen PC, die unter Windows (und inzwischen auch Linux) verschüttet wird, frei zu legen und Programmen bereit zu stellen.
Zitat:
perfection is reached not when there is no longer anything to add, but when there is no longer anything to take away.
Antoine de St. Exupéry.
Zitat:
Protected Mode has several advantages over Real Mode:
1. Protected Mode has Protection, the ability to keep programs from messing around and crashing each other(this is how Proctected Mode got its name).
2. You may only have several tasks (like Windows and Linux).
3. You have 4GB of address space (A20-Gate enabled).
4. You can use paging to access memory.
Wichtig ist nach Tanenbaum eine Abstraktionsidee, Beispiele:
alles ist ein Tape (Fortran)
alles ist ein File (Unix)
alles ist ein Objekt (W2K)
alles ist ein Dokument (www)
A.S.Tanenbaum ist ja immer einer der Verfechter des aus seiner Sicht "eleganten" Microkernel-Prinzips gewesen, das er im OS Minix umsetzte.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 11:30:42 29.03.2009, insgesamt 6-mal bearbeitet
@+Fricky: danke für die Links, aber teilweise schon etwas weit in den Details, dafür aber wieder zu komplex und gleichzeitg theoretisch. Das verwirrt momentan mehr als es beim Design hilft.
schau es dir einfach in ruhe an und lass dich davon inspirieren. du musst ja kein OS strikt nach lehrbuchmeinung basteln (das wäre auch langweilig), aber z.b. in den tanenbaum-slides sind prinzipien erwähnt, von denen man als os-designer mal was gehört haben sollte. was du davon verwertest und was nicht, bleibt dir überlassen. ich würde zumindest leichte erweiterbarkeit und austauschbarkeit von komponenten von vorn herein einplanen. nicht dass du später mal alles umbauen musst, wenn ausgaben statt auf dem bildschirm, in einer datei landen sollen.
ich würde zumindest leichte erweiterbarkeit und austauschbarkeit von komponenten von vorn herein einplanen. nicht dass du später mal alles umbauen musst, wenn ausgaben statt auf dem bildschirm, in einer datei landen sollen.
Das ist richtig. Abstraktion ist der entscheidende Punkt. Momentan schmökere ich gerade im Buch von Richard A. Burgess "Developing Your Own 32 Bit Computer Operating System" (MMURTL V1.0). Er hatte sich bei seinem Operating System folgende Ziele gesetzt:
- True Multitasking
- Real-time operation (react to outside events in real time ==> message based OS)
- Client/Server design (share services with multiple client applications)
- Common affordable hardware platform with minimal hardware requirements
- Flat 32-Bit Virtual Memory Model
- Easy programming (procedural interface into OS with no intermediate library)
- Protection from other programs - But not the programmer
- Use the CPU Instruction Set as Designed (heute zählt Portabilität mehr)
- Simplicity
==>
Zitat:
"MMURTL (pronounced like the girl’s name Myrtle) is a 32-bit, Message based, Multitasking, Real-Time, operating system designed around the Intel 80386 and 80486 processors on the PC Industry Standard Architecture (ISA) platforms. The name is an acronym for Message based MUltitasking, Real-Time, kerneL."
Folgende Punkte sehe ich bisher für mein "PrettyOS" (Deckname):
- Common affordable hardware platform (Standard PC ab 80386)
- Flat 32-Bit Virtual Memory Model (das bedeutet PM bei x86)
- Easy programming
- Simplicity (KISS Prinzip gilt in einer komplexen Welt immer mehr)
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 16:01:08 29.03.2009, insgesamt 4-mal bearbeitet
Habe mit mingw-Linker & Co ein wenig rumexperimentiert. Es funktioniert immer noch nicht und ich glaube nicht, dass es funktionieren wird...
Der mingw-Linker unterstützt folgende Formate:
C:\BochsMyOS\20090327_eh_os>c:\nasm-2.06rc6\nasm -hf
...
valid output formats for -f are (`*' denotes default):
* bin flat-form binary files (e.g. DOS .COM, .SYS)
aout Linux a.out object files
aoutb NetBSD/FreeBSD a.out object files
coff COFF (i386) object files (e.g. DJGPP for DOS)
elf32 ELF32 (i386) object files (e.g. Linux)
elf ELF (short name for ELF32)
elf64 ELF64 (x86_64) object files (e.g. Linux)
as86 Linux as86 (bin86 version 0.3) object files
obj MS-DOS 16-bit/32-bit OMF object files
win32 Microsoft Win32 (i386) object files
win64 Microsoft Win64 (x86-64) object files
rdf Relocatable Dynamic Object File Format v2.0
ieee IEEE-695 (LADsoft variant) object file format
macho NeXTstep/OpenStep/Rhapsody/Darwin/MacOS X object files
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
C:\BochsMyOS\20090327_eh_os>c:\nasm-2.06rc6\nasm -hf
...
valid output formats for -f are (`*' denotes default):
* bin flat-form binary files (e.g. DOS .COM, .SYS)
aout Linux a.out object files
aoutb NetBSD/FreeBSD a.out object files
coff COFF (i386) object files (e.g. DJGPP for DOS)
elf32 ELF32 (i386) object files (e.g. Linux)
elf ELF (short name for ELF32)
elf64 ELF64 (x86_64) object files (e.g. Linux)
as86 Linux as86 (bin86 version 0.3) object files
obj MS-DOS 16-bit/32-bit OMF object files
win32 Microsoft Win32 (i386) object files
win64 Microsoft Win64 (x86-64) object files
rdf Relocatable Dynamic Object File Format v2.0
ieee IEEE-695 (LADsoft variant) object file format
macho NeXTstep/OpenStep/Rhapsody/Darwin/MacOS X object files
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
C:\BochsMyOS\20090327_eh_os>c:\nasm-2.06rc6\nasm -hf
...
valid output formats for -f are (`*' denotes default):
* bin flat-form binary files (e.g. DOS .COM, .SYS)
aout Linux a.out object files
aoutb NetBSD/FreeBSD a.out object files
coff COFF (i386) object files (e.g. DJGPP for DOS)
elf32 ELF32 (i386) object files (e.g. Linux)
elf ELF (short name for ELF32)
elf64 ELF64 (x86_64) object files (e.g. Linux)
as86 Linux as86 (bin86 version 0.3) object files
obj MS-DOS 16-bit/32-bit OMF object files
win32 Microsoft Win32 (i386) object files
win64 Microsoft Win64 (x86-64) object files
rdf Relocatable Dynamic Object File Format v2.0
ieee IEEE-695 (LADsoft variant) object file format
macho NeXTstep/OpenStep/Rhapsody/Darwin/MacOS X object files
D.h. der nasm Assembler kann Objektdateien erzeugen, die der mingw-Linker nicht kennt. Ausserdem scheint es problematisch zu sein, dass die Datei kernel.asm 16-Bit und 32-Bit Code enthält. Wenn man mit nasm versucht, z.B. win32 Format zu generieren, bekommt man zahlreiche Fehlermeldungen:
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
C:\nasm-2.06rc6\nasm -O32 -f win32 kernel.asm -o kernel.o
kernel.asm:21: error: COFF format does not support non-32-bit relocations
kernel.asm:27: error: COFF format does not support non-32-bit relocations
kernel.asm:35: error: COFF format does not support non-32-bit relocations
kernel.asm:40: error: COFF format does not support non-32-bit relocations
kernel.asm:45: error: COFF format does not support non-32-bit relocations
kernel.asm:50: error: COFF format does not support non-32-bit relocations
kernel.asm:55: error: COFF format does not support non-32-bit relocations
kernel.asm:59: error: COFF format does not support non-32-bit relocations
kernel.asm:64: error: COFF format does not support non-32-bit relocations
kernel.asm:69: error: COFF format does not support non-32-bit relocations
kernel.asm:74: error: COFF format does not support non-32-bit relocations
kernel.asm:82: error: COFF format does not support non-32-bit relocations
kernel.asm:88: error: COFF format does not support non-32-bit relocations
kernel.asm:117: error: COFF format does not support non-32-bit relocations
make: *** [all] Error 1
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
C:\nasm-2.06rc6\nasm -O32 -f win32 kernel.asm -o kernel.o
kernel.asm:21: error: COFF format does not support non-32-bit relocations
kernel.asm:27: error: COFF format does not support non-32-bit relocations
kernel.asm:35: error: COFF format does not support non-32-bit relocations
kernel.asm:40: error: COFF format does not support non-32-bit relocations
kernel.asm:45: error: COFF format does not support non-32-bit relocations
kernel.asm:50: error: COFF format does not support non-32-bit relocations
kernel.asm:55: error: COFF format does not support non-32-bit relocations
kernel.asm:59: error: COFF format does not support non-32-bit relocations
kernel.asm:64: error: COFF format does not support non-32-bit relocations
kernel.asm:69: error: COFF format does not support non-32-bit relocations
kernel.asm:74: error: COFF format does not support non-32-bit relocations
kernel.asm:82: error: COFF format does not support non-32-bit relocations
kernel.asm:88: error: COFF format does not support non-32-bit relocations
kernel.asm:117: error: COFF format does not support non-32-bit relocations
make: *** [all] Error 1
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
C:\nasm-2.06rc6\nasm -O32 -f win32 kernel.asm -o kernel.o
kernel.asm:21: error: COFF format does not support non-32-bit relocations
kernel.asm:27: error: COFF format does not support non-32-bit relocations
kernel.asm:35: error: COFF format does not support non-32-bit relocations
kernel.asm:40: error: COFF format does not support non-32-bit relocations
kernel.asm:45: error: COFF format does not support non-32-bit relocations
kernel.asm:50: error: COFF format does not support non-32-bit relocations
kernel.asm:55: error: COFF format does not support non-32-bit relocations
kernel.asm:59: error: COFF format does not support non-32-bit relocations
kernel.asm:64: error: COFF format does not support non-32-bit relocations
kernel.asm:69: error: COFF format does not support non-32-bit relocations
kernel.asm:74: error: COFF format does not support non-32-bit relocations
kernel.asm:82: error: COFF format does not support non-32-bit relocations
kernel.asm:88: error: COFF format does not support non-32-bit relocations
kernel.asm:117: error: COFF format does not support non-32-bit relocations
make: *** [all] Error 1
Was COFF mit win32 Format zu tun hat, weiss ich jetzt nicht...
Dann habe ich mit ELF Format ausprobiert (scheint der kleinste gemeinsame Nenner zu sein, mingw-Linker und nasm können es beide und man kann die ckernel.o mit objcopy nach ELF konvertieren). Es kommt ungefähr folgende Fehlermeldung:
Code:
C:\BochsMyOS\20090327_eh_os>ld -T kernel.ld kernel32.o ckernel_elf.o
ld: cannot perform PE operations on non PE output file 'a.exe'.
Code:
C:\BochsMyOS\20090327_eh_os>ld -T kernel.ld kernel32.o ckernel_elf.o
ld: cannot perform PE operations on non PE output file 'a.exe'.
Code:
C:\BochsMyOS\20090327_eh_os>ld -T kernel.ld kernel32.o ckernel_elf.o
ld: cannot perform PE operations on non PE output file 'a.exe'.
Mit kernel32.o habe ich versucht, 16-Bit Code von 32-Bit Code zu trennen und die Datei ckernel_elf.o ist die ckernel.o konvertiert nach ELF.
Wie es aussieht, es geht mit mingw Tools nicht. Oder ich mache irgendwas falsch.
Werde mir wohl den weisshaarigen DJGPP runterladen...
Momentan schmökere ich gerade im Buch von Richard A. Burgess "Developing Your Own 32 Bit Computer Operating System" (MMURTL V1.0). Er hatte sich bei seinem Operating System folgende Ziele gesetzt:
ah, ich glaub' das hatte ich mal in den fingern. ist schon ziemlich alt und er hat sein OS komplett in asm programmiert, ne?
Erhard Henkes schrieb:
The name is an acronym for Message based MUltitasking, Real-Time, kerneL."
'message based' bedeutet, dass kein timer-gesteuerter scheduler drin ist, sondern context-switches werden durch versenden von messages herbeigeführt, oder? finde ich gut. präemptives, gewaltsames multitasking nach zeitscheiben ist manchmal gar nicht so toll.
Erhard Henkes schrieb:
Folgende Punkte sehe ich bisher für mein "PrettyOS" (Deckname):
- Flat 32-Bit Virtual Memory Model (das bedeutet PM bei x86)
- Easy programming
- Simplicity (KISS Prinzip gilt in einer komplexen Welt immer mehr)
ja, pretty-OS hört sich auch besser an, als der bisherige name.
flat memory model: was denn sonst? segment/offset-gedöns ist einfach nur übel.
easy programming: muss schon sein. keine übertriebenen schutzmechanismen, vertrau dem programmierer, denn er weiss, was er tut.
KISS: sehr vernünftig.
Zunächst müssen wir erstmal das NASM/Linker-Problem bei mingw (siehe post von abc.w) lösen, damit wir niemanden verlieren.
Multitasking/Singletasking? Da bin ich mir noch nicht sicher, ob Multitasking am Anfang nicht zuviel des Guten ist. Vorbereitet sind wir ja via PM. Mal sehen.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Ja, das ist richtig. Als PC-Betriebssystem kenne ich bisher noch kein Multitasking OS im Real Address Mode (=Real Mode), muss irgendwie an mir vorbei gegangen sein. Kannst Du mir da mal auf die Sprünge helfen?
Prinzipiell ist task switching / context switching unabhängig von der Adressierungstechnik (direkt gekoppelt mit der physikalischen Adresse oder indirekt über Zeigertabellen). Auch Protected oder Unprotected hängt damit nicht zwingend zusammen.
Kannst Du mir Beispiele nennen für folgende Kombinationen:
Habe mir DJGPP heruntergeladen und konnte 20090327_eh_os erfolgreich kompilieren, linken, zusammenkopieren. Ein ähnliches Problem wie mit copy wieder gehabt, diesmal mit rename:
Code:
1 2 3 4 5 6 7 8 9
1 2 3 4 5 6 7 8 9
C:\BochsMyOS\20090327_eh_os>make
C:\nasm-2.06rc6\nasm -O32 -f bin boot.asm -o boot.bin
C:\nasm-2.06rc6\nasm -O32 -f aout kernel.asm -o kernel.o
c:\djgpp\bin\gcc -c ckernel.c -o ckernel.o
c:\djgpp\bin\ld -T kernel.ld kernel.o ckernel.o
rename a.out ckernel.bin
process_begin: CreateProcess(NULL, rename a.out ckernel.bin, ...) failed.
make (e=2): Das System kann die angegebene Datei nicht finden.
make: *** [all] Error 2
Code:
1 2 3 4 5 6 7 8 9
C:\BochsMyOS\20090327_eh_os>make
C:\nasm-2.06rc6\nasm -O32 -f bin boot.asm -o boot.bin
C:\nasm-2.06rc6\nasm -O32 -f aout kernel.asm -o kernel.o
c:\djgpp\bin\gcc -c ckernel.c -o ckernel.o
c:\djgpp\bin\ld -T kernel.ld kernel.o ckernel.o
rename a.out ckernel.bin
process_begin: CreateProcess(NULL, rename a.out ckernel.bin, ...) failed.
make (e=2): Das System kann die angegebene Datei nicht finden.
make: *** [all] Error 2
Code:
1 2 3 4 5 6 7 8 9
C:\BochsMyOS\20090327_eh_os>make
C:\nasm-2.06rc6\nasm -O32 -f bin boot.asm -o boot.bin
C:\nasm-2.06rc6\nasm -O32 -f aout kernel.asm -o kernel.o
c:\djgpp\bin\gcc -c ckernel.c -o ckernel.o
c:\djgpp\bin\ld -T kernel.ld kernel.o ckernel.o
rename a.out ckernel.bin
process_begin: CreateProcess(NULL, rename a.out ckernel.bin, ...) failed.
make (e=2): Das System kann die angegebene Datei nicht finden.
make: *** [all] Error 2
Dann habe ich mein Makefile umgebaut:
Code:
all:
...
cmd /c rename a.out ckernel.bin
...
Code:
all:
...
cmd /c rename a.out ckernel.bin
...
Code:
all:
...
cmd /c rename a.out ckernel.bin
...
Und so funktioniert es bei mir wieder...
Eine Sache vielleicht noch: Die Funktion outportb() im C-Kernel ist ja als inline gekennzeichnet, "inlinen" tut der GCC anscheinend aber ab Optimierungsstufe -O1:
Code:
all:
...
gcc -c ckernel.c -o ckernel.o -O1
...
Code:
all:
...
gcc -c ckernel.c -o ckernel.o -O1
...
Code:
all:
...
gcc -c ckernel.c -o ckernel.o -O1
...
Man kann sich die Objektdatei ckernel.o mit objdump anschauen, einmal ohne Optimierungsstufe und einmal mit, z.B. so:
Code:
c:\djgpp\bin\objdump.exe -D ckernel.o
Code:
c:\djgpp\bin\objdump.exe -D ckernel.o
Code:
c:\djgpp\bin\objdump.exe -D ckernel.o
Wofür steht eigentlich -O32 beim Aufruf von nasm? -O steht für Optimierung, aber 32... finde grade nichts über Optimierungsstufe 32 in der nasm Doku...
Code:
C:\nasm-2.06rc6\nasm -O32 -f bin boot.asm -o boot.bin
Code:
C:\nasm-2.06rc6\nasm -O32 -f bin boot.asm -o boot.bin
Code:
C:\nasm-2.06rc6\nasm -O32 -f bin boot.asm -o boot.bin
Erhard Henkes schrieb:
Zunächst müssen wir erstmal das NASM/Linker-Problem bei mingw (siehe post von abc.w) lösen, damit wir niemanden verlieren.
Nur aus Neugier, wie viele Leute sind noch dabei ? Ich bin aus persönlichem Interesse dabei, weil ich u.a. vorhabe, hobbymässig einen echten 80386 auf einer Lochrasterplatine zum Laufen zu bekommen. Ein PC ist mir zu langweilig Überlege gerade, wie ich die RAM Bausteine 8 Stück je 32 kByte anschliesse. Eins weiss ich inzwischen genau: Auf keinen Fall A20 Gate
Habe mir DJGPP heruntergeladen und konnte 20090327_eh_os erfolgreich kompilieren, linken, zusammenkopieren.
Super! Freut mich, dass es bei Dir nun auch klappt. Eigentlich ein Hammer, dass das alte Teil mehr kann als das neue Allerdings hätte mich auch die wirkliche Lösung für den mingw interessiert, aber Hauptsache es geht. Kann man den alten ld vom DJGPP bei den Dateien vom neueren mingw einsetzen?
Zitat:
Ein ähnliches Problem wie mit copy wieder gehabt, diesmal mit rename
Das habe ich inzwischen über Bord geworfen, weil es das ckernel.bin nicht überschrieben hat, hätte man aber vorher mit del löschen können. So sieht es momentan aus (video.c ausgelagert), es wird einfach die Option -o ckernel.bin beim Linker ld angegeben:
http://www.henkessoft.de/OS_Dev/Bilder/make_process.PNG
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13
1 2 3 4 5 6 7 8 9 10 11 12 13
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
gcc -c video.c -o video.o
ld -T kernel.ld kernel.o ckernel.o video.o -o ckernel.bin --verbose
cmd /c copy /b boot.bin + ckernel.bin MyOS.bin
del video.o
del kernel.o
del ckernel.o
del ckernel.bin
del boot.bin
partcopy MyOS.bin 0 800 -f0
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13
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
gcc -c video.c -o video.o
ld -T kernel.ld kernel.o ckernel.o video.o -o ckernel.bin --verbose
cmd /c copy /b boot.bin + ckernel.bin MyOS.bin
del video.o
del kernel.o
del ckernel.o
del ckernel.bin
del boot.bin
partcopy MyOS.bin 0 800 -f0
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13
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
gcc -c video.c -o video.o
ld -T kernel.ld kernel.o ckernel.o video.o -o ckernel.bin --verbose
cmd /c copy /b boot.bin + ckernel.bin MyOS.bin
del video.o
del kernel.o
del ckernel.o
del ckernel.bin
del boot.bin
partcopy MyOS.bin 0 800 -f0
Zitat:
Ich bin aus persönlichem Interesse dabei, weil ich u.a. vorhabe, hobbymässig einen echten 80386 auf einer Lochrasterplatine zum Laufen zu bekommen. Ein PC ist mir zu langweilig Überlege gerade, wie ich die RAM Bausteine 8 Stück je 32 kByte anschliesse. Eins weiss ich inzwischen genau: Auf keinen Fall A20 Gate
Falls Du das nicht selbst auf einer Homepage verarbeiten willst, sollten wir ein Kapitel über dein Projekt einbauen (natürlich mit Fotos deines Konstrukts, bitte denke an die VDE und Funkabschirmung ).
Gibt man "-O32" nasm (das habe ich von Nobuo T übernommen) bei Google ein, so kommen wir hier mit diesem Thread auf Platz eins heraus.
Danke für den Hinweis mit der Optimierungsstufe und den Tipp bezüglich objdump (man kann die Leser eines solchen OS-Tutorials bezüglich Tools gar nicht genug fit machen). Das werde ich ins Tutorial einbauen.
finde ich sehr cool, wie du hier eine mini-OS bastelst, habe ich auch mal versucht und bin irgendwann bis zu einer minigrafischen Oberfäche gekommen.
Ich werde das noch mal rauskramen.
Ich finde, dass du, bevor du den Keyboardtreiber schreibst, dir ein geeignetes Konzept zur Interruptbehandlung zulegst und das auch schon mal Schablonenartig implementierst.
Ich finde diese Prolog/Epilog Konzept sehr gut. Mit entsprechenden Tricks und Hirnschmalz kann man es dann auch schaffen, dass die Interrupts _nie_ ausgestellt werden müssen.
Als PC-Betriebssystem kenne ich bisher noch kein Multitasking OS im Real Address Mode (=Real Mode), muss irgendwie an mir vorbei gegangen sein. Kannst Du mir da mal auf die Sprünge helfen?
z.b. von cmx-rtx, embOS, freeRTOS, proc, usw, gibts auch versionen für x86-boards (das sind systeme für den embedded-bereich, machen alle timer-gesteuertes multitasking). die meisten davon laufen im real mode.
Ein ganz anderer Punkt sind die mathematischen und sonstigen Routinen, die man ständig benötigt, und die aber nicht von einer library abhängig sein dürfen.
Beispiel: Will man eine char-, integer- oder float-Zahl auf dem Bildschirm ausgeben, so muss man zunächst diese in einen "string" umwandeln. Man benötigt also ein "frei schwebendes" k_itoa ohne Routinen aus string.h etc. Selbst im alten K&R wurde ich da nicht fündig, da verweist ständig eine Funktion auf die nächste, wie bei einem russischen Püppchen
So geht es aber, verwendet nur limits.h von gcc includes:
Ich finde, dass du, bevor du den Keyboardtreiber schreibst, dir ein geeignetes Konzept zur Interruptbehandlung zulegst und das auch schon mal Schablonenartig implementierst.
Ich möchte es zunächst erstmal ohne Interrupts probieren, bin nicht sicher, ob das vernünftig geht, wird dann aber logischerweise der nächste Schritt.
Zitat:
Ich finde diese Prolog/Epilog Konzept sehr gut. Mit entsprechenden Tricks und Hirnschmalz kann man es dann auch schaffen, dass die Interrupts _nie_ ausgestellt werden müssen.
Danke an alle für die tollen Links. Das hilft mir sehr.
Momentan ist mein Hauptproblem, dass ich zwei Rechner benötige, weil ich nicht ständig Windows booten will, wenn das liebe PrettyOS selbst "aus Bochs heraus" meine Tastatur abgeschossen hat.
EDIT:
Problem gelöst, Keyboard-Treiber funktioniert, zur Zeit noch ohne Interrupts, wie ich es wollte.
OS fragt die Tastatur in einer Schleife ab, löscht den Bildschirm und druckt in Zeile 0 das ASCII-Zeichen und in Zeile 1 den ASCII-Code (dank k_itoa). Zur Zeichendarstellung benötigt man bereits zwei Keymaps (eine mit und eine ohne Shift-Taste, zur Zeit International US-Tastatur).
http://www.henkessoft.de/OS_Dev/Bilder/KeyboardDriver_funktioniert.PNG
Gefällt mir didaktisch gut, vor allem noch alles in C außer den Funktionen outportb(...) und inportb(...).
Nun muss ich den Sourcecode noch didaktisch optimieren, sieht teilweise improvisiert ziemlich unordentlich aus, kann man so nicht uploaden.
Module:
ckernel.c:
main()
video.c:
k_clear_screen()
k_printf(char* message, unsigned int line, char attribute)
void update_cursor(int row, int col)
util.c (wollte es nicht stdlib nennen):
void k_itoa(int value, char* valuestring)
void k_i2hex(unsigned int val, unsigned char* dest, int len)
void float2string(float value, int decimal, char* valuestring)
math.c:
int power(int base,int n)
keyboard.c:
...
...
k_getch()
Wohin gehören outportb, inportb?
Wie würdet ihr überhaupt die Module anordnen?
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 23:30:17 30.03.2009, insgesamt 8-mal bearbeitet
Ich bin aus persönlichem Interesse dabei, weil ich u.a. vorhabe, hobbymässig einen echten 80386 auf einer Lochrasterplatine zum Laufen zu bekommen. Ein PC ist mir zu langweilig Überlege gerade, wie ich die RAM Bausteine 8 Stück je 32 kByte anschliesse. Eins weiss ich inzwischen genau: Auf keinen Fall A20 Gate
Falls Du das nicht selbst auf einer Homepage verarbeiten willst, sollten wir ein Kapitel über dein Projekt einbauen (natürlich mit Fotos deines Konstrukts, bitte denke an die VDE und Funkabschirmung ).
Eine Homepage habe ich nicht... und hätte auch nichts gegen ein Kapitel mit ein Paar Fotos von meinem Board Ein Kapitel, der heissen soll "So nicht" Es ist nämlich so, dass ich inzwischen 20 ICs auf der Platine habe und wenn ich so überlege, noch ziemlich am Anfang stehe. Diese 20 ICs habe ich nicht draufgemacht, weil ich mir Gedanken gemacht habe, wie die Schaltung optimal oder am Besten wäre, sondern, ich hatte sie zufällig in meiner Bastelkiste... Meine Idee ist, vom PC aus ein binäres Programm auf die Platine "runterladen" und dieses Programm vom 80386 ausführen lassen. Die Kommunikation mit dem PC soll über eine UART gehen und die Steuerung soll ein ATmega644 übernehmen. Sprich, ein 8-Bit Mikrocontroller soll volle Kontrolle über eine 32-Bit CPU haben. Der ATmega soll den 80386 im Reset halten, wenn ein Programm über UART gesendet wird und, dieses Programm ins RAM schreiben. Danach soll der 80386 "freigelassen" werden Also nicht gerage sinnvoll das Ganze. Nicht, dass jemand denkt, boah, der baut einen PC nach... Ich habe noch nicht mal irgendwelche I/O Bausteine für den 386er. Mein Ziel ist also wirklich "nur" 386er soll ein Programm aus dem RAM ausführen und wenn die Spannung weg ist, dann ist auch das Programm weg. Das Programm könnte natürlich alles Mögliche sein, auch ein kleines OS... nur man müsste dann irgendeine Form von I/O dazubauen und Platz dafür wäre auf der Platine da.
Ich kann Dir gerne ein Paar Fotos von dem Board im jetzigen Stand zuschicken, die RAM Bausteine und der 386er sind aber noch nicht drauf...
Dazu ist noch ein weiter Weg, zuerst muss RAM mit seinem 32-Bit Datenbus funktionieren. Die Funktion der 19 ICs besteht im Prinzip darinm, dass der 20. IC, der ATmega, wie ein Master mit 20-Bit Adressbus und 32-Bit Datenbus erscheint plus ein Paar Steuersignale... Die Funktion dieser 19 ICs muss ich noch überprüfen
PS: Ups, die UART realisiert mit MAX232 läuft bereits, also sind es nur noch 18 ICs...
Zuletzt bearbeitet von abc.w am 21:57:35 30.03.2009, insgesamt 1-mal bearbeitet
Ein ganz anderer Punkt sind die mathematischen und sonstigen Routinen, die man ständig benötigt, und die aber nicht von einer library abhängig sein dürfen.
Es ist nämlich so, dass ich inzwischen 20 ICs auf der Platine habe und wenn ich so überlege, noch ziemlich am Anfang stehe. Diese 20 ICs habe ich nicht draufgemacht, weil ich mir Gedanken gemacht habe, wie die Schaltung optimal oder am Besten wäre, sondern, ich hatte sie zufällig in meiner Bastelkiste...
das ist nicht dein ernst?!
abc.w schrieb:
Meine Idee ist, vom PC aus ein binäres Programm auf die Platine "runterladen" und dieses Programm vom 80386 ausführen lassen.
Es ist nämlich so, dass ich inzwischen 20 ICs auf der Platine habe und wenn ich so überlege, noch ziemlich am Anfang stehe. Diese 20 ICs habe ich nicht draufgemacht, weil ich mir Gedanken gemacht habe, wie die Schaltung optimal oder am Besten wäre, sondern, ich hatte sie zufällig in meiner Bastelkiste...
das ist nicht dein ernst?!
Also, es ist nicht so kompliziert, 12 davon sind 74HC244, Tristate Treiber, ein ATmega, ein Paar Schieberegister, Zähler, 3zu8 Dekoder... Würde sagen, lauter Standard-Bausteine.
Package Pin Count and Type 388 PBGA
das ist nicht dein ernst?! Nix für meinen "Schlitz"-Lötkolben.
+fricky schrieb:
aber wenn du mehr auf basteln stehst (was ich vermute, siehe oben), dann vielleicht doch eher sowas: http://www.mycpu.eu/
Genau, ich stehe auf Basteln. MyCPU ist interessant, wenn man aber bereits mal mit FPGAs in Berührung gekommen ist und zufällig ein FPGA-Board besitzt, dann, nun ja, 8-Bit CPU, hm, hm...
Und wenn man zufällig ein Paar alte 80386 CPUs besitzt, wo man an die Pins auch noch mit nem "Schlitz"-Lötkolben drankommt, warum nicht irgendwas damit bauen. x86 Assembler-Programmierung kann man zwar am PC schön erlernen und vielleicht sogar irgendwie irgendwo anwenden, aber so richtiges Verständnis für die "Physik" fehlt einem irgendwo. Wie ist es mit dem Datenbus, mit dem Adressbus, wie sieht Adressbus aus, welche Steuersignale sind da, was bedeuten sie, was kann man damit machen usw. Eine selbstgebastelte funktioniernde Platine wäre für mich, dass ich u.a. auch diese "Physik"-Ebene verstanden habe...
Package Pin Count and Type 388 PBGA
das ist nicht dein ernst?! Nix für meinen "Schlitz"-Lötkolben.
ach, du hast doch bestimmt irgendwo 'nen backofen rumstehen.
abc.w schrieb:
Genau, ich stehe auf Basteln. MyCPU ist interessant, wenn man aber bereits mal mit FPGAs in Berührung gekommen ist und zufällig ein FPGA-Board besitzt, dann, nun ja, 8-Bit CPU, hm, hm...
Und wenn man zufällig ein Paar alte 80386 CPUs besitzt, wo man an die Pins auch noch mit nem "Schlitz"-Lötkolben drankommt, warum nicht irgendwas damit bauen.
datenbusbreite ist doch nicht wichtig. 8-bitter können auch schnell sein. wenn du sowieso mit FPGA's rumfummelst, musste dir auch keine schaltung aus alten schrott-ICs basteln. mach dir 'nen x86-core und etwas peripherie in deinen FPGA rein, und fertig.
Hab die 13 C.zip bei mir mal ausprobiert und hatte schon wieder diese komische Fehlermeldung von mingw make (möchte aus welchen Gründen auch immer MinGW Tools verwenden , habe auch noch die Verzeichnisse in PATH für nasm und DJGPP gar nicht angepasst und benutze im Makefile absolute Pfadangaben):
Code:
del kernel.o
process_begin: CreateProcess(NULL, del kernel.o, ...) failed.
make (e=2): Das System kann die angegebene Datei nicht finden.
make: *** [all] Error 2
Code:
del kernel.o
process_begin: CreateProcess(NULL, del kernel.o, ...) failed.
make (e=2): Das System kann die angegebene Datei nicht finden.
make: *** [all] Error 2
Code:
del kernel.o
process_begin: CreateProcess(NULL, del kernel.o, ...) failed.
make (e=2): Das System kann die angegebene Datei nicht finden.
make: *** [all] Error 2
Dann im makefile die Aufrufe von del wie den Aufruf von copy geändert:
Code:
1 2 3 4 5 6 7 8 9 10
1 2 3 4 5 6 7 8 9 10
cmd /c del kernel.o
cmd /c del ckernel.o
cmd /c del video.o
cmd /c del keyboard.o
cmd /c del math.o
cmd /c del util.o
cmd /c del ckernel.bin
cmd /c del boot.bin
Code:
1 2 3 4 5 6 7 8 9 10
cmd /c del kernel.o
cmd /c del ckernel.o
cmd /c del video.o
cmd /c del keyboard.o
cmd /c del math.o
cmd /c del util.o
cmd /c del ckernel.bin
cmd /c del boot.bin
Code:
1 2 3 4 5 6 7 8 9 10
cmd /c del kernel.o
cmd /c del ckernel.o
cmd /c del video.o
cmd /c del keyboard.o
cmd /c del math.o
cmd /c del util.o
cmd /c del ckernel.bin
cmd /c del boot.bin
Und es funktioniert auch mit dem mingw make...
Mit der make.exe, die in der zip-Datei mit dabei ist, funktioniert es auch ohne zusätzliches "cmd /c ". Ich sehe grade, dass die make.exe aus der zip-Datei die gleiche ist wie die c:\djgpp\bin\make.exe.
Hm, mingw scheint irgendwie eigenwilliger als DJGPP zu sein. Kleine Inkompatibilitäten und so was...
datenbusbreite ist doch nicht wichtig. 8-bitter können auch schnell sein. wenn du sowieso mit FPGA's rumfummelst, musste dir auch keine schaltung aus alten schrott-ICs basteln. mach dir 'nen x86-core und etwas peripherie in deinen FPGA rein, und fertig.
Bezüglich x86 Core habe ich mir mal echt darüber Gedanken gemacht, einen in VHDL zu implementieren. Die "Komplexität" hat mich abgeschreckt, Real Mode, Protected Mode, elf Adressierungsarten, Anzahl der Befehle, Codierung der Befehle wie irgendwelche Präfixe, die mal auf die Berechnung der Adresse, mal auf die Operandenbitbreite Auswirkung haben usw... Ich habe auch mal darüber nachgedacht, eine Art Untermenge von 386 zu "implementieren", einen Core, der bereits im Protected Mode ist, nur wenige Befehle kann usw. Und dann ist noch die Sache bezüglich "lad mal nen Core", man muss ja noch das Programm irgendwie mitladen, wenn denn auf dem FPGA Platz dafür ist...
Gibt es irgendwo einen freien x86 IP ab 80386, am Besten in VHDL?
@abc.w: danke für deine Tests mit mingw. Geht denn jetzt auch das Linken? Wie hast Du das geschafft?
Den Code zum Keyboard Driver habe ich hoffentlich noch etwas klarer gemacht. Vielleicht schaffe ich noch eine vereinfachte Variante mit nur einer Keymap und ohne Shift-Abfrage. Muss mir mal andere keymaps anschauen, ob diese einem allgemeinen Standard folgen, denn wir wollen ja auch unsere deutsche Tastatur durch einfaches Übernehmen anderer keymaps nutzen.
Ich hoffe, dass man es noch versteht, was ich da von mir gebe.
Die Grundideen sind ziemlich einfach:
Man holt den Scancode von Port 0x60, analysiert Bit7 (key pressed or released?),
filtriert (=unterdrückt) den Shift-Tasten Scancode, speichert das Ergebnis in einer Variable ShiftKeyDown
und gibt dann den Scancode der nachfolgenden Taste zurück.
Damit steigt man dann in die richtige Keymap (Shift oder Non-Shift) und gibt mittels k_getch() das zum Scancode passende Zeichen zurück.
Das ganze geht sicher auch einfacher, aber ich möchte die Bildung des Scancodes aus Pressed/Released-Bit und 7-Bit-Tasten-Nummerierung klar machen. Da fehlt noch ein Bild.
Dieser kleine ohne Schnörkel daher kommende Text im Internet hat mir am besten geholfen, das Thema Scancode zu verstehen: http://www.qzx.com/pc-gpe/keyboard.txt
Das nächste logische Thema wären dann wohl Interrupts, was denkt ihr?
@abc.w:
Zitat:
Ich habe mich auch mal mit einem eigenen OS in Assembler beschäftigt zwecks endlich mal den Protected Mode der Intel CPUs zu verstehen. Habe aber diesbezüglich versagt und daraus wurde nichts. Hm, vertane Lebenszeit... hätte lieber in was anderes und sinnvolleres investieren sollen.
Ich hoffe, Du bist dem Protected Mode schon etwas näher gekommen.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 21:56:44 01.04.2009, insgesamt 8-mal bearbeitet
Beim Recherchieren habe ich eine ausführliche neue Tutorial-Serie (bisher 19 Teile) gefunden, didaktisch allerdings nicht geschickt gemacht, dafür in englisch und vollgestopft mit Detail-Infos:
http://brokenthorn.com/Resources/OSDev1.html
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
@Erhard: Bezüglich Linken mit mingw hab ich es leider nicht hingegriegt. Benutze nun DJGPP. Es ist nur so, dass meine Pfade auf mingw Verzeichnisse eingestellt sind und wenn ich make in der Eingabeaufforderung aufrufe, mingw make ausgeführt wird. Und da kamen wieder die komischen Fehlermeldungen. Und ich wollte wissen, woran es liegt...
Bezüglich des nächsten logischen Themas über Interrupts: Ich halte grade ein älteres Buch von Klaus-Dieter Thies "Die Innovativen 80286/80386 Architekturen" Teil 2. Im ersten Kapitel Basis-Programmier-Modell gibt es einen Unterkapitel "Ausnahmen und Interrupts". Vielleicht wäre das nächste logische Thema nicht nur über Interrupts alleine (als asynchrone Ereignisse), sondern auch über Ausnahmen (als synchrone Ereignisse) sinnvoll...
Erhard Henkes schrieb:
@abc.w:
Zitat:
Ich habe mich auch mal mit einem eigenen OS in Assembler beschäftigt zwecks endlich mal den Protected Mode der Intel CPUs zu verstehen. Habe aber diesbezüglich versagt und daraus wurde nichts. Hm, vertane Lebenszeit... hätte lieber in was anderes und sinnvolleres investieren sollen.
Ich hoffe, Du bist dem Protected Mode schon etwas näher gekommen.
Hm, ja, bisschen Aber noch weit davon entfernt, mal aus dem Stegreif eigene Tasks anlegen zu können, die sich nicht gegenseitig kaputtmachen könnten
+fricky schrieb:
abc.w schrieb:
Gibt es irgendwo einen freien x86 IP ab 80386, am Besten in VHDL?
16 bit Zet processor equivalent to an Intel 80186. Ich hätte gerne einen ab 80386... Ich hatte auch mal danach recherchiert, es gibt irgendwie keine, nicht mal "kostenpflichtige", warum auch immer.
Vielleicht wäre das nächste logische Thema nicht nur über Interrupts alleine (als asynchrone Ereignisse), sondern auch über Ausnahmen (als synchrone Ereignisse) sinnvoll...
Ja, das gehört zusammen: Gates, Interrupts, Traps, Exceptions.
Ich bin am nachdenken, wie man dieses Kapitel am besten anpackt.
Mir war es nur wichtig zu zeigen, dass man auch ohne Interrupts Daten per Polling aus dem Tastaturpuffer abholen kann. Das ist aber, als würde man ständig an der Tür stehen, um zu öffnen, falls jemand kommt (eben weitgehend unnötiger Aufwand). Nun wird nach dem Umzug nach PM wieder die "Klingel" eingeführt, damit wir asynchron auf "echten" Bedarf reagieren können.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 01:44:59 02.04.2009, insgesamt 2-mal bearbeitet
Ich habe eine Nomenklatur-Frage. Man benötigt in den verschiedenen C-Modulen immer wieder eine ganze Menge extern definierter Funktionen. Dies erfolgt oft durch Inkludieren einer Header-Datei namens "system.h", in der man dann alle "externen" Funktionen und einiges andere deklariert/definiert. Mir gefällt der Name "system.h" nicht besonders, weil er nicht wirklich verständlich ist. System kann alles und nichts sein.
Wie würdet ihr diesen Header nennen, der dann fast in jedem Modul am Anfang zu finden ist? Mein Favorit ist bisher "os.h". Lässt man geistig das "o" weg, landet man tatsächlich bei "system.h".
Vielleicht hat jemand eine viel bessere Idee.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
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).
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
Code:
__asm__("int $0x21");
Code:
__asm__("int $0x21");
Code:
__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
Code:
__asm__("int $0x21");
Code:
__asm__("int $0x21");
Code:
__asm__("int $0x21");
reicht bereits aus, um den Prozess zu starten.
Soll ich ein
Code:
keyboard_init(){__asm__("int $0x21");}
Code:
keyboard_init(){__asm__("int $0x21");}
Code:
keyboard_init(){__asm__("int $0x21");}
schreiben, das ich einmal zu Beginn der main() aufrufe??
Es gibt noch eine zweite - evtl. bessere - Lösung:
Code:
1 2 3 4 5 6 7 8
1 2 3 4 5 6 7 8
void keyboard_init()
{
/* Wait until buffer is empty */
while (inportb(0x64) & 0x01)
inportb(0x60);
// __asm__("int $0x21"); // ?
};
Code:
1 2 3 4 5 6 7 8
void keyboard_init()
{
/* Wait until buffer is empty */
while (inportb(0x64) & 0x01)
inportb(0x60);
// __asm__("int $0x21"); // ?
};
Code:
1 2 3 4 5 6 7 8
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:
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.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 02:43:04 04.04.2009, insgesamt 14-mal bearbeitet
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.
Zuletzt bearbeitet von Bitsy am 05:32:46 04.04.2009, insgesamt 2-mal bearbeitet
@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:
__asm__("int $0x21"); // 0x21 = 33, gemappt auf IRQ1 (Keyboard)
C/C++ Code:
__asm__("int $0x21"); // 0x21 = 33, gemappt auf IRQ1 (Keyboard)
C/C++ Code:
__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.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 12:35:04 04.04.2009, insgesamt 8-mal bearbeitet
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.
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.
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.
Zitat:
... 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?
Zitat:
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.):
Die Funktion k_null_pointer() wird aber ausgeführt und das System läuft Oder ist Zugriff mit Offset Null ok
Zitat:
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.
Zitat:
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?
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 16:58:45 04.04.2009, insgesamt 2-mal bearbeitet
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".
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.
"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.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 18:35:41 06.04.2009, insgesamt 3-mal bearbeitet
@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?
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 01:14:25 05.04.2009, insgesamt 1-mal bearbeitet
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.
Zuletzt bearbeitet von Bitsy am 10:59:36 05.04.2009, insgesamt 3-mal bearbeitet
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?
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.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
k_printf(" ************************************************", 0, 0xA);
k_printf(" * *", 1, 0xA);
k_printf(" * Welcome to PrettyOS 0.05 *", 2, 0xA);
k_printf(" * *", 3, 0xA);
k_printf(" * The C kernel has been loaded. *", 4, 0xA);
k_printf(" * *", 5, 0xA);
k_printf(" ************************************************", 6, 0xA);
update_cursor(8, 0);
sti();
while(TRUE)
{
static int zz=10;
sleepSeconds(20);
k_printf("20 sec have passed", ++zz, 0x0B);
};
return 0;
};
Es gibt ein offenbar bisher unerkanntes Problem:
Solange man die Finger vom Keyboard lässt läuft der timer_handler. Sobald man aber die erste Taste nach dem Drücken los lässt, bleibt der Timer (die gezählten timer_ticks) stehen. Nur bei gedrückter Taste läuft er wieder weiter. Rattenscharfer Effekt.
Bei Keyboard Port 0x60 u. 0x64 muss ich noch mal richtig lesen. Da klafft noch eine echte Wissenslücke bei mir.
@Nobuo T: Würdest Du Dir bitte keyboard.c anschauen. Da muss ja der Wurm lauern. Deine konkreten Vorschläge von oben zum Thema Keyboard Controller wurden noch nicht umgesetzt. Mich würde interessieren, was genau den IRQ0 beim Taste-Loslassen "outknockt" und beim Taste-Drücken wieder aktiviert.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 18:11:36 05.04.2009, insgesamt 2-mal bearbeitet
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 gehe mal davon aus, dass Erhard sich da schon einen groben Plan gemacht hat, was fuer eine Art von OS er fabrizieren will. Das ob ist da wohl keine Frage, das wie wird hier aktuell diskutiert.
Was du da weiter anfuehrst, sind doch eher Spezialfaelle und Teilgebiete, des Themenbereichs Scheduling (Echtzeit-Prozesse). Solche Ansaetze wie Prioritaetensteuerung oder verschiedene Ansaetze fairer Ressourcenverteilung mit verschiedensten abgefahrenen queue-Konstrukten (da gibt es wirklich eine Menge weit komplizierteres als einfache Prioritaetensteuerung) kann man da vielleicht in einem Ueberblick kurz anschneiden, aber um den Rahmen nicht zu sprengen, waere mein Vorschlag, sich schliesslich einfach auf Round Robin zu beschraenken.
Und DMA-Gefrickel ist ja nun voellig abgehoben. 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.
[OT]BTW ist das Basteln eines Synths auch auf dem PC selbst unter Windows kein groesseres Problem als unter DOS - solange man gute Hardware (am besten mit HW Wavetable Synth) oder zumindest sehr gute Treiber (zumindest directX) hat.[/OT]
Erhard Henkes schrieb:
Würdest Du Dir bitte keyboard.c anschauen. Da muss ja der Wurm lauern. Deine konkreten Vorschläge von oben zum Thema Keyboard Controller wurden noch nicht umgesetzt. Mich würde interessieren, was genau den IRQ0 beim Taste-Loslassen "outknockt" und beim Taste-Drücken wieder aktiviert.
Scharfer Effekt -> reichlich daemlicher Fehler, wenn ich mich nicht verschielt habe:
FetchAndAnalyzeScancode landet in einer Endlosschleife, wenn eine Taste losgelassen wird. Mal abgesehen von dem fehlenden EOI an den PIC duerfte das geloeschte IF wohl ausschlaggebend dafuer sein, dass sich in diesem Fall dann so lange nichts mehr tut, bis du wieder eine Taste drueckst.
Kurz: Die Endlosschleife hat in FetchAndAnalyzeScancode nichts zu suchen.
BTW: Ich hatte in meiner Auflistung noch vergessen, sich mal mit der Kernel-API (und einem HW-Abstraktions/Treiber-Prinzip) deines OS zu befasssen. Alle Kernel-Funktionen, Treiber etc. einfach nur ueber C-Funktionen und header zur Verfuegung zu stellen ist sicher nicht das Wahre.
Ich wuerde die Einrichtung eines kleinen Sets der wichtigsten Funktionen zum I/O und spaeter dann Speicher- und Prozessverwaltung ueber Interrupts aehnlich DOS oder Linux vorschlagen.
Je nachdem, was fuer einen Kern du basteln willst (Micro oder Monolith), waere es evtl. sinnvoll, das schon ganz am Anfang beim Aufbauen der ersten abstrakteren OS-Funktionen wie RAM-Verwaltung zu diskutieren, statt erst bei Adressraumtrennung, Privilegien und IPC. Evtl. mit Verweis auf diese spaeteren Themen zur Begruendung.
FetchAndAnalyzeScancode landet in einer Endlosschleife, wenn eine Taste losgelassen wird. ...
Kurz: Die Endlosschleife hat in FetchAndAnalyzeScancode nichts zu suchen.
Uups, stimmt. Ich dachte, es wäre etwas komplizierteres.
Da wimmelt es ja nur so von Endlosschleifen. Shit Polling!
Zitat:
Mal abgesehen von dem fehlenden EOI an den PIC duerfte das geloeschte IF wohl ausschlaggebend dafuer sein, dass sich in diesem Fall dann so lange nichts mehr tut, bis du wieder eine Taste drueckst.
Thanks.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 19:32:22 05.04.2009, insgesamt 1-mal bearbeitet
... 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?
Ja, AT&T Syntax mit dem Nachteil von links nach rechts Lesen, so wie man es normalerweise jahrelange macht, und dem Haufen Intel-Doku, wo man von rechts nach links lesen muss. Aber Lesbarkeit scheint ja sowieso der Punkt zu sein, auf den ich hier alleine bestehe.
Ich habe mal aus Neugier die Assembler-Dateien boot.asm, kernel.asm und isr.asm nach AT&T umgesetzt und erfolgreich versucht, einmal allein mit DJGPP (im Sinne ohne nasm) und einmal mit mingw Tools eine funktionierende MyOS.bin zu bauen. Nicht, dass ich darauf bestehe, vollständig auf diese Tools umzusteigen. Ist nur "Machbarkeitsstudie". Mein Makefile dazu sieht so aus:
Das einzige Programm, das nicht Teil von DJGPP und mingw ist, ist dd. Ich weiss nicht, wie man unter WinXP eine fest definierte Anzahl von Bytes aus einer Datei in eine andere kopieren kann, also habe ich dd benutzt, was ja von *nix stammt. Wie dem auch sei, so scheint zu gehen, ohne nasm...
Ein Nachteil bezüglich mingw ist noch, dass AntiVir bei von mingw as erzeugten Dateien meckert, sind angeblich irgendwelche Trojanische Pferde
Erhard Henkes schrieb:
Was abc.w anspricht ist mein Hin- und Herhüpfen zwischen C-Code (vor allem zum Thema Interrupts) und isr.asm.
Genauer meine ich, dass der gcc spezifische inline-Code in den c-Dateien nicht schön ist, weil, nun, sehr gcc spezifisch...
Ja, gute Idee, habe es umgesetzt. Thanks @ +fricky.
keine ursache. später aber, wenn du mal multitasking machen willst, kannste die funktion so nicht mehr verwenden. dann besser einen counter in den 'task control block' (der struct, die den zustand einer task kontrolliert) einbauen, und beim warten nicht einfach rechenzeit verbraten, sondern zur nächten task weiterschalten (plus einer möglichkeit, den 'wait' abbrechen zu können, z.b. von einer anderen task oder aus einem interrupt).
boot.asm, kernel.asm und isr.asm nach AT&T umgesetzt
Würdest Du mir diese Files bitte schicken oder - noch besser - hier posten? Damit ich die AT&T Syntax endlich besser lerne, denn isr.asm könnte man vielleicht auch weitgehend oder völlig in den C-Modulen aufgehen lassen, falls das bezüglich Ordnung mehr Sinn macht. Für mich ist z.B. die Intel Syntax eindeutig besser lesbar.
Das einzige Programm, das nicht Teil von DJGPP und mingw ist, ist dd. Ich weiss nicht, wie man unter WinXP eine fest definierte Anzahl von Bytes aus einer Datei in eine andere kopieren kann.
Nicht-Linux-User kennen dd nicht, verwenden partcopy (das ist, was Du anstelle dd suchst) oder rawwrite.
Ich bin da aber flexibel, nur möchte ich alles auf MS Windows ermöglichen, was ja bisher keinerlei Problem ist.
Ich möchte auch den Hinweis auf gvim (am Anfang fand ich das nicht uninteressant , weil OSDEV bezüglich des Flairs eher archaische Instrumente benötigt. ) verwerfen, weil notepad++ besser aussieht und weniger störrisch ist. Seht ihr das ähnlich?
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 19:51:58 05.04.2009, insgesamt 2-mal bearbeitet
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.
Erhard Henkes schrieb:
Ich möchte auch den Hinweis auf gvim (am Anfang fand ich das nicht uninteressant , weil OSDEV bezüglich des Flairs eher archaische Instrumente benötigt. ) verwerfen, weil notepad++ besser aussieht und weniger störrisch ist. Seht ihr das ähnlich?
Wie ich das sehe, kann man sich vielleicht denken.
Ich möchte auch den Hinweis auf gvim (am Anfang fand ich das nicht uninteressant , weil OSDEV bezüglich des Flairs eher archaische Instrumente benötigt. ) verwerfen, weil notepad++ besser aussieht und weniger störrisch ist. Seht ihr das ähnlich?
klar, wenn man assembler-code auf tiefster low-level ebene schreibt, muss man noch lange keine prähistorischen tools verwenden.
Bezüglich der Assembler-Dateien in AT&T, die Kommentare sind alle unverändert. boot.s (hier habe ich, glaube, nur die Anzahl der Sektoren auf 40 geändert):
print_string:
mov $0x0E, %ah # VGA BIOS fnct. 0x0E: teletype
1:
lodsb # grab a byte from SI
testb %al, %al # NUL?
jz 2f # if the result is zero, get out
int $0x10 # otherwise, print out the character!
jmp 1b
2:
ret
print_string:
mov $0x0E, %ah # VGA BIOS fnct. 0x0E: teletype
1:
lodsb # grab a byte from SI
testb %al, %al # NUL?
jz 2f # if the result is zero, get out
int $0x10 # otherwise, print out the character!
jmp 1b
2:
ret
print_string:
mov $0x0E, %ah # VGA BIOS fnct. 0x0E: teletype
1:
lodsb # grab a byte from SI
testb %al, %al # NUL?
jz 2f # if the result is zero, get out
int $0x10 # otherwise, print out the character!
jmp 1b
2:
ret
# we actually only need to do this ONCE, but for now it doesn't hurt to do this more often when
# switching between RM and PM
inb $0x92, %al # switch A20 gate via fast A20 port 92
cmpb $0xFF, %al # if it reads 0xFF, nothing's implemented on this port
je 1f
orb $2, %al # set A20_Gate_Bit (bit 1)
andb $0xFE, %al # clear INIT_NOW bit (don't reset pc...)
outb %al, $0x92
jmp 2f
1: # no fast shortcut -> use the slow kbc...
call empty_8042
movb 0xD1, %al # kbc command: write to output port
outb %al, $0x64
call empty_8042
movb $0xDF, %al # writing this to kbc output port enables A20
outb %al, $0x60
call empty_8042
2:
mov %cr0, %eax # switch-over to Protected Mode
orl $1, %eax # set bit 0 of CR0 register
movl %eax, %cr0
jump_to_protected_mode:
ljmp $8, $ProtectedMode
#########
# Calls #
#########
empty_8042:
call Waitingloop
inb $0x64, %al
cmpb $0xFF, %al # ... no real kbc at all?
je 1f
testb $1, %al # something in input buffer?
jz 2f
call Waitingloop
inb $0x60, %al # yes: read buffer
jmp empty_8042 # and try again
2:
testb $2, %al # command buffer empty?
jnz empty_8042 # no: we can't send anything new till it's empty
1:
ret
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
get_string:
xorw %cx, %cx
1:
xorw %ax, %ax
int $0x16 # wait for keypress
cmpb $8, %al # backspace pressed?
je 2f # yes, handle it
cmpb $13, %al # enter pressed?
je 3f # yes, we're done
cmpb $63, %cl # 63 chars inputted?
je 1b # yes, only let in backspace and enter
movb $0x0E, %ah
int $0x10 # print out character
stosb # put character in buffer
inc %cx
jmp 1b
2:
jcxz 1b # zero? (start of the string) if yes, ignore the key
dec %di
movb $0, (%di) # delete character
dec %cx # decrement counter as well
movb $0x0E, %ah
int $0x10 # backspace on the screen
movb $32, %al
int $0x10 # blank character out
movb $8, %al
int $0x10 # backspace again
jmp 1b # go to the main loop
3:
movb $0, (%di) # null terminator
movw $0x0E0D, %ax
int $0x10
movb $0x0A, %al
int $0x10 # newline
ret
strcmp:
1:
movb (%si), %al # grab a byte from SI
cmpb (%di), %al # are SI and DI equal?
jne 2f # no, we're done.
testb %al, %al # zero?
jz 2f # yes, we're done.
inc %di # increment DI
inc %si # increment SI
jmp 1b # loop!
2:
ret
clrscr:
movw $0x0600, %ax
xorw %cx, %cx
mov $0x174F, %dx
mov $0x07, %bh
int $0x10
ret
# You load the address you want to output to in [PutStr_Ptr],
# the address of the string (has to be NUL terminated)
# you want to print in esi and the attributes in ah
# lodsb loads one byte from esi into al, then increments esi,
# then it checks for a NUL terminator,
# then it moves the char into the write position in video memory,
# then increments edi and writes the attributes,
# loops until it finds NUL pointer at which point it breaks ...
# we actually only need to do this ONCE, but for now it doesn't hurt to do this more often when
# switching between RM and PM
inb $0x92, %al # switch A20 gate via fast A20 port 92
cmpb $0xFF, %al # if it reads 0xFF, nothing's implemented on this port
je 1f
orb $2, %al # set A20_Gate_Bit (bit 1)
andb $0xFE, %al # clear INIT_NOW bit (don't reset pc...)
outb %al, $0x92
jmp 2f
1: # no fast shortcut -> use the slow kbc...
call empty_8042
movb 0xD1, %al # kbc command: write to output port
outb %al, $0x64
call empty_8042
movb $0xDF, %al # writing this to kbc output port enables A20
outb %al, $0x60
call empty_8042
2:
mov %cr0, %eax # switch-over to Protected Mode
orl $1, %eax # set bit 0 of CR0 register
movl %eax, %cr0
jump_to_protected_mode:
ljmp $8, $ProtectedMode
#########
# Calls #
#########
empty_8042:
call Waitingloop
inb $0x64, %al
cmpb $0xFF, %al # ... no real kbc at all?
je 1f
testb $1, %al # something in input buffer?
jz 2f
call Waitingloop
inb $0x60, %al # yes: read buffer
jmp empty_8042 # and try again
2:
testb $2, %al # command buffer empty?
jnz empty_8042 # no: we can't send anything new till it's empty
1:
ret
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
get_string:
xorw %cx, %cx
1:
xorw %ax, %ax
int $0x16 # wait for keypress
cmpb $8, %al # backspace pressed?
je 2f # yes, handle it
cmpb $13, %al # enter pressed?
je 3f # yes, we're done
cmpb $63, %cl # 63 chars inputted?
je 1b # yes, only let in backspace and enter
movb $0x0E, %ah
int $0x10 # print out character
stosb # put character in buffer
inc %cx
jmp 1b
2:
jcxz 1b # zero? (start of the string) if yes, ignore the key
dec %di
movb $0, (%di) # delete character
dec %cx # decrement counter as well
movb $0x0E, %ah
int $0x10 # backspace on the screen
movb $32, %al
int $0x10 # blank character out
movb $8, %al
int $0x10 # backspace again
jmp 1b # go to the main loop
3:
movb $0, (%di) # null terminator
movw $0x0E0D, %ax
int $0x10
movb $0x0A, %al
int $0x10 # newline
ret
strcmp:
1:
movb (%si), %al # grab a byte from SI
cmpb (%di), %al # are SI and DI equal?
jne 2f # no, we're done.
testb %al, %al # zero?
jz 2f # yes, we're done.
inc %di # increment DI
inc %si # increment SI
jmp 1b # loop!
2:
ret
clrscr:
movw $0x0600, %ax
xorw %cx, %cx
mov $0x174F, %dx
mov $0x07, %bh
int $0x10
ret
# You load the address you want to output to in [PutStr_Ptr],
# the address of the string (has to be NUL terminated)
# you want to print in esi and the attributes in ah
# lodsb loads one byte from esi into al, then increments esi,
# then it checks for a NUL terminator,
# then it moves the char into the write position in video memory,
# then increments edi and writes the attributes,
# loops until it finds NUL pointer at which point it breaks ...
# we actually only need to do this ONCE, but for now it doesn't hurt to do this more often when
# switching between RM and PM
inb $0x92, %al # switch A20 gate via fast A20 port 92
cmpb $0xFF, %al # if it reads 0xFF, nothing's implemented on this port
je 1f
orb $2, %al # set A20_Gate_Bit (bit 1)
andb $0xFE, %al # clear INIT_NOW bit (don't reset pc...)
outb %al, $0x92
jmp 2f
1: # no fast shortcut -> use the slow kbc...
call empty_8042
movb 0xD1, %al # kbc command: write to output port
outb %al, $0x64
call empty_8042
movb $0xDF, %al # writing this to kbc output port enables A20
outb %al, $0x60
call empty_8042
2:
mov %cr0, %eax # switch-over to Protected Mode
orl $1, %eax # set bit 0 of CR0 register
movl %eax, %cr0
jump_to_protected_mode:
ljmp $8, $ProtectedMode
#########
# Calls #
#########
empty_8042:
call Waitingloop
inb $0x64, %al
cmpb $0xFF, %al # ... no real kbc at all?
je 1f
testb $1, %al # something in input buffer?
jz 2f
call Waitingloop
inb $0x60, %al # yes: read buffer
jmp empty_8042 # and try again
2:
testb $2, %al # command buffer empty?
jnz empty_8042 # no: we can't send anything new till it's empty
1:
ret
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
get_string:
xorw %cx, %cx
1:
xorw %ax, %ax
int $0x16 # wait for keypress
cmpb $8, %al # backspace pressed?
je 2f # yes, handle it
cmpb $13, %al # enter pressed?
je 3f # yes, we're done
cmpb $63, %cl # 63 chars inputted?
je 1b # yes, only let in backspace and enter
movb $0x0E, %ah
int $0x10 # print out character
stosb # put character in buffer
inc %cx
jmp 1b
2:
jcxz 1b # zero? (start of the string) if yes, ignore the key
dec %di
movb $0, (%di) # delete character
dec %cx # decrement counter as well
movb $0x0E, %ah
int $0x10 # backspace on the screen
movb $32, %al
int $0x10 # blank character out
movb $8, %al
int $0x10 # backspace again
jmp 1b # go to the main loop
3:
movb $0, (%di) # null terminator
movw $0x0E0D, %ax
int $0x10
movb $0x0A, %al
int $0x10 # newline
ret
strcmp:
1:
movb (%si), %al # grab a byte from SI
cmpb (%di), %al # are SI and DI equal?
jne 2f # no, we're done.
testb %al, %al # zero?
jz 2f # yes, we're done.
inc %di # increment DI
inc %si # increment SI
jmp 1b # loop!
2:
ret
clrscr:
movw $0x0600, %ax
xorw %cx, %cx
mov $0x174F, %dx
mov $0x07, %bh
int $0x10
ret
# You load the address you want to output to in [PutStr_Ptr],
# the address of the string (has to be NUL terminated)
# you want to print in esi and the attributes in ah
# lodsb loads one byte from esi into al, then increments esi,
# then it checks for a NUL terminator,
# then it moves the char into the write position in video memory,
# then increments edi and writes the attributes,
# loops until it finds NUL pointer at which point it breaks ...
Das einzige Programm, das nicht Teil von DJGPP und mingw ist, ist dd. Ich weiss nicht, wie man unter WinXP eine fest definierte Anzahl von Bytes aus einer Datei in eine andere kopieren kann.
Nicht-Linux-User kennen dd nicht, verwenden partcopy (das ist, was Du anstelle dd suchst) oder rawwrite.
Ich bin da aber flexibel, nur möchte ich alles auf MS Windows ermöglichen, was ja bisher keinerlei Problem ist.
Ich habe grade geguckt, woher ich dd auf meinem System habe... aus dem Verzeichnis C:\Compiler\WinAVR-20081205\utils\bin. Nicht schön. Benutze ein Programm und weiss überhaupt nicht, woher ich es habe. Das kommt davon, wenn man die PATH Variable einfach mal so modifiziert...
Erhard Henkes schrieb:
Ich möchte auch den Hinweis auf gvim (am Anfang fand ich das nicht uninteressant , weil OSDEV bezüglich des Flairs eher archaische Instrumente benötigt. ) verwerfen, weil notepad++ besser aussieht und weniger störrisch ist. Seht ihr das ähnlich?
Ich benutze in letzter Zeit ausschliesslich gvim...
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...
@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.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
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.
Ich habe den aktuellen Code zum Testen "upgeloadet".
/* Wait until buffer is empty */ void keyboard_init()
{
while (inportb(0x64)&1)
inportb(0x60);
};
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
if ( scancode & 0x80 ) // Key released? Check bit 7 (10000000b = 0x80) of scan code for this
{
scancode &= 0x7F; // Key was released, compare only low seven bits: 01111111b = 0x7F if ( scancode == KRLEFT_SHIFT || scancode == KRRIGHT_SHIFT ) // A key was released, shift key up?
{
ShiftKeyDown = 0; // yes, it is up --> NonShift
}
}
else// Key was pressed.
{
// Capture scan code of shift key, if pressed if ( scancode == KRLEFT_SHIFT || scancode == KRRIGHT_SHIFT )
{
ShiftKeyDown = 1; // It is down, use asciiShift characters continue; // Loop, so it will not return a scan code for the shift key
}
}
break; // Leave the loop
}
return scancode;
}
unsigned char const k_getch() // Scancode --> ASCII
{
unsigned int scan; // scancode from the keyboard unsigned char retchar; // The chararacter that returns the scan code to ASCII code
scan = FetchAndAnalyzeScancode(); // Grab scancode, and get the position of the shift key if ( ShiftKeyDown )
retchar = asciiShift[scan]; // (Upper) Shift Codes else
retchar = asciiNonShift[ scan ]; // (Lower) Non-Shift Codes return retchar; // ASCII version
}
/* Wait until buffer is empty */ void keyboard_init()
{
while (inportb(0x64)&1)
inportb(0x60);
};
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
if ( scancode & 0x80 ) // Key released? Check bit 7 (10000000b = 0x80) of scan code for this
{
scancode &= 0x7F; // Key was released, compare only low seven bits: 01111111b = 0x7F if ( scancode == KRLEFT_SHIFT || scancode == KRRIGHT_SHIFT ) // A key was released, shift key up?
{
ShiftKeyDown = 0; // yes, it is up --> NonShift
}
}
else// Key was pressed.
{
// Capture scan code of shift key, if pressed if ( scancode == KRLEFT_SHIFT || scancode == KRRIGHT_SHIFT )
{
ShiftKeyDown = 1; // It is down, use asciiShift characters continue; // Loop, so it will not return a scan code for the shift key
}
}
break; // Leave the loop
}
return scancode;
}
unsigned char const k_getch() // Scancode --> ASCII
{
unsigned int scan; // scancode from the keyboard unsigned char retchar; // The chararacter that returns the scan code to ASCII code
scan = FetchAndAnalyzeScancode(); // Grab scancode, and get the position of the shift key if ( ShiftKeyDown )
retchar = asciiShift[scan]; // (Upper) Shift Codes else
retchar = asciiNonShift[ scan ]; // (Lower) Non-Shift Codes return retchar; // ASCII version
}
/* Wait until buffer is empty */ void keyboard_init()
{
while (inportb(0x64)&1)
inportb(0x60);
};
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
if ( scancode & 0x80 ) // Key released? Check bit 7 (10000000b = 0x80) of scan code for this
{
scancode &= 0x7F; // Key was released, compare only low seven bits: 01111111b = 0x7F if ( scancode == KRLEFT_SHIFT || scancode == KRRIGHT_SHIFT ) // A key was released, shift key up?
{
ShiftKeyDown = 0; // yes, it is up --> NonShift
}
}
else// Key was pressed.
{
// Capture scan code of shift key, if pressed if ( scancode == KRLEFT_SHIFT || scancode == KRRIGHT_SHIFT )
{
ShiftKeyDown = 1; // It is down, use asciiShift characters continue; // Loop, so it will not return a scan code for the shift key
}
}
break; // Leave the loop
}
return scancode;
}
unsigned char const k_getch() // Scancode --> ASCII
{
unsigned int scan; // scancode from the keyboard unsigned char retchar; // The chararacter that returns the scan code to ASCII code
scan = FetchAndAnalyzeScancode(); // Grab scancode, and get the position of the shift key if ( ShiftKeyDown )
retchar = asciiShift[scan]; // (Upper) Shift Codes else
retchar = asciiNonShift[ scan ]; // (Lower) Non-Shift Codes return retchar; // ASCII version
}
@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:
Code:
1 2 3 4 5 6 7 8 9 10
1 2 3 4 5 6 7 8 9 10
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
Code:
1 2 3 4 5 6 7 8 9 10
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
Code:
1 2 3 4 5 6 7 8 9 10
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...
Code:
1 2 3 4 5 6 7 8 9 10
1 2 3 4 5 6 7 8 9 10
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
Code:
1 2 3 4 5 6 7 8 9 10
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
Code:
1 2 3 4 5 6 7 8 9 10
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...
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ß.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 22:51:15 05.04.2009, insgesamt 1-mal bearbeitet
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.
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.
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:
C/C++ Code:
void timer_handler(struct regs* r)
{
++timer_ticks;
if (eticks)
--eticks;
}
C/C++ Code:
void timer_handler(struct regs* r)
{
++timer_ticks;
if (eticks)
--eticks;
}
C/C++ Code:
void timer_handler(struct regs* r)
{
++timer_ticks;
if (eticks)
--eticks;
}
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?
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
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.
Zitat:
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.
Zitat:
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.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 02:01:24 06.04.2009, insgesamt 2-mal bearbeitet
@Nobuo T:
Noch zu den Themen: Port 0x61 Bit7, EOI und IF
Code:
1 2 3 4 5 6 7 8
1 2 3 4 5 6 7 8
; Acknowledge Keyboard : Toggle PB bit 7
PUSH AX ; save scan code
IN AL, 61H ; read current PB value
OR AL, 80H ; set bit 7
OUT 61H, AL ; write value back + bit 7=1
AND AL, 7FH ; clear bit 7–back to original
OUT 61H , AL ; write original value back
POP AX ; restore scan code
Code:
1 2 3 4 5 6 7 8
; Acknowledge Keyboard : Toggle PB bit 7
PUSH AX ; save scan code
IN AL, 61H ; read current PB value
OR AL, 80H ; set bit 7
OUT 61H, AL ; write value back + bit 7=1
AND AL, 7FH ; clear bit 7–back to original
OUT 61H , AL ; write original value back
POP AX ; restore scan code
Code:
1 2 3 4 5 6 7 8
; Acknowledge Keyboard : Toggle PB bit 7
PUSH AX ; save scan code
IN AL, 61H ; read current PB value
OR AL, 80H ; set bit 7
OUT 61H, AL ; write value back + bit 7=1
AND AL, 7FH ; clear bit 7–back to original
OUT 61H , AL ; write original value back
POP AX ; restore scan code
Note that if you repeatedly read the port 0x60 without waiting for another IRQ, you'll read the same byte again. That's the 'normal' behaviour of keyboard controller, but if that doesn't suit your needs, you can still "acknowledge" the scancode by quickly disabling and re-enabling the keyboard at the PPI (Programmable Peripheral Interface):
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?
Würde das so passen?
C/C++ Code:
1 2 3 4 5 6 7 8 9 10 11 12
1 2 3 4 5 6 7 8 9 10 11 12
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
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
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
// 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
C/C++ Code:
// 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
C/C++ Code:
// 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
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:
C/C++ Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
/* EOI command to the controllers. If you don't send them, any more IRQs cannot be raised */ void irq_handler(struct regs* r)
{
/* This is a blank function pointer */ void (*handler)(struct regs* r);
/* Find out if we have a custom handler to run for this IRQ, and then finally, run it */
handler = irq_routines[r->int_no - 32];
if (handler) { handler(r); }
/* If the IDT entry that was invoked was greater than 40 (IRQ8 - 15),
* then we need to send an EOI to the slave controller */ if (r->int_no >= 40) { outportb(0xA0, 0x20); }
/* In either case, we need to send an EOI to the master interrupt controller too */
outportb(0x20, 0x20);
}
C/C++ Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
/* EOI command to the controllers. If you don't send them, any more IRQs cannot be raised */ void irq_handler(struct regs* r)
{
/* This is a blank function pointer */ void (*handler)(struct regs* r);
/* Find out if we have a custom handler to run for this IRQ, and then finally, run it */
handler = irq_routines[r->int_no - 32];
if (handler) { handler(r); }
/* If the IDT entry that was invoked was greater than 40 (IRQ8 - 15),
* then we need to send an EOI to the slave controller */ if (r->int_no >= 40) { outportb(0xA0, 0x20); }
/* In either case, we need to send an EOI to the master interrupt controller too */
outportb(0x20, 0x20);
}
C/C++ Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
/* EOI command to the controllers. If you don't send them, any more IRQs cannot be raised */ void irq_handler(struct regs* r)
{
/* This is a blank function pointer */ void (*handler)(struct regs* r);
/* Find out if we have a custom handler to run for this IRQ, and then finally, run it */
handler = irq_routines[r->int_no - 32];
if (handler) { handler(r); }
/* If the IDT entry that was invoked was greater than 40 (IRQ8 - 15),
* then we need to send an EOI to the slave controller */ if (r->int_no >= 40) { outportb(0xA0, 0x20); }
/* In either case, we need to send an EOI to the master interrupt controller too */
outportb(0x20, 0x20);
}
Da war doch noch die Rede von einem IF? Ist dies das IF bei der CPU im Flag-Register?
Zitat:
Dieses Flag ist ... ein Steuerungs-Flag. Wird es gelöscht, so werden Interrupts vom Prozessor ausgesetzt. Man löscht dieses Flag zu Beginn von Interrupt-Routinen (manche Prozessoren erledigen dies automatisch), damit diese ungestört bis zum Ende durchlaufen können. Der Prozessor führt nach dem Setzen des Interrupt-Enable-Flags erst noch eine Anweisung aus, bevor er wieder Interrupts zulässt. So kann man in einer Interrupt-Routine nach dem Setzten dieses Flags noch den RETURN-Befehl ausführen lassen. Es gibt Interrupts, die vom Interrupt-Enable-Flag unberührt bleiben. Diese nennt man nicht-maskierbare Interrupts (NMI).
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:
C/C++ Code:
void timer_handler(struct regs* r)
{
++timer_ticks;
if (eticks)
--eticks;
}
C/C++ Code:
void timer_handler(struct regs* r)
{
++timer_ticks;
if (eticks)
--eticks;
}
C/C++ Code:
void timer_handler(struct regs* r)
{
++timer_ticks;
if (eticks)
--eticks;
}
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:
Code:
1 2 3 4 5 6 7 8 9 10 11
1 2 3 4 5 6 7 8 9 10 11
// 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);
Code:
1 2 3 4 5 6 7 8 9 10 11
// 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);
Code:
1 2 3 4 5 6 7 8 9 10 11
// 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:
Code:
// hole ein zeichen aus dem tastatur-buffer. wenn c -1 (oder EOF) ist, war der FIFO leer
int c = getchar();
Code:
// hole ein zeichen aus dem tastatur-buffer. wenn c -1 (oder EOF) ist, war der FIFO leer
int c = getchar();
Code:
// hole ein zeichen aus dem tastatur-buffer. wenn c -1 (oder EOF) ist, war der FIFO leer
int c = getchar();
@+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.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 11:11:19 06.04.2009, insgesamt 1-mal bearbeitet
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.
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.
Zitat:
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.
Zitat:
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.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 18:20:23 06.04.2009, insgesamt 1-mal bearbeitet
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:
Zitat:
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?
C/C++ Code:
1 2 3 4 5 6 7 8 9 10 11 12
1 2 3 4 5 6 7 8 9 10 11 12
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
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
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
// 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
C/C++ Code:
// 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
C/C++ Code:
// 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 ).
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).
void update_cursor()
{
USHORT position = csr_y * 80 + csr_x;
// cursor HIGH port to vga INDEX register
outportb(0x3D4, 0x0E);
outportb(0x3D5, (UCHAR)((position>>8)&0xFF));
// cursor LOW port to vga INDEX register
outportb(0x3D4, 0x0F);
outportb(0x3D5, (UCHAR)(position&0xFF));
};
void putch(UCHAR c)
{
USHORT* pos;
UINT att = attrib << 8;
if(c == 0x08) // backspace: move the cursor back one space
{
if(csr_x != 0) --csr_x;
}
else if(c == 0x09) // tab: increment csr_x (divisible by 8)
{
csr_x = (csr_x + 8) & ~(8 - 1);
}
else if(c == '\r') // cr: cursor back to the margin
{
csr_x = 0;
}
else if(c == '\n') // newline: like 'cr': cursor to the margin and increment csr_y
{
csr_x = 0; ++csr_y;
}
/* Any character greater than and including a space, is a printable character.
* Index = [(y * width) + x] */ else if(c >= ' ')
{
pos = vidmem + (csr_y * 80 + csr_x);
*pos = c | att; // Character AND attributes: color
++csr_x;
}
if(csr_x >= 80) // cursor reaches edge of the screen's width, a new line is inserted
{
csr_x = 0;
++csr_y;
}
/* Scroll the screen if needed, and finally move the cursor */
scroll();
update_cursor();
}
void puts(UCHAR* text)
{
int i;
for(i=0; i<k_strlen(text); ++i)
putch(text[i]);
void update_cursor()
{
USHORT position = csr_y * 80 + csr_x;
// cursor HIGH port to vga INDEX register
outportb(0x3D4, 0x0E);
outportb(0x3D5, (UCHAR)((position>>8)&0xFF));
// cursor LOW port to vga INDEX register
outportb(0x3D4, 0x0F);
outportb(0x3D5, (UCHAR)(position&0xFF));
};
void putch(UCHAR c)
{
USHORT* pos;
UINT att = attrib << 8;
if(c == 0x08) // backspace: move the cursor back one space
{
if(csr_x != 0) --csr_x;
}
else if(c == 0x09) // tab: increment csr_x (divisible by 8)
{
csr_x = (csr_x + 8) & ~(8 - 1);
}
else if(c == '\r') // cr: cursor back to the margin
{
csr_x = 0;
}
else if(c == '\n') // newline: like 'cr': cursor to the margin and increment csr_y
{
csr_x = 0; ++csr_y;
}
/* Any character greater than and including a space, is a printable character.
* Index = [(y * width) + x] */ else if(c >= ' ')
{
pos = vidmem + (csr_y * 80 + csr_x);
*pos = c | att; // Character AND attributes: color
++csr_x;
}
if(csr_x >= 80) // cursor reaches edge of the screen's width, a new line is inserted
{
csr_x = 0;
++csr_y;
}
/* Scroll the screen if needed, and finally move the cursor */
scroll();
update_cursor();
}
void puts(UCHAR* text)
{
int i;
for(i=0; i<k_strlen(text); ++i)
putch(text[i]);
void update_cursor()
{
USHORT position = csr_y * 80 + csr_x;
// cursor HIGH port to vga INDEX register
outportb(0x3D4, 0x0E);
outportb(0x3D5, (UCHAR)((position>>8)&0xFF));
// cursor LOW port to vga INDEX register
outportb(0x3D4, 0x0F);
outportb(0x3D5, (UCHAR)(position&0xFF));
};
void putch(UCHAR c)
{
USHORT* pos;
UINT att = attrib << 8;
if(c == 0x08) // backspace: move the cursor back one space
{
if(csr_x != 0) --csr_x;
}
else if(c == 0x09) // tab: increment csr_x (divisible by 8)
{
csr_x = (csr_x + 8) & ~(8 - 1);
}
else if(c == '\r') // cr: cursor back to the margin
{
csr_x = 0;
}
else if(c == '\n') // newline: like 'cr': cursor to the margin and increment csr_y
{
csr_x = 0; ++csr_y;
}
/* Any character greater than and including a space, is a printable character.
* Index = [(y * width) + x] */ else if(c >= ' ')
{
pos = vidmem + (csr_y * 80 + csr_x);
*pos = c | att; // Character AND attributes: color
++csr_x;
}
if(csr_x >= 80) // cursor reaches edge of the screen's width, a new line is inserted
{
csr_x = 0;
++csr_y;
}
/* Scroll the screen if needed, and finally move the cursor */
scroll();
update_cursor();
}
void puts(UCHAR* text)
{
int i;
for(i=0; i<k_strlen(text); ++i)
putch(text[i]);
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
C/C++ Code:
k_memsetw (vidmem, blank, 80 * 25);
C/C++ Code:
k_memsetw (vidmem, blank, 80 * 25);
C/C++ Code:
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.
puts:
Wozu extra mit strlen zaehlen, wenn du das Array danach eh nochmal Zeichen fuer Zeichen abwanderst?
Mach's doch gleich so:
(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?
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?
Ja, ich habe es mal auskommentiert. Habe putch(...) anstelle puts(...) gesetzt.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 00:26:43 09.04.2009, insgesamt 1-mal bearbeitet
Da kam nichts aus meinem PC!
Ich habe einen speaker code (timer, channel 2) probiert, aber da ist alles abgestürzt, hier ist der Code, ging aber nicht, alles abgestürzt.
Ich habe die GDT/IDT-Infos und Timer-Späßchen auf dem Screen lahm gelegt und begonnen, den Keyboard Treiber zu überarbeiten, damit man endlich Texte mit automatischem Zeilenumbruch und Scroll eintippen kann, so eine Art Primitiv-Editor eben. Nobuo T dürfte sich freuen, die while(TRUE) Story ist vorbei, nun läuft es nur einmal den "Scanner" durch, wie es logischer ist. BACKSPACE, TAB und ENTER funktionieren, aber man hat noch keine Steuerung mit den Pfeiltasten (da bin ich noch vorsichtig, ein move_cursor_right() ist allerdings schon eingebaut; Orientierung an schwarzer DOS-Box (cmd)?), diesbezüglich muss man also noch "übertippen" bzw. sich mit der Kombination aus TAB und BACKSPACE korrekt positionieren. Pos1, Ende, Entf ... sind auch noch nicht aktiviert. Das läuft ja alles via putc(...) als default im printformat(...), siehe keyboard_handler(...).
UCHAR ShiftKeyDown = 0; // Variable for Shift Key Down
UCHAR KeyPressed = 0; // Variable for Key Pressed
UCHAR scan = 0; // Scan code from Keyboard
UCHAR FetchAndAnalyzeScancode()
{
if( inportb(0x64)&1 )
scan = inportb(0x60); // 0x60: get scan code from the keyboard
// ACK: toggle bit 7 at port 0x61
UCHAR port_value = inportb(0x61);
outportb(0x61,port_value | 0x80); // 0->1
outportb(0x61,port_value &~ 0x80); // 1->0
if( scan & 0x80 ) // Key released? Check bit 7 (10000000b = 0x80) of scan code for this
{
KeyPressed = 0;
scan &= 0x7F; // Key was released, compare only low seven bits: 01111111b = 0x7F if( scan == KRLEFT_SHIFT || scan == KRRIGHT_SHIFT ) // A key was released, shift key up?
{
ShiftKeyDown = 0; // yes, it is up --> NonShift
}
}
else// Key was pressed.
{
KeyPressed = 1;
if( scan == KRLEFT_SHIFT || scan == KRRIGHT_SHIFT )
{
ShiftKeyDown = 1; // It is down, use asciiShift characters
}
}
return scan;
}
UCHAR k_getch() // Scancode --> ASCII
{
UCHAR retchar; // The chararacter that returns the scan code to ASCII code
scan = FetchAndAnalyzeScancode(); // Grab scancode, and get the position of the shift key
UCHAR ShiftKeyDown = 0; // Variable for Shift Key Down
UCHAR KeyPressed = 0; // Variable for Key Pressed
UCHAR scan = 0; // Scan code from Keyboard
UCHAR FetchAndAnalyzeScancode()
{
if( inportb(0x64)&1 )
scan = inportb(0x60); // 0x60: get scan code from the keyboard
// ACK: toggle bit 7 at port 0x61
UCHAR port_value = inportb(0x61);
outportb(0x61,port_value | 0x80); // 0->1
outportb(0x61,port_value &~ 0x80); // 1->0
if( scan & 0x80 ) // Key released? Check bit 7 (10000000b = 0x80) of scan code for this
{
KeyPressed = 0;
scan &= 0x7F; // Key was released, compare only low seven bits: 01111111b = 0x7F if( scan == KRLEFT_SHIFT || scan == KRRIGHT_SHIFT ) // A key was released, shift key up?
{
ShiftKeyDown = 0; // yes, it is up --> NonShift
}
}
else// Key was pressed.
{
KeyPressed = 1;
if( scan == KRLEFT_SHIFT || scan == KRRIGHT_SHIFT )
{
ShiftKeyDown = 1; // It is down, use asciiShift characters
}
}
return scan;
}
UCHAR k_getch() // Scancode --> ASCII
{
UCHAR retchar; // The chararacter that returns the scan code to ASCII code
scan = FetchAndAnalyzeScancode(); // Grab scancode, and get the position of the shift key
UCHAR ShiftKeyDown = 0; // Variable for Shift Key Down
UCHAR KeyPressed = 0; // Variable for Key Pressed
UCHAR scan = 0; // Scan code from Keyboard
UCHAR FetchAndAnalyzeScancode()
{
if( inportb(0x64)&1 )
scan = inportb(0x60); // 0x60: get scan code from the keyboard
// ACK: toggle bit 7 at port 0x61
UCHAR port_value = inportb(0x61);
outportb(0x61,port_value | 0x80); // 0->1
outportb(0x61,port_value &~ 0x80); // 1->0
if( scan & 0x80 ) // Key released? Check bit 7 (10000000b = 0x80) of scan code for this
{
KeyPressed = 0;
scan &= 0x7F; // Key was released, compare only low seven bits: 01111111b = 0x7F if( scan == KRLEFT_SHIFT || scan == KRRIGHT_SHIFT ) // A key was released, shift key up?
{
ShiftKeyDown = 0; // yes, it is up --> NonShift
}
}
else// Key was pressed.
{
KeyPressed = 1;
if( scan == KRLEFT_SHIFT || scan == KRRIGHT_SHIFT )
{
ShiftKeyDown = 1; // It is down, use asciiShift characters
}
}
return scan;
}
UCHAR k_getch() // Scancode --> ASCII
{
UCHAR retchar; // The chararacter that returns the scan code to ASCII code
scan = FetchAndAnalyzeScancode(); // Grab scancode, and get the position of the shift key
/* Wait until buffer is empty */ void keyboard_init()
{
while( inportb(0x64)&1 )
inportb(0x60);
};
Was fehlt eurer Meinung nach noch bei dem EDIT-Modus des OS?
"Befehle" würde man dann wohl per ENTER entgegen nehmen?
In einem strcmp (analog zur Methode im Real Mode, jetzt nur in C) vergleichen und entsprechend über eine switch/case-Kontrollstruktur reagieren?
Welche Befehle würdet ihr denn als ersten Schritt implementieren?
- Info/info
- Help/help/?
- Reboot/reboot
- ...
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 12:05:03 10.04.2009, insgesamt 2-mal bearbeitet
Damit wir nicht den Gesamtüberblick verlieren, hier eine rohe Tasklist / Sammlung aus den bisherigen Posts:
Zitat:
- 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
- Kernel-API (und einem HW-Abstraktions/Treiber-Prinzip).
Alle Kernel-Funktionen, Treiber etc. einfach nur ueber C-Funktionen und header zur Verfuegung zu stellen,
ist sicher nicht das Wahre. Ich wuerde die Einrichtung eines kleinen Sets der wichtigsten Funktionen zum
I/O und spaeter dann Speicher- und Prozessverwaltung ueber Interrupts aehnlich DOS oder Linux vorschlagen.
Je nachdem, was fuer einen Kern du basteln willst (Micro oder Monolith), waere es evtl. sinnvoll,
das schon ganz am Anfang beim Aufbauen der ersten abstrakteren OS-Funktionen wie RAM-Verwaltung zu diskutieren,
statt erst bei Adressraumtrennung, Privilegien und IPC. Evtl. mit Verweis auf diese spaeteren Themen zur Begruendung.
- Was du da weiter anfuehrst, sind doch eher Spezialfaelle und Teilgebiete, des Themenbereichs Scheduling
(Echtzeit-Prozesse). Solche Ansaetze wie Prioritaetensteuerung oder verschiedene Ansaetze fairer Ressourcenverteilung
mit verschiedensten abgefahrenen queue-Konstrukten (da gibt es wirklich eine Menge weit komplizierteres
als einfache Prioritaetensteuerung) kann man da vielleicht in einem Ueberblick kurz anschneiden,
aber um den Rahmen nicht zu sprengen, waere mein Vorschlag, sich schliesslich einfach auf Round Robin zu beschraenken.
- DMA-Gefrickel ist ja nun voellig abgehoben. 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.
Timer:
- 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.
Keyboard:
- 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();
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 12:13:24 10.04.2009, insgesamt 1-mal bearbeitet
Interessanter Punkt. Ich kenne bisher nur dieses gvim und kann mich nur noch dunkel an den DOS editor (EDIT) erinnern. Arbeitet vi wie gvim?
vim ist ein aufgemotzter vi, also voll kompatibel, aber genau so besch... in der bedienung. btw, ein editor ist doch kein bestandteil eines OS, sondern nur eine gewöhnliche anwendnung. mach besser mit'm OS weiter und nicht noch 'ne baustelle auf.
ein editor ist doch kein bestandteil eines OS, sondern nur eine gewöhnliche anwendnung. mach besser mit'm OS weiter und nicht noch 'ne baustelle auf.
"Festina lente" sagt der Lateiner (Lieblingsspruch des Kaisers Augustus). Mir geht es aktuell darum, keyboard.c und video.c vernünftig zu stabilisieren. Diese beiden Module mit ihren Funktionen sind wichtig. Das wird dann auch das Ende von Teil 1 (Booten, RM, A20 Gate, RM -> PM, asm <-> C, GDT, IDT u. IRQ, video, timer und keyboard). Bei Teil 2 geht es mit dem wirklichen OS (Speicher, Multitasking, Prozesse, API, ...) weiter. Aber zuerst muss Teil 1 so abgerundet sein, dass man dort niemand verliert.
Ein echter Editor ist in der Tat ein User-Programm, gehört also nicht zwingend zum OS. Daher werde ich mich bezüglich der Bedienung an den DOS- und Linux-Konsolen orientieren.
Noch eine Detail-Frage: Über den PIT bin ich ja etwas weg gegangen, akzeptiere bisher die Standardeinstellung. Sollte man so etwas in der Art noch einbauen:
C/C++ Code:
1 2 3 4 5 6 7 8 9 10 11 12 13
1 2 3 4 5 6 7 8 9 10 11 12 13
void systemTimer_setFrequency( ULONG freq )
{
ULONG divisor = 1193180 / freq; //divisor must fit into 16 bits
Macht das mit 100 Hz Sinn? Sollte man hier eine Auswahl bieten oder eine Festeinstellung vornehmen? (wird für IRQ-gesteuertes Multitasking interessant)
Beim Keyboard verwende ich nun auch gespeicherte Cursors, falls man z.B. Infos in eine Statuszeile schreibt:
C/C++ Code:
1 2 3 4 5 6 7 8
1 2 3 4 5 6 7 8
void keyboard_handler(struct regs* r)
{
UCHAR KEY;
KEY = k_getch();
restore_cursor();
printformat("%c",KEY); // the ASCII character
save_cursor();
}
C/C++ Code:
1 2 3 4 5 6 7 8
void keyboard_handler(struct regs* r)
{
UCHAR KEY;
KEY = k_getch();
restore_cursor();
printformat("%c",KEY); // the ASCII character
save_cursor();
}
C/C++ Code:
1 2 3 4 5 6 7 8
void keyboard_handler(struct regs* r)
{
UCHAR KEY;
KEY = k_getch();
restore_cursor();
printformat("%c",KEY); // the ASCII character
save_cursor();
}
set_cursor(0,0);
printformat(" ************************************************\n");
printformat(" * *\n");
printformat(" * Welcome to PrettyOS 0.05 *\n");
printformat(" * *\n");
printformat(" * The C kernel has been loaded. *\n");
printformat(" * *\n");
printformat(" ************************************************\n");
settextcolor(4,1);
int y=10;
while(TRUE)
{
sleepSeconds(20);
if(y>24) y=24;
set_cursor(0,++y);
printformat("10 sec have passed");
};
return 0;
};
In Bochs klappt dies ganz gut mit den 100 Hz.
Allgemein:
Bezüglich des OS überdenke ich momentan die Reihenfolge. Wäre nun das "Paging" als nächster Schritt angesagt? Man kann diesem Mechanismus ja doch nicht wirklich entfliehen, und ich möchte wegen des interessanten inneren Aufbaus ein Multitasking-OS aufbauen.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 19:50:20 10.04.2009, insgesamt 3-mal bearbeitet
Wäre nun das "Paging" als nächster Schritt angesagt? Man kann diesem Mechanismus ja doch nicht wirklich entfliehen, und ich möchte wegen des interessanten inneren Aufbaus ein Multitasking-OS aufbauen.
naja, virtual memory und paging/swapping/was-auch-immer kannste auch später noch einbauen. multitasking geht auch, ohne mmu und isolation des speichers der prozesse voneinander. nachträglich lässt sich virtual memory transparent ins system integrieren, ohne dass grossartige umbauten nötig sind. wenn dich das thema interessiert, kannste natürlich sofort damit loslegen. aber irgendwie werd' ich das gefühl nicht los, dass du dir sowas wie 'nen projektplan machen solltest.
@+fricky: Du siehst das IMHO sehr schematisch. "Projekte" sind zumeist langweilig, ökonomisch getrieben und haben einen engen Zielkorridor. So etwas interessiert mich nur beruflich.
Experimente schaffen dagegen wirklich Neues. Ich habe weitgehend klare Vorstellungen von der prinzipiellen Vorgehensweise. Obwohl es keine klare Theorie über den optimalen Weg gibt, werden vielfach bestehende OS als Ziel nachgeahmt. Als Ziel schwebt mir ein lernfähiges und bezüglich der Anforderungen adaptives OS vor, bin aber nicht sicher, ob dies im ersten Ansatz erreichbar ist.
Zur Zeit geht es erst einmal darum, die Basics stabil und dennoch flexibel zu arrangieren, um zum Experimentieren einzuladen. Das Ganze kann sich auch gabeln, wenn es darum geht interessante Pfade zu verfolgen. Bisher denke ich, habe ich hoffentlich noch keinen neuen wirklich verfolgenswerten Ansatz übersehen.
Einen Fehler muss ich allerdings gestehen, nämlich das Tutorial in deutsch aufgesetzt zu haben. Damit halte ich einige interessierte Mitstreiter aus aller Welt auf Distanz. Vielleicht kann ich noch kapitelweise englische Summaries einflechten, die das Nachvollziehen/Mitdenken erlauben.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 23:31:42 10.04.2009, insgesamt 1-mal bearbeitet
Einen Fehler muss ich allerdings gestehen, nämlich das Tutorial in deutsch aufgesetzt zu haben. Damit halte ich einige interessierte Mitstreiter aus aller Welt auf Distanz. Vielleicht kann ich noch kapitelweise englische Summaries einflechten, die das Nachvollziehen/Mitdenken erlauben.
wenn du anklingen läßt, daß du dich freuen würdest, wenn das jemand übersetzen würde, dann findet sich vermutlich auch jemand, der sich freut, das offiziell zu übersetzen, natürlich mit namensnennung und link auf die hp des edlen helfers.
wenn du anklingen läßt, daß du dich freuen würdest, wenn das jemand übersetzen würde, dann findet sich vermutlich auch jemand, der sich freut, das offiziell zu übersetzen, natürlich mit namensnennung und link auf die hp des edlen helfers.
Nein, daran habe ich wirklich nicht gedacht, da das Tutorial noch zu sehr im Fluss ist. Vorne den Abschnitt im Real Mode könnte man z.B. noch mit interessanten Befehlen/Funktionen aufbohren. Ich finde z.B. dein C++-Tutorial als didaktisches Vorbild super. Solche Fragestellungen mit Lösungen sind wirklich schön, weil man dadurch die Leser zum Experimentieren und selbst denken anregen kann. Es geht mir darum, auf praktische Weise zu zeigen, welche "Bausteine" beim OS-Bau zur Verfügung stehen, wie diese funktionieren und was man damit praktisch machen kann.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zunächst muss ich die Funktionen innerhalb video.c an allgemein übliche Vorgehensweisen anpassen. Mir ist z.B. aufgefallen, dass das "Backspace" noch nicht "löscht", das scheint aber üblich zu sein. http://de.wikipedia.org/wiki/Backspace
"Rücklöschtaste". Das und einiges andere muss ich zunächst anpassen, bevor es weiter geht. So, das sieht schon besser aus, sogar rekursiv :
Bei P(0,0) macht es gar nichts mehr, ansonsten geht es eins zurück, löscht (druckt ' ') und geht wieder eins zurück. Am Zeilenanfang muss ein Rücksprung ans Zeilenende eins höher erfolgen.
void keyboard_handler(struct regs* r)
{
UCHAR KEY;
KEY = k_getch();
restore_cursor();
switch(KEY)
{
case KINS:
break;
case KDEL:
move_cursor_right();
putch('\b');//BACKSPACE break;
case KHOME:
move_cursor_home();
break;
case KEND:
move_cursor_end();
break;
case KPGUP:
break;
case KPGDN:
break;
case KLEFT:
move_cursor_left();
break;
case KUP:
break;
case KDOWN:
break;
case KRIGHT:
move_cursor_right();
break;
default:
printformat("%c",KEY); // the ASCII character break;
}
save_cursor();
}
void keyboard_handler(struct regs* r)
{
UCHAR KEY;
KEY = k_getch();
restore_cursor();
switch(KEY)
{
case KINS:
break;
case KDEL:
move_cursor_right();
putch('\b');//BACKSPACE break;
case KHOME:
move_cursor_home();
break;
case KEND:
move_cursor_end();
break;
case KPGUP:
break;
case KPGDN:
break;
case KLEFT:
move_cursor_left();
break;
case KUP:
break;
case KDOWN:
break;
case KRIGHT:
move_cursor_right();
break;
default:
printformat("%c",KEY); // the ASCII character break;
}
save_cursor();
}
void keyboard_handler(struct regs* r)
{
UCHAR KEY;
KEY = k_getch();
restore_cursor();
switch(KEY)
{
case KINS:
break;
case KDEL:
move_cursor_right();
putch('\b');//BACKSPACE break;
case KHOME:
move_cursor_home();
break;
case KEND:
move_cursor_end();
break;
case KPGUP:
break;
case KPGDN:
break;
case KLEFT:
move_cursor_left();
break;
case KUP:
break;
case KDOWN:
break;
case KRIGHT:
move_cursor_right();
break;
default:
printformat("%c",KEY); // the ASCII character break;
}
save_cursor();
}
In video.c musste auch noch einiges hinzu gefügt werden.
Wenn jemand hier bessere Ideen hat, bitte posten.
Ich bin mir z.B. noch nicht sicher, ob man bei der Instruktionseingabe überhaupt einen Wechsel der Zeile - und damit mehrzeilige Eingaben - zulassen sollte.
Bei END springe ich an das Ende der Zeile und nicht an das Ende des Eingetippten. Bei DEL lösche ich auch nur ein Zeichen und schiebe nicht den Rest der Zeile eins nach links.
Vielleicht müsste man zwischen zwei ENTER einen virtuellen Befehlsstring aufbauen, der von HOME bis END geht. Zumindest sieht es bei der DOS-Box (Konsole) so aus. Da bin ich noch flexibel.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 01:14:02 11.04.2009, insgesamt 3-mal bearbeitet
Ist auch einfach die Frage, wie komfortabel Du das überhaupt gestalten möchtest.
Ein grundlegender Kommandointerpreter könnte ja einen eigenen Editor mitbringen, sodaß etwas Rudimentäres vollkommen ausreicht. Auf der anderen Seite kannst Du's im Kernel aber bis zum kompletten Memoryhexeditor treiben
IMHO haben konkrete Reaktionen auf Tasteneingaben im IRQ-Handler (HW-Treiber) nichts zu suchen. Das sind Teile einer Anwendung. Ist vielleicht so zu Demo-Zwecken ganz nett, aber mit brauchbarem OS-Design hat das nichts zu tun.
Irgendwie macht das so immer noch den Eindruck, als wuerdest du die Sache ein wenig wie dieses erste Polling-Relikt behandeln.
Also entweder ueberlegst du dir ein vernuenftiges Treiber-Design und machst bei keyboard und video dann mal langsam die Kiste zu (das bedeutet auch, dass du kapselnde Funktionen brauchst, falls du mal mit dem Entwurf einer echten API anfaengst), dann kannst du auf diesen abgeschlossenen Treibern auch etwas rumbasteln, das ueber den Rest des Tutorials im Prinzip so funktionieren sollte, oder du beschraenkst dich hier nur auf das absolut notwendigste. Solche aufwendigen, dennoch halbgaren und bestenfalls bis zum Ende dieses Kapitels brauchbaren Basteleien sind IMHO kontraproduktiv.
IMHO haben konkrete Reaktionen auf Tasteneingaben im IRQ-Handler (HW-Treiber) nichts zu suchen. Das sind Teile einer Anwendung. Ist vielleicht so zu Demo-Zwecken ganz nett, aber mit brauchbarem OS-Design hat das nichts zu tun.
Ja, das ist völlig richtig. Ich werde jetzt bei video.c und keyboard.c aufhören, als kleine Demo reichen diese Funktionen bereits. "Externe Anwendungen" sind noch nicht lauffähig. Was ich auf der Stufe noch einfügen möchte, ist ein Kommandozeileninterpreter mit nachgeschaltetem strcmp(...) und entsprechenden Reaktionen so wie beim Real Mode, am besten in main(), diesmal nur in C programmiert und ohne BIOS. Diese Parallele erwartet man irgendwie.
Dann geht's im zweiten Teil thematisch mit Paging, Heap, Designfragen weiter. Das wird dann aber alles elend komplex. Da knabbere ich noch dran. Zur Zeit beschimpft mich sogar schon mein Linker, weil ich Paging und Heap modular nicht sauber auseinander halte. Übel, übel, ....
EDIT: zumindest das endlich geschafft.
@Nobuo T: Wenn ich Dich richtig verstanden habe, würdest Du im Keyboard Handler die Information in eine Queue stecken. Der Kommandozeilen-Interpreter (wo gehört der als "Anwendung" hin? Für mich ist dies noch Teil des Kernels.) müsste sich diese dann dort abholen und verarbeiten, so wie jetzt - zur Demo - bereits im Keyboard Handler erfolgt, richtig? Der Kommandozeilen-Interpreter muss ein Prompt drucken, nach ENTER die Zeile (ohne Prompt) schlucken und verarbeiten mittels strcmp und switch/case-Struktur (wie bei einer Windows-Nachrichten-Pumpe).
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 17:25:00 11.04.2009, insgesamt 8-mal bearbeitet
Der Keyboardhandler sollte die Eingaben nicht in eine eigene Queue fließen lassen, sondern gleich an die Queue der aktiven Anwendung.
Deshalb würde ich als nächstes damit beginnen Grundstrukturen für Anwendungen zu definieren. Da gehören dann auch die Schnittstellen zu Treibern dazu.
Wenn man die Grundstrukturen hat, dann am besten gleich Multitasking/threading und nen einfach Scheduler einbauen, dann macht es einfach mehr Spaß.
Man kann auch erst mal die Schnittstellen zu den Treibern weglassen und einfach ein paar Anwendungen laufen lassen. Die könnten dann ja alle gleichzeitig in den Bildschirm malen.
Wenn man erst mal zwischen Anwendungen und Kern/System getrennt hat, dann ist es einfacher die Schnittstellen zu entdecken und zu bauen. Das diese dann gleich so angenehm wie möglich sind ist natürlich noch eine ganz andere Frage.
Der Keyboardhandler sollte die Eingaben nicht in eine eigene Queue fließen lassen, sondern gleich an die Queue der aktiven Anwendung.
Ja, das klingt sinnvoll, damit es keine Verwechslungsprobleme beim Leeren eines gemeinsamen Briefkastens gibt. Das Problem ist, dass wir noch keine Anwendungen laufen lassen können, weil die Strukturen hierzu noch fehlen. Würdest Du den Kommandozeileninterpreter bereits als Anwendung in Privilegstufe 3 laufen lassen?
Zitat:
Am besten gleich Multitasking/Threading und 'nen einfachen Scheduler einbauen, dann macht es einfach mehr Spaß.
Spaß klingt gut.
Zitat:
Man kann auch erst mal die Schnittstellen zu den Treibern weglassen und einfach ein paar Anwendungen laufen lassen. Die könnten dann ja alle gleichzeitig in den Bildschirm malen.
Das mache ich momentan mit dem Timer und dem "Editor" Keyboard Handler ja auch, deshalb musste ich schon den Cursor speichern.
Ich experimentiere gerade mit einem Paging-/Heap-Modul, ist kompliziert (schlecht für Didaktik), miteinander vernetzt und klappt leider noch nicht, wie ich es will. Das wollte ich als nächsten Schritt einfügen, noch bevor die Anwendungen im Multitasking laufen. Das gehört aber alles nach Teil 2, der leider viel Zeit braucht (liegt an meinen beschränkten Fähigkeiten und Erfahrungen in diesem Bereich). Macht aber nichts, fahre ja kein Wettrennen.
Teil 1 wollte ich mit einem kleinen Befehlsinterpreter im Kernel (Analogie zur Vorgehensweise im RM) beenden, damit die eingetippten Zeichen Sinn machen. Kann man aber in Teil 2 wohl alles wegwerfen, da hat Nobuo T leider Recht.
Sehe ich aber nicht als großes Problem.
EDIT: Vielleicht kann man die Idee mit dem BIOS-Briefkasten und dem Abholen der Post dort durch einen Befehlsinterpreter kombinieren.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 18:59:26 11.04.2009, insgesamt 2-mal bearbeitet
Klar waere es ein praktisches Konzept, eine KB-queue pro Anwendung einzurichten. Da das OS in diesem Stadium aber noch nicht mal ein Konzept fuer Anwendungen hat, ist das alles noch Zukunftsmusik und erst im naechsten Kapitel ausfuehrlich zu diskutieren.
Eine globale queue zum Ablegen der KB-Codes (so macht es das BIOS auch) waere im Moment sicher am praktischsten. Diese liesse sich spaeter auch relativ einfach entsprechend den Beduerfnissen des Multitaskings anpassen.
Die ISR liest also die Scancodes vom KB, setzt die Flags, wandelt in ASCII-Codes um, o.Ae. und eine getch-Funktion (vgl. int 16h, ah=0) liest die queue dann im Rahmen der Anwendung (hier einfach des "Kerns") wieder aus.
Ja, ich würde die Anwendung erst mal im Kern laufen lassen. Erst mal alle Anwendungen.
Die dürfen natürlich von Anfang an nur über entsprechende Methodenaufrufe auf Systemfunktionen zugreifen, da kann man dann den Wechsel von Rang 0 auf 3 oder wie herum auch immer Transparent einbauen.
Aber erst mal muss was laufen. Dann hat man auch leichter die Möglichkeit Aufgaben abzugeben und jemand anderes kann dir Testanwendungen zusammenbauen oder an Treiberfunktionen schrauben.
Ich würde (habe) es (mal mehr mal weniger) schön mit C++, nicht mit C machen (gemacht).
Ich würde (habe) es (mal mehr mal weniger) schön mit C++, nicht mit C machen (gemacht).
Im Kernel bleiben wir bei C.
@Nobuo T: welche Flags meinst Du genau (nehmen wir die BDA als Vorbild) setzen? Diesen Bereich bei 40:17? Gibt es bereits eine vereinfachte (oder auch die komplette) BDA Struktur als C-Struct, die man einsetzen könnte oder muss man das selbst aufbauen? Da könnte man ja auch die statische Queue gleich ablaufen lassen.
Im BDA wird offenbar 40:1E - 40:3D als "circular queue buffer" (32 Byte) verwendet.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 19:17:01 11.04.2009, insgesamt 1-mal bearbeitet
welche Flags meinst Du genau (nehmen wir die BDA als Vorbild) setzen? Diesen Bereich bei 40:17?
Jup, genau die.
Erhard Henkes schrieb:
Gibt es bereits eine vereinfachte (oder auch die komplette) BDA Struktur als C-Struct, die man einsetzen könnte oder muss man das selbst aufbauen? Da könnte man ja auch die statische Queue gleich ablaufen lassen.
Im BDA wird offenbar 40:1E - 40:3D als "circular queue buffer" (32 Byte) verwendet.
Keine Ahnung, ob es da bereits etwas fertiges gibt. Da was eigenes zu implementieren sollte aber wohl schneller erledigt sein, als nun erst was zu suchen.
Musst ja erstmal auch nicht alles implementieren, und auch nicht genau wie in der BDA. Nicht vergessen: Das ist nur ein moegliches Konzept, das in der Form wahrscheinlich auch nicht gerade ideal fuer Multitasking geeignet ist.
... ...
Evtl. bietet es sich als naechstes dann doch schon mal an, vorausschauend grob etwas ueber das Treiber-Design nachzudenken. zB. erstmal:
Was hast du an Input von der HW? Was davon willst du welchen Anwendungen in welchem Fall mitteilen? Welche Datenstruktur koennte sich dazu eignen? Was fuer Interface-Funktionen koennte man darauf benutzen?
jenz schrieb:
Schade, kann ich die Begründung weiter oben im Thread lesen?
Wenn nicht, wieso?
Ja, das wurde bereits diskutiert und einvernehmlich befunden, dass die Nachteile beim Einsatz von C++ die Vorteile bisher ueberwiegen.
IMHO haben konkrete Reaktionen auf Tasteneingaben im IRQ-Handler (HW-Treiber) nichts zu suchen. Das sind Teile einer Anwendung. Ist vielleicht so zu Demo-Zwecken ganz nett, aber mit brauchbarem OS-Design hat das nichts zu tun.
Irgendwie macht das so immer noch den Eindruck, als wuerdest du die Sache ein wenig wie dieses erste Polling-Relikt behandeln.
Also entweder ueberlegst du dir ein vernuenftiges Treiber-Design und machst bei keyboard und video dann mal langsam die Kiste zu (das bedeutet auch, dass du kapselnde Funktionen brauchst, falls du mal mit dem Entwurf einer echten API anfaengst), dann kannst du auf diesen abgeschlossenen Treibern auch etwas rumbasteln, das ueber den Rest des Tutorials im Prinzip so funktionieren sollte, oder du beschraenkst dich hier nur auf das absolut notwendigste. Solche aufwendigen, dennoch halbgaren und bestenfalls bis zum Ende dieses Kapitels brauchbaren Basteleien sind IMHO kontraproduktiv.
alle daumen hoch. dem muss man nichts mehr hinzufügen.
jenz schrieb:
Der Keyboardhandler sollte die Eingaben nicht in eine eigene Queue fließen lassen, sondern gleich an die Queue der aktiven Anwendung.
würde ich nicht so machen. die queue gehört in den keyboard-treiber bzw. in den kernel und anwendungen können sich was daraus abholen. ebenso würde ich mit maus-events verfahren. auch würde ich mir die möglichkeit offen lassen, mehrere mäuse und mehrere keyboards anschliessen zu können, also von vorn herein modulare, objektorientierte strukturen zu schaffen, finde ich ziemlich wichtig.
Erhard Henkes schrieb:
Der Kommandozeilen-Interpreter (wo gehört der als "Anwendung" hin? Für mich ist dies noch Teil des Kernels.)
nee, ein komandozeilen-interpreter ist bestenfalls eine sogenannte 'shell', also eine art bindeglied zwischen benutzer und bestimmten system-services. der gehört genau so wenig in den kernel, wie ein editor oder eine tabellenkalkulation.
jenz schrieb:
Schade, kann ich die Begründung weiter oben im Thread lesen?
Wenn nicht, wieso?
C++ ist unglaublich mächtig, aber auch komplex und 'esoterisch'. man könnte natürlich c++ verwenden, aber dann bräuchte man knallharte coding-richtlinien und leute, die diszipliniert und perfekt im umgang mit dieser sprache sind, sonst wird das nix. C ist im vergleich dazu primitiv und einfach strukturiert. das ist aber der entscheidende vorteil: weniger ist mehr. z.b. sind fehler in einem C-programm, im vergleich zu C++, trivial, leicht zu finden/debuggen und haben weniger weitreichende folgen.
Mir würde C++ leichter fallen als C, aber ich weiß von den Robotik-Anwendungen, dass man nur gewisse Features sinnvoll nutzen kann, sozusagen gekapseltes C mit Klassen ist der Idealstil, aber das schafft man auch anders. Daher trage ich die Entscheidung für C eindeutig mit. Wenn man vernünftig damit umgeht, ist es ein herrliches Bindeglied zwischen Assembler und der Hochsprachen-Welt, im OS-Bereich sicher die richtige Wahl.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 00:17:35 12.04.2009, insgesamt 1-mal bearbeitet
stimmt, objektorientiert kann man auch mit c entwickeln. ist aber aus meiner sicht eben fehleranfälliger und auch aufwendiger beim schreiben. aber okay, entscheidung ist gefallen.
warum sollen sich die anwendungen denn die ereignisse selbst abholen? man kann die doch besser von anfang gleich eventgesteuert implementieren.
pollen ist doch nicht wirklich schön
warum sollen sich die anwendungen denn die ereignisse selbst abholen?
weil das z.b. für sequenziell ablaufende programme (ein stinknormales c-programm z.b. das mit 'main' beginnt) einfacher ist und es bedeutet auch weniger verwaltungsaufwand im kernel (der schiebt einfach daten in die dazugehörigen queues und kümmert sich nicht weiter drum). falls du auf sowas wie eventgesteuerte gui-anwendnungen anspielst, die sind ja clients eines 'window-managers', der prinzipiell selber bloss eine anwendung ist. er würde dann z.b. beim eintreffen eines zeichens die window-prozedur des entsprechenden fensterchens aufrufen und damit hätte die gui-anwendung ihre events.
jenz schrieb:
man kann die doch besser von anfang gleich eventgesteuert implementieren.
pollen ist doch nicht wirklich schön.
mit dem 'selbstabholen' hat man doch beide möglichkeiten. beispiele mit einem non-blocking getchar()...
event-gesteuert:
Code:
int c;
wait_for_event (KEYBOARD_EVENT); // die task schläft, bis mindestens 1 zeichen in der queue ist
c = getchar(); // zeichen abholen
Code:
int c;
wait_for_event (KEYBOARD_EVENT); // die task schläft, bis mindestens 1 zeichen in der queue ist
c = getchar(); // zeichen abholen
Code:
int c;
wait_for_event (KEYBOARD_EVENT); // die task schläft, bis mindestens 1 zeichen in der queue ist
c = getchar(); // zeichen abholen
polling:
Code:
1 2 3 4 5 6 7 8 9 10
1 2 3 4 5 6 7 8 9 10
int c;
c = getchar(); // zeichen abholen, -1 bei fehlschlag
if (c > 0)
{
// ein zeichen ist da
}
else
{
// war leider nix
}
Code:
1 2 3 4 5 6 7 8 9 10
int c;
c = getchar(); // zeichen abholen, -1 bei fehlschlag
if (c > 0)
{
// ein zeichen ist da
}
else
{
// war leider nix
}
Code:
1 2 3 4 5 6 7 8 9 10
int c;
c = getchar(); // zeichen abholen, -1 bei fehlschlag
if (c > 0)
{
// ein zeichen ist da
}
else
{
// war leider nix
}
Hat es angesichts der Diskussion über das weitere Vorgehen noch einen Sinn, den jeweils aktuellen Quellcode mal unter die Lupe zu nehmen?
Da gäbe es zwei Aspekte, wo ich Unterstützung/Zustimmung brauchen könnte:
1) Ich habe mal versuchsweise einen Paging/Heap-Mechanismus eingebaut, der aber sehr kompliziert ist, also für den ersten Schritt noch vereinfacht werden sollte, und leider noch nicht funktioniert, habe Testroutinen eingebaut, um zu sehen, wo der Code ausflippt: z.Z. beim Umschalten auf das Paging (wahrscheinlich sind Speicheradressen falsch vorgegeben), echte Tüftelei.
Da könnte sich jemand auf die Dateien ordered_array.h/c, kheap.h/c und paging.h/c konzentrieren und diese aus didaktischen Gründen vereinfachen und in PrettyOS zum Laufen bringen (da beiße ich gerade dran rum und komme nicht wirklich weiter). Ich habe diesen Code mal hochgeschickt, in der Hoffnung, dass mir jemand einen guten Tipp gibt:
http://www.henkessoft.de/OS_Dev/Downloads/20_paging_heap_problems.zip
2) s.u. (Circular Queue: füllen, leeren)
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 13:18:27 12.04.2009, insgesamt 1-mal bearbeitet
ah, schon gefunden: http://www.jamesmolloy.co.uk/tutorial_html/7.-The%20Heap.html
ist die frage, ob da bugs drin sind, oder ob du nachträglich was fehlerhaftes reinbastelt hast. ich würde vorschlagen, du machst sowas wie 'nen unit-test, also ein kleines testprogramm für den heap, das (unter windoofs, mit msvc z.b.) ohne dein OS läuft. dann kannste auch besser debuggen.
@+fricky: Der ganze Code gefällt mir nicht, da er schwierig zu verstehen und vor allem bezüglich paging und heap nicht voneinander unabhängig ist, aber ich habe bisher noch kein besseres Beispiel gefunden, wollte die Mechanismen auf die Schnelle ausprobieren und analysieren, was man für einen ersten Einstieg wirklich benötigt. James Molloy ist einer der Moderatoren und führenden Köpfe bei osdev.net. Sein Code basiert teilweise auf älteren Arbeiten im Rahmen von Bran's Tutorial.
Die Idee mit dem unit test ist brauchbar.
Wenn man sti() vor der Paging-Initialisierung anschaltet, kracht es noch früher, für mich momentan nicht nachvollziehbar.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 14:15:10 12.04.2009, insgesamt 3-mal bearbeitet
zu 2) Ich mache mir momentan Gedanken im Verständnis des Queue-Mechanismus:
Wie bewegt man die beiden Zeiger Head and Tail am sinnvollsten? Der buffer verändert ja seine inhaltsbedeutende Größe. Tail ist der Eingang und wird vom Keyboard Handler versorgt. Der wandert bei jedem Key Entry Event eins nach "links", so dass die Zeichen in einer Kette angeordnet werden. Nun aber zum Head: Wenn die KeyQueue (KQ) leer ist, sind doch Head und Tail deckungsgleich und der Key an dieser Stelle wird auf ??? gesetzt. Man kann ja prüfen ob Head und Tail verschieden sind, wenn nein ist die KQ leer (Head minus Tail == 0), wenn ja, stehen Head minus Tail Zeichen drinnen. Sobald Zeichen rein kommen, wandern Head und Tail auseinander, Tail nach links, Head bleibt stehen. Sobald eine "Anwendung" ein Zeichen abholt, wandert Head ebenfalls eins nach links. Habe so etwas noch nie gebaut, daher die Frage, ob das so stimmt. man muss ja noch den Circular-Mechanismus einbauen: Bei Zeiger < KEYQUEUE springt man auf KEYQUEUE+KQSIZE-1, richtig?
Nun zu den "Zeichen" in der KQ: Ein Zeichen besteht ja aus dem ASCII-Zeichen plus Flags (Sonderzeichen), d.h. pro Zeichen müsste man eigentlich mindestens zwei Byte ablegen, eins enthält das ASCII-Zeichen (da könnte man Shift auch gleich verarbeiten, also im Flag weglassen) und eins die Zusatz-Info, z.B. (Shift links/rechts), ALT/ALT Gr, CTRL links/rechts, usw. bzw. Kombinationen davon. (Bisher analysiere ich im Handler ja nur die Shift-Taste, rechts/links noch gleich).
Ich hoffe, ihr versteht, was ich da schreibe.
Ich verstehe momentan nicht wie BIOS/BDA das machen, weil dort ja die Sondertasten-Bits stehen. Zu jedem Tastenanschlag gehört ja je nach design entweder nur eine Taste (shift/non-shift) oder eine Kombination aus Sondertasten und Taste (vielleicht zu kompliziert). Momentan kommen wir an ALT Gr aber nicht ran.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 14:49:27 12.04.2009, insgesamt 2-mal bearbeitet
Der ganze Code gefällt mir nicht, da er schwierig zu verstehen und vor allem bezüglich paging und heap nicht voneinander unabhängig ist...
naja, memory-management ist nie 'ne einfache sache. am anfang liefert der kernel ein paar aufeinanderfolgende speicherseiten und der heap-manager ist dazu da, diese relativ grossen speicherbereich in kleinere häppchen zu verhackstücken. sind alle seiten aufgebraucht, dann müssen vom kernel neue seiten angefordert werden, die hinten angehängt werden. trennen kannste das insofern, dass der heap-manager (testweise) mit einem fixen block arbeitet. also,
baustelle-1, kernel: seitenverwaltung, paging, virtual memory.
baustelle-2, heap-manager: einen beliebig grossen, zusammenhängenden speicherbereich mit malloc/realloc/free zu zerstückeln.
baustelle-3: zusammenspiel beider subsysteme.
wichtig ist z.b. auch, dass es mehrere instanzen des heap-managers geben kann, so dass jeder prozess seinen eigenen heap bekommt. auch im kernel sollte mindestens eine instanz des heap-managers da sein, damit kernel-komponenten nicht für jeden kleinkram eine ganze speicherseite verbraten müssen.
Der "Reset" des OS erfolgt bei noch ausgeschaltetem Interrupt hier:
C/C++ Code:
1 2 3 4 5 6 7 8 9 10 11 12
1 2 3 4 5 6 7 8 9 10 11 12
//TEST
printformat("switch_page_directory before paging switch \n");
for (counter=0; counter<50000000; ++counter);
//TEST
cr0 |= 0x80000000; // Enable paging by setting the PG bit in the CR0 register //Absturz!!! asm volatile("mov %0, %%cr0":: "r"(cr0));
//TEST
printformat("switch_page_directory after paging switch \n");
for (counter=0; counter<50000000; ++counter);
//TEST
C/C++ Code:
1 2 3 4 5 6 7 8 9 10 11 12
//TEST
printformat("switch_page_directory before paging switch \n");
for (counter=0; counter<50000000; ++counter);
//TEST
cr0 |= 0x80000000; // Enable paging by setting the PG bit in the CR0 register //Absturz!!! asm volatile("mov %0, %%cr0":: "r"(cr0));
//TEST
printformat("switch_page_directory after paging switch \n");
for (counter=0; counter<50000000; ++counter);
//TEST
C/C++ Code:
1 2 3 4 5 6 7 8 9 10 11 12
//TEST
printformat("switch_page_directory before paging switch \n");
for (counter=0; counter<50000000; ++counter);
//TEST
cr0 |= 0x80000000; // Enable paging by setting the PG bit in the CR0 register //Absturz!!! asm volatile("mov %0, %%cr0":: "r"(cr0));
//TEST
printformat("switch_page_directory after paging switch \n");
for (counter=0; counter<50000000; ++counter);
//TEST
"switch_page_directory before paging switch " wird ausgegeben.
Dann wird der Cursor eins nach unten geschoben???
Dann erfolgt der Neustart (wohl triple fault).
Schaltet man sti() vorher ein, so kracht es sofort. Man sieht keine einzige Meldung:
//TEST
printformat("kernel_directory->physicalAddr: %x \n", kernel_directory->physicalAddr);
for (counter=0; counter<50000000; ++counter);
//TEST int i = 0;
for (i=KHEAP_START; i<KHEAP_START+KHEAP_INITIAL_SIZE; i+=PAGESIZE)
get_page(i, 1, kernel_directory);
//TEST
printformat("get_page \n");
for (counter=0; counter<50000000; ++counter);
//TEST
i = 0;
while (i < XYZ_ADDRESS ) //placement_address+0x1000)
{
alloc_frame( get_page(i, 1, kernel_directory), 0, 0);
i += PAGESIZE;
}
for (i = KHEAP_START; i < KHEAP_START+KHEAP_INITIAL_SIZE; i+=PAGESIZE)
alloc_frame( get_page(i, 1, kernel_directory), 0, 0);
//TEST
printformat("alloc_frame part 1 and 2 \n");
for (counter=0; counter<50000000; ++counter);
//TEST
// register_interrupt_handler(14, page_fault); // TODO???
switch_page_directory(kernel_directory);//Hier liegt der Absturz begraben!
//TEST
printformat("kernel_directory->physicalAddr: %x \n", kernel_directory->physicalAddr);
for (counter=0; counter<50000000; ++counter);
//TEST int i = 0;
for (i=KHEAP_START; i<KHEAP_START+KHEAP_INITIAL_SIZE; i+=PAGESIZE)
get_page(i, 1, kernel_directory);
//TEST
printformat("get_page \n");
for (counter=0; counter<50000000; ++counter);
//TEST
i = 0;
while (i < XYZ_ADDRESS ) //placement_address+0x1000)
{
alloc_frame( get_page(i, 1, kernel_directory), 0, 0);
i += PAGESIZE;
}
for (i = KHEAP_START; i < KHEAP_START+KHEAP_INITIAL_SIZE; i+=PAGESIZE)
alloc_frame( get_page(i, 1, kernel_directory), 0, 0);
//TEST
printformat("alloc_frame part 1 and 2 \n");
for (counter=0; counter<50000000; ++counter);
//TEST
// register_interrupt_handler(14, page_fault); // TODO???
switch_page_directory(kernel_directory);//Hier liegt der Absturz begraben!
//TEST
printformat("kernel_directory->physicalAddr: %x \n", kernel_directory->physicalAddr);
for (counter=0; counter<50000000; ++counter);
//TEST int i = 0;
for (i=KHEAP_START; i<KHEAP_START+KHEAP_INITIAL_SIZE; i+=PAGESIZE)
get_page(i, 1, kernel_directory);
//TEST
printformat("get_page \n");
for (counter=0; counter<50000000; ++counter);
//TEST
i = 0;
while (i < XYZ_ADDRESS ) //placement_address+0x1000)
{
alloc_frame( get_page(i, 1, kernel_directory), 0, 0);
i += PAGESIZE;
}
for (i = KHEAP_START; i < KHEAP_START+KHEAP_INITIAL_SIZE; i+=PAGESIZE)
alloc_frame( get_page(i, 1, kernel_directory), 0, 0);
//TEST
printformat("alloc_frame part 1 and 2 \n");
for (counter=0; counter<50000000; ++counter);
//TEST
// register_interrupt_handler(14, page_fault); // TODO???
switch_page_directory(kernel_directory);//Hier liegt der Absturz begraben!
Danke für den Hinweis. Kontrolle der "Read/Write Counts" gefällt mir am besten, weil der Zirkular-Mechanismus nicht gestört wird.
Zitat:
(write_count - read_count) always yields the number of items placed in the buffer and not yet retrieved.
Das macht Sinn, die beiden Variablen kommen in meine allgemeine ODA(OS Data Area)-Struktur dazu. Dann hat man auch eine einfache Kontrolle über Schreiben/Lesen/Buffer Full bzw. Overrun in der Queue.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 15:49:19 12.04.2009, insgesamt 1-mal bearbeitet
auch "stinknormale" c-anwendungen lassen sich bauen, wenn man die anwendungen ereignisgesteuert macht.
wie die tastatureingabe beim "keyget" ankommt hat damit überhaupt nichts zu tun.
in jeder anwendung aber dieses polling zu haben ist, meiner meinung nach, einfach nicht sinnvoll.
Eine Speicherverwaltung zu bauen ist aus meiner Sicht noch viel zu früh.
Eine Speicherverwaltung zu bauen ist aus meiner Sicht noch viel zu früh.
Lieber einen Scheduler und mehrere Anwendungen.
Noch liegt nichts fest, bin nur am Austesten. Ich habe mich bei der Reihenfolge des Aufbaus von folgender Aussage James Molloy's inspirieren lassen:
Zitat:
Dynamic memory allocation is one of the few things that it is very difficult to do without. Without it, you would have to specify an absolute maximum number of processes running (static array of pids), you would have to statically give the size of every buffer - Generally making your OS lacklustre and woefully inefficient.
"glanzlos und beklagenswert ineffizient", das hört sich nicht gerade anziehend an.
Daher schlage ich mich im Hintergrund mit dem Pagigng/Heap-Mechanismus von James Molley herum. Das muss auch alles noch simplifiziert werden, derart unverständlicher Code kommt mir in kein Tutorial.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Das ganze ist einfach ein riesiger Wirrwarr, macht so als Tutorial keinen Sinn, weil niemand irgendetwas nachverfolgen und damit experimentieren kann. Ich stehe selbst wie vor einem Berg und weiß noch nicht, wie ich den Tunnel bohren soll.
Wenn ich so auf kmalloc(...) schaue, frage ich mich gerade, wo eigentlich der heap erzeugt wird, denn vorher ist kheap = 0.
probiert, also mal 65536 (10000h) als Ziel. Das geht ohne Reset.
Also sollte es doch an diesem 44880 (AF50h) liegen. Liege ich da mit meinen Gedanken richtig?
Versuche gerade nachzuvollziehen, warum bei der Paging-Install-Routine gerade 44880 heraus kommt.
Diese Bitset-Geschichte (vielleicht kann man das einfacher machen) läuft wie folgt:
C/C++ Code:
INDEX_FROM_BIT(nframes)
// Macros used in the bitset algorithms.
#define INDEX_FROM_BIT(a) (a/(8*4))
#define OFFSET_FROM_BIT(a) (a%(8*4))
C/C++ Code:
INDEX_FROM_BIT(nframes)
// Macros used in the bitset algorithms.
#define INDEX_FROM_BIT(a) (a/(8*4))
#define OFFSET_FROM_BIT(a) (a%(8*4))
C/C++ Code:
INDEX_FROM_BIT(nframes)
// Macros used in the bitset algorithms.
#define INDEX_FROM_BIT(a) (a/(8*4))
#define OFFSET_FROM_BIT(a) (a%(8*4))
Also INDEX_FROM_BIT(16384) ==> (16384/(8*4)) = 512
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 21:34:38 12.04.2009, insgesamt 1-mal bearbeitet
@+fricky:
auch "stinknormale" c-anwendungen lassen sich bauen, wenn man die anwendungen ereignisgesteuert macht.
wie die tastatureingabe beim "keyget" ankommt hat damit überhaupt nichts zu tun.
also willst du, dass jede anwendung event-handler (für key events) implementieren muss, oder wie meinst du das?
jenz schrieb:
in jeder anwendung aber dieses polling zu haben ist, meiner meinung nach, einfach nicht sinnvoll.
polling in dem sinn, dass die cpu, wegen vieler missglückter abfragen, zyklen verbrät, kann ja schon durch eine blocking 'getchar' umgangen werden (oder durch mein beispiel von weiter oben).
Diese Message Geschichten sind eher typisch für Mini Kernels wie das bedeutungslose (?) aber "elegante" Minix von Tanenbaum. Wie gesagt, ich bin diesbezüglich noch völlig locker.
Momentan machen mich einfach die C-Basics fertig. Als C++ler bin ich ich echt nicht gewohnt, so "tief unten" zu wühlen, aber ich finde gerade diese herausforderung, alles selbst aufbauen zu müssen, total interessant. Ist alles nur eine Frage der Zeit, bis das klappt.
Aber ob das noch tutorial-tauglich ist, was ich da treibe, da bin ich nicht mehr sicher. So auf gar keinen Fall. Ein Zwischenverweis auf James Molley's Tutorial (er lädt mit Grubs und verwendet im Programm externe Daten aus dem Linker Skript, igitt) und weiter machen / modifizieren, wo er aufhört, macht IMHO keinen Sinn.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
@+gjm+: Danke für die Analyse, kannst Du Dir den aktuellen Code nochmals anschauen?
Da waren leider doch noch einige plötzlich aufpoppende Fehler mit memset() anstelle k_memset in ordered_array usw., aber dennoch klappt es noch nicht richtig. Habe auch in den headern os.h, ... noch etwas Ordnung (hoffentlich) geschafft.
Ich schiebe den aktuellen Code mal hoch, vielleicht hat jemand die entscheidende Idee zum Durchblick. Vor lauter blöden Adressen blicke ich nicht mehr durch.
create_heap() hilft auch noch nicht. Bin nicht völlig sicher, ob das in ckernel.c sein muss. Keine Ahnung, wo der heap momentan erzeugt wird.
Sehe vor lauter Wald die Bäume nicht mehr, aber den Code dürfte ich jetzt soweit funktionsfähig haben. Könnte sich das bitte mal jemand anschauen (fehlen nur noch die richtigen Parameter, sicher bin ich aber noch nicht)? http://www.henkessoft.de/OS_Dev/Downloads/20_analyze_failure.zip
EDIT1: hier sieht die frames Anzahl (mit createheap(0x300000,...) z.B. 3674124) nun besser aus.
es läuft ohne sti() durch bis zum Umschalten auf Paging, dann kommt der reset, kann noch an falschen Parametern liegen:
http://www.henkessoft.de/OS_Dev/Bilder/Fehlersuche_PAGING_HEAP.PNG
EDIT2: das Problem liegt in create_heap(...)
Da ist etwas im hinteren Teil instabil. Auch mit Kontroll-Prints passieren da merkwürdige Sachen auf dem Bildschirm, noch dazu nicht gleich reproduzierbar.
//TEST
printformat("heap in create_heap(...) %x \n", heap);
for (counter=0; counter<50000000; ++counter);
//TEST
// All our assumptions are made on startAddress and endAddress being page-aligned.
ASSERT(start%PAGESIZE == 0);
ASSERT(end_addr%PAGESIZE == 0);
// Initialise the index.
heap->index = place_ordered_array( (void*)start, HEAP_INDEX_SIZE, &header_t_less_than);
//TEST
printformat("heap->index in create_heap(...) %x \n", heap->index);
printformat("header_t_less_than in create_heap(...) %x \n", &header_t_less_than);
for (counter=0; counter<50000000; ++counter);
//TEST
// Shift the start address forward to resemble where we can start putting data.
start += sizeof(type_t)*HEAP_INDEX_SIZE;
//TEST
printformat("start in create_heap(...) %x \n", start);
for (counter=0; counter<50000000; ++counter);
//TEST
// Make sure the start address is page-aligned. if ((start & 0xFFFFF000) != 0)
{
start &= 0xFFFFF000;
start += PAGESIZE;
}
//TEST
printformat("start in create_heap(...) after alignment check %x \n", start);
for (counter=0; counter<50000000; ++counter);
//TEST
// Write the start, end and max addresses into the heap structure.
heap->start_address = start;
heap->end_address = end_addr;
heap->max_address = max;
heap->supervisor = supervisor;
heap->readonly = readonly;
// We start off with one large hole in the index.
header_t* hole = (header_t*) start;
hole->size = end_addr-start;
hole->magic = HEAP_MAGIC;
hole->is_hole = 1;
//TEST
printformat("hole %x", hole);
for (counter=0; counter<50000000; ++counter);
//TEST
insert_ordered_array((void*)hole, &heap->index);
//TEST
printformat("after insert_ordered_array");
for (counter=0; counter<50000000; ++counter);
//TEST
//TEST
printformat("heap in create_heap(...) %x \n", heap);
for (counter=0; counter<50000000; ++counter);
//TEST
// All our assumptions are made on startAddress and endAddress being page-aligned.
ASSERT(start%PAGESIZE == 0);
ASSERT(end_addr%PAGESIZE == 0);
// Initialise the index.
heap->index = place_ordered_array( (void*)start, HEAP_INDEX_SIZE, &header_t_less_than);
//TEST
printformat("heap->index in create_heap(...) %x \n", heap->index);
printformat("header_t_less_than in create_heap(...) %x \n", &header_t_less_than);
for (counter=0; counter<50000000; ++counter);
//TEST
// Shift the start address forward to resemble where we can start putting data.
start += sizeof(type_t)*HEAP_INDEX_SIZE;
//TEST
printformat("start in create_heap(...) %x \n", start);
for (counter=0; counter<50000000; ++counter);
//TEST
// Make sure the start address is page-aligned. if ((start & 0xFFFFF000) != 0)
{
start &= 0xFFFFF000;
start += PAGESIZE;
}
//TEST
printformat("start in create_heap(...) after alignment check %x \n", start);
for (counter=0; counter<50000000; ++counter);
//TEST
// Write the start, end and max addresses into the heap structure.
heap->start_address = start;
heap->end_address = end_addr;
heap->max_address = max;
heap->supervisor = supervisor;
heap->readonly = readonly;
// We start off with one large hole in the index.
header_t* hole = (header_t*) start;
hole->size = end_addr-start;
hole->magic = HEAP_MAGIC;
hole->is_hole = 1;
//TEST
printformat("hole %x", hole);
for (counter=0; counter<50000000; ++counter);
//TEST
insert_ordered_array((void*)hole, &heap->index);
//TEST
printformat("after insert_ordered_array");
for (counter=0; counter<50000000; ++counter);
//TEST
//TEST
printformat("heap in create_heap(...) %x \n", heap);
for (counter=0; counter<50000000; ++counter);
//TEST
// All our assumptions are made on startAddress and endAddress being page-aligned.
ASSERT(start%PAGESIZE == 0);
ASSERT(end_addr%PAGESIZE == 0);
// Initialise the index.
heap->index = place_ordered_array( (void*)start, HEAP_INDEX_SIZE, &header_t_less_than);
//TEST
printformat("heap->index in create_heap(...) %x \n", heap->index);
printformat("header_t_less_than in create_heap(...) %x \n", &header_t_less_than);
for (counter=0; counter<50000000; ++counter);
//TEST
// Shift the start address forward to resemble where we can start putting data.
start += sizeof(type_t)*HEAP_INDEX_SIZE;
//TEST
printformat("start in create_heap(...) %x \n", start);
for (counter=0; counter<50000000; ++counter);
//TEST
// Make sure the start address is page-aligned. if ((start & 0xFFFFF000) != 0)
{
start &= 0xFFFFF000;
start += PAGESIZE;
}
//TEST
printformat("start in create_heap(...) after alignment check %x \n", start);
for (counter=0; counter<50000000; ++counter);
//TEST
// Write the start, end and max addresses into the heap structure.
heap->start_address = start;
heap->end_address = end_addr;
heap->max_address = max;
heap->supervisor = supervisor;
heap->readonly = readonly;
// We start off with one large hole in the index.
header_t* hole = (header_t*) start;
hole->size = end_addr-start;
hole->magic = HEAP_MAGIC;
hole->is_hole = 1;
//TEST
printformat("hole %x", hole);
for (counter=0; counter<50000000; ++counter);
//TEST
insert_ordered_array((void*)hole, &heap->index);
//TEST
printformat("after insert_ordered_array");
for (counter=0; counter<50000000; ++counter);
//TEST
return heap;
}
Zitat:
heap in create_heap(...) 0000B1D0h
heap->index in create_heap(...) 00200000h
header_t_less_than in create_heap(...) 0000A90Ch
start in create_heap(...) 00280000h
start ... after alignment check 00281000h
Absturz?
Manchmal kommt auch noch die nächste Zeile. ???
Nach der letzten Ausgabe springt der Cursor nach (0,0)! keine Ahnung warum?
Dann erfolgt Reset.
Ohne sti() bekommt man mehr Infos (s.u. seaprater Post)
Bitte um weitere Unterstützung, damit wir das Thema didaktisch angehen können.
Ich werde mich mal zur Ablenkung auf die Key Queue als nächste Aufgabe konzentrieren.
Diesen Paging/Heap Mist werde ich etwas schieben. Dieser verschachtelte Code mit den kreuz und quer verteilten Parametern nervt mich langsam.
Der Code müsste für ein Tutorial entflochten und klar strukturiert werden.
Vielleicht kennt/hat jemand etwas Besseres?
Problemliste:
- Beep() geht nicht (speaker)
- Paging/Heap zum Laufen bringen
Kurzfristige Taskliste:
- KeyQueue aufbauen (als ersten Schritt kann man die Daten ja über eine Auswerte-Funktion in ckernel.c abholen und darstellen)
- Paging/Heap entflechten (erst Heap und Demo, dann Paging für Multitasking)
Auf jeden Fall danke an alle, die mich unterstützen.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 00:35:52 13.04.2009, insgesamt 9-mal bearbeitet
Fehler gefunden, ein kleines & zuviel vor 'end'. Man sucht immer an der falschen Stelle. Naja, zumindest beschäftigt man sich intensiv mit dem Code. Das hat seine Vorteile.
Mit Bitte um Vorschläge zur Vereinfachung und Demo-Nutzung des Codes,
z.B. Page Fault ...
Bezüglich Didaktik denke ich darüber nach:
Wie kann man Paging zum Anfassen/Experimentieren darstellen?
Welche Vorteile haben wir nun davon?
...
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 14:19:07 13.04.2009, insgesamt 7-mal bearbeitet
da gibt's doch 'ne funktion 'create_heap'. die musste natürlich vorher aufrufen.
@+fricky: Wie Du siehst ist Paging und Heap hier entkoppelt, geht also zum Glück ohne dieses create_heap(...) via kmalloc(...).
ok, aber irgendwie musste dem heap ja sagen, wo sein speicher ist.
Erhard Henkes schrieb:
- Was macht man nun mit dem Heap? - Einfache Anwendungssteuerung? (was zuerst?)
na, den user-programmen (und dem kernel, also z.b. treibern usw) die funktionen malloc(), realloc() und free() zur verfügung stellen.
Erhard Henkes schrieb:
- Anwendung auf KeyQueue zugreifen lassen
hier haste für sowas einen lock-free FIFO: http://svn.akop.org/psp/tags/fuse/0.9.0.1/sound/sfifo.c
so'n FIFO kann z.b. von einer interrupt-routine (oder anderen task) gefüllt und während des lesens müssen keine interrupts gesperrt werden. ich hab' ihn schon mal benutzt: funzt perfekt.
/* This defines the operatings system common data area */
#define KQSIZE 100 // size of key queue
typedef struct oda
{
// Hardware Data
//...
// Key Queue
UCHAR KEYQUEUE[KQSIZE]; // circular queue buffer
UCHAR* pHeadKQ; // pointer to the head of valid data
UCHAR* pTailKQ; // pointer to the tail of valid data
UCHAR KQ_count_read; // number of data read from queue buffer
UCHAR KQ_count_write; // number of data put into queue buffer
}oda_t;
// operatings system common data area
oda_t ODA;
extern oda_t* pODA;
/* This defines the operatings system common data area */
#define KQSIZE 100 // size of key queue
typedef struct oda
{
// Hardware Data
//...
// Key Queue
UCHAR KEYQUEUE[KQSIZE]; // circular queue buffer
UCHAR* pHeadKQ; // pointer to the head of valid data
UCHAR* pTailKQ; // pointer to the tail of valid data
UCHAR KQ_count_read; // number of data read from queue buffer
UCHAR KQ_count_write; // number of data put into queue buffer
}oda_t;
// operatings system common data area
oda_t ODA;
extern oda_t* pODA;
/* This defines the operatings system common data area */
#define KQSIZE 100 // size of key queue
typedef struct oda
{
// Hardware Data
//...
// Key Queue
UCHAR KEYQUEUE[KQSIZE]; // circular queue buffer
UCHAR* pHeadKQ; // pointer to the head of valid data
UCHAR* pTailKQ; // pointer to the tail of valid data
UCHAR KQ_count_read; // number of data read from queue buffer
UCHAR KQ_count_write; // number of data put into queue buffer
}oda_t;
// operatings system common data area
oda_t ODA;
extern oda_t* pODA;
util.c (später vielleicht os.c):
C/C++ Code:
1 2 3 4 5 6 7 8 9 10 11 12
1 2 3 4 5 6 7 8 9 10 11 12
oda_t* pODA = &ODA;
void initODA()
{
int i;
for(i=0;i<KQSIZE;++i)
pODA->KEYQUEUE[i]=0; // circular queue buffer
pODA->pHeadKQ = pODA->KEYQUEUE; // pointer to the head of valid data
pODA->pTailKQ = pODA->KEYQUEUE; // pointer to the tail of valid data
pODA->KQ_count_read = 0; // number of data read from queue buffer
pODA->KQ_count_write = 0; // number of data put into queue buffer
}
C/C++ Code:
1 2 3 4 5 6 7 8 9 10 11 12
oda_t* pODA = &ODA;
void initODA()
{
int i;
for(i=0;i<KQSIZE;++i)
pODA->KEYQUEUE[i]=0; // circular queue buffer
pODA->pHeadKQ = pODA->KEYQUEUE; // pointer to the head of valid data
pODA->pTailKQ = pODA->KEYQUEUE; // pointer to the tail of valid data
pODA->KQ_count_read = 0; // number of data read from queue buffer
pODA->KQ_count_write = 0; // number of data put into queue buffer
}
C/C++ Code:
1 2 3 4 5 6 7 8 9 10 11 12
oda_t* pODA = &ODA;
void initODA()
{
int i;
for(i=0;i<KQSIZE;++i)
pODA->KEYQUEUE[i]=0; // circular queue buffer
pODA->pHeadKQ = pODA->KEYQUEUE; // pointer to the head of valid data
pODA->pTailKQ = pODA->KEYQUEUE; // pointer to the tail of valid data
pODA->KQ_count_read = 0; // number of data read from queue buffer
pODA->KQ_count_write = 0; // number of data put into queue buffer
}
0xA207 = 0xA1A4 + 0x64 (KQSIZE) - 1
passt also alles. Den Tasten-Release (KEY == 0) habe ich unterdrückt, so dass nur ein Zeichen pro Tastenanschlag in die KQ gelangt.
Ich möchte diesen Mechanismus komplett selbst durchziehen, weil ich das derart low-level noch nicht gemacht habe, bin durch die Standard Template Library verhätschelt.
Über Lesen aus der KQ und Interrupts habe ich mir noch keine Gedanken gemacht.
Wie geht man mit einem buffer full, also (pODA->KQ_count_write - pODA->KQ_count_read)>100 um. Da ich die Zähler momentan leichtsinnig in UCHAR definiert habe, kommt es auch noch zu einem Überlauf. Das lässt sich sicher durch ULONG in den Griff bekommen, aber prinzipiell ist das Problem dadurch nur in die weite Ferne geschoben. Es soll ja Leute geben, die ihren Rechner nie ausschalten. Bei PrettyOS ist das fast garantiert.
restore_cursor();
switch(KEY)
{
case KINS:
break;
case KDEL:
move_cursor_right();
putch('\b'); //BACKSPACE break;
case KHOME:
move_cursor_home();
break;
case KEND:
move_cursor_end();
break;
case KPGUP:
break;
case KPGDN:
break;
case KLEFT:
move_cursor_left();
break;
case KUP:
break;
case KDOWN:
break;
case KRIGHT:
move_cursor_right();
break;
default:
printformat("%c",KEY); // the ASCII character break;
}
save_cursor();
restore_cursor();
switch(KEY)
{
case KINS:
break;
case KDEL:
move_cursor_right();
putch('\b'); //BACKSPACE break;
case KHOME:
move_cursor_home();
break;
case KEND:
move_cursor_end();
break;
case KPGUP:
break;
case KPGDN:
break;
case KLEFT:
move_cursor_left();
break;
case KUP:
break;
case KDOWN:
break;
case KRIGHT:
move_cursor_right();
break;
default:
printformat("%c",KEY); // the ASCII character break;
}
save_cursor();
restore_cursor();
switch(KEY)
{
case KINS:
break;
case KDEL:
move_cursor_right();
putch('\b'); //BACKSPACE break;
case KHOME:
move_cursor_home();
break;
case KEND:
move_cursor_end();
break;
case KPGUP:
break;
case KPGDN:
break;
case KLEFT:
move_cursor_left();
break;
case KUP:
break;
case KDOWN:
break;
case KRIGHT:
move_cursor_right();
break;
default:
printformat("%c",KEY); // the ASCII character break;
}
save_cursor();
nun eigentlich hin? Der User will doch Zahlen und Buchstaben sehen, wenn er tippt. Zumindest als Demo.
Soll ich das als getchar() in keyboard.c realisieren und in ckernel.c mal als demo in der while-Schleife (klares "Polling") laufen lassen oder habt ihr bessere Ideen?
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 23:08:31 13.04.2009, insgesamt 6-mal bearbeitet
Ich habe das zunächst als k_checkKQ_and_print_char() umgesetzt, um didaktisch an unsere bisherige - von Nobuo T kritisierte - "Schreibmaschine" in keyboard_handler(...) anzuknüpfen.
Test und Reaktion (welche?) auf BufferFull/Overrun fehlt noch.
Die Pfeiltasten links/rechts kommen nicht mehr durch.
Was man wirklich mit den Infos in der KeyQueue machen will, hängt sehr von der konkreten Anwendung ab. Ich frage mich momentan, wie man mit den Sondertasten sinnvoll umgehen sollte. Als Flags in der ODA wie bei der BDA machen sie keinen Sinn, weil man nicht weiß, zu welchem Zeichen sie gehören. Aber das lässt sich alles leicht anpassen. Vielleicht hat jemand eine glänzende Idee oder gute Erfahrungen mit eigenen OS.
Baut man eine Verzögerungsschleife ein, so kommt Schreiben/Lesen so ab 100 Millisekunden (Warteschleife für ms habe ich nun in timer.c ergänzt) aus dem Gleichklang: http://www.henkessoft.de/OS_Dev/Downloads/22b.zip
Code:
1 2 3 4 5 6 7 8 9 10 11
1 2 3 4 5 6 7 8 9 10 11
jksdghkdfjhgkskdfjhgkjsdfhgkjsdfhgskjdfhkhjhgdfjkhgdfkjhgkjdfhgkjdfhgkdfjhgkjdfh
gkfdhgkdfhgkdfhgkdfhgkdfhgkdfjhgkdfhgkdfhgkfhkjdshgkjsdfjjjjjjjjjjjjjjjjjjjjjjjj
jjjjJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAh WRITE: 662 Read: 640 *T: A 65 *H: A 65
T: 0000A2E0h H: 0000A30Dh WRITE: 765 Read: 720 *T: A 65 *H: A 65
Code:
1 2 3 4 5 6 7 8 9 10 11
jksdghkdfjhgkskdfjhgkjsdfhgkjsdfhgskjdfhkhjhgdfjkhgdfkjhgkjdfhgkjdfhgkdfjhgkjdfh
gkfdhgkdfhgkdfhgkdfhgkdfhgkdfjhgkdfhgkdfhgkfhkjdshgkjsdfjjjjjjjjjjjjjjjjjjjjjjjj
jjjjJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAh WRITE: 662 Read: 640 *T: A 65 *H: A 65
T: 0000A2E0h H: 0000A30Dh WRITE: 765 Read: 720 *T: A 65 *H: A 65
Code:
1 2 3 4 5 6 7 8 9 10 11
jksdghkdfjhgkskdfjhgkjsdfhgkjsdfhgskjdfhkhjhgdfjkhgdfkjhgkjdfhgkjdfhgkdfjhgkjdfh
gkfdhgkdfhgkdfhgkdfhgkdfhgkdfjhgkdfhgkdfhgkfhkjdshgkjsdfjjjjjjjjjjjjjjjjjjjjjjjj
jjjjJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAh WRITE: 662 Read: 640 *T: A 65 *H: A 65
T: 0000A2E0h H: 0000A30Dh WRITE: 765 Read: 720 *T: A 65 *H: A 65
Bleiben die Fragen, wie groß muss KQ sein und wie geht man mit einem Overrun um? (früher hat es da gepiepst ).
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 01:14:20 14.04.2009, insgesamt 2-mal bearbeitet
Bezüglich der Interpretation von Scancodes habe ich folgende interessante Gliederung gefunden:
http://www.toasteros.net/
Zitat:
ASCII Translation
Bedeutet Tasten die als ASCII/ANSI Zeichen darzustellen sind, solche wie „asdf“.
Folgende Scancodes zählen dazu: 2 – 13, 16 – 27, 30 – 41, 43 – 53, 86
Sie müssen je nach Shift und Alt Gr und Tastatur Layout interpretiert werden.
Immediate Translation
Tasten die direkt an den User weitergesendet werden ohne vorher in irgendeiner Weise interpretiert zu haben, solche wie die escape taste oder backspace.
Folgende Scancodes zählen dazu: 0 – 1, 14 – 15, 28, 57, 59 – 68, 87 – 88
Numblock Translation
Tasten die vom numerischen Block stammen.
Sie müssen je nach Num Lock interpretiert werden.
Folgende Scancodes zählen dazu: 69, 71 – 83, 55
Direct Translation
Der Rest der Tasten der direkt interpretiert werden muss, dazu gehören caps lock, ctrl, shift, alt und screen lock.
Folgende Scancodes zählen dazu: 58, 29, 42, 54, 56, 70
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Bleiben die Fragen, wie groß muss KQ sein und wie geht man mit einem Overrun um?
einfach das älteste zeichen überschreiben (overrun ignorieren). daten von der tastatur kannste als veraltet ansehen, wenn lange keiner drauf zugegriffen hat. der buffer kann auch ziemlich klein sein.
Ja, ich denke auch, dass ein Puffer für Tastatureingaben eher max. 20 Zeichen haben sollte, denn es wirkt - siehe Beispiel mit Warteschleife oben - extrem komisch, wenn da nach dem Tippen noch fast 100 Zeichen aus der Queue auf dem Bildschirm eintrudeln.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
...extrem komisch, wenn da nach dem Tippen noch fast 100 Zeichen aus der Queue auf dem Bildschirm eintrudeln.
vor allem brauchste dir keine gedanken darum zu machen, wie du den fall handlest, wenn die queue voll ist und neue daten eintrudeln. das problem kriegste erst bei datenquellen, wo nix verloren gehen darf.
C++ in the Kernel
Opinions vary widely over the use of C++ in a kernel: most Linux kernel coders stay well away from it, whereas some people have entire operating systems in C++. I'd personally stick with C, although I see no real reason why you shouldn't use C++, as long as you know what you're doing. One thing you should realize is that to write a C++ kernel you'll need to write a bit more code for the framework, and that some C++ features are off-limits to you (unless you can write the necessary support code). First off, you'll need to code the new and delete operators. This is easy if you've already coded malloc() and free(): new and delete can be single-line functions which call each of these. If you want to have global instances of classes you'll need to put some code in your startup routine to call each of their constructors. The way this is done will differ between compilers, so the best thing to do is to browse through your compiler's run-time library to see how the vendor did it; try looking in files with names like crt0.c. You'll probably also need to implement atexit(), because the compiler is likely to emit code which uses atexit() to call global object's destructors when the program ends. If you want to use try/catch in your kernel you'll have to implement whatever mechanism your compile uses to implement them. Again, this will depend on the compiler you use, and whatever operating system that compiler targets. In general, I'd steer clear of both exception handling and huge virtual classes in your kernel. Exception handling usually adds unneeded bloat, and calling masses of virtual functions add unnecessary indirection and defeats the optimizer somewhat. Remember, your kernel should be as efficient as possible, even to the detriment of a beautiful design where necessary. Writing an OS in a high-level language such as C can be a lot more productive than coding one entirely in assembly, particularly if you're willing to forego the slight speed increase that assembly offers. There are compilers freely available, such as gcc, which make writing kernel code relatively easy, and using C will probably prove beneficial in the long run
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 20:32:03 14.04.2009, insgesamt 2-mal bearbeitet
Ich habe da eine Funktion - basierend auf dem Sourcecode von James Molloy - die dem Compiler beständig eine Warnung entlockt:
C/C++ Code:
1 2 3 4 5 6 7 8 9
1 2 3 4 5 6 7 8 9
static ULONG first_frame() // find the first free frame in frames bitset
{
ULONG index, offset;
for(index=0; index<(NFRAMES/32); ++index)
if(frames[index] != 0xFFFFFFFF)
for(offset=0; offset<32; ++offset)
if( !(frames[index] & 1<<offset) ) // bit set to zero? return (index*32 + offset);
}
C/C++ Code:
1 2 3 4 5 6 7 8 9
static ULONG first_frame() // find the first free frame in frames bitset
{
ULONG index, offset;
for(index=0; index<(NFRAMES/32); ++index)
if(frames[index] != 0xFFFFFFFF)
for(offset=0; offset<32; ++offset)
if( !(frames[index] & 1<<offset) ) // bit set to zero? return (index*32 + offset);
}
C/C++ Code:
1 2 3 4 5 6 7 8 9
static ULONG first_frame() // find the first free frame in frames bitset
{
ULONG index, offset;
for(index=0; index<(NFRAMES/32); ++index)
if(frames[index] != 0xFFFFFFFF)
for(offset=0; offset<32; ++offset)
if( !(frames[index] & 1<<offset) ) // bit set to zero? return (index*32 + offset);
}
Zitat:
warning: control reaches end of non-void function
Diese Warnung erhält man, wenn eine Funktion beendet wird, ohne einen Rückgabewert zu erzeugen, obwohl dies laut Deklaration erfolgen sollte. Hat jemand eine Idee, wie man diese nicht einfach lesbare Funktion gesamtheitlich am besten umbaut, damit diese Warnung entfällt.
Anwendung dieser modul-eigenen static-Funktion bisher nur hier:
C/C++ Code:
1 2 3 4 5 6 7 8 9 10 11
1 2 3 4 5 6 7 8 9 10 11
void alloc_frame(page_t* page, int is_kernel, int is_writeable) // allocate a frame
{
if (!(page->frame_addr) )
{
ULONG index = first_frame(); //search first free page frame if (index == (ULONG)-1)
printformat("message from alloc_frame: no free frames!!!");
\\...
}
}
C/C++ Code:
1 2 3 4 5 6 7 8 9 10 11
void alloc_frame(page_t* page, int is_kernel, int is_writeable) // allocate a frame
{
if (!(page->frame_addr) )
{
ULONG index = first_frame(); //search first free page frame if (index == (ULONG)-1)
printformat("message from alloc_frame: no free frames!!!");
\\...
}
}
C/C++ Code:
1 2 3 4 5 6 7 8 9 10 11
void alloc_frame(page_t* page, int is_kernel, int is_writeable) // allocate a frame
{
if (!(page->frame_addr) )
{
ULONG index = first_frame(); //search first free page frame if (index == (ULONG)-1)
printformat("message from alloc_frame: no free frames!!!");
\\...
}
}
Man könnte first_frame() z.B. void deklarieren und eine Argument 'index' als Pointer übergeben, aber vielleicht habt ihr eine bessere Idee.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 21:58:46 16.04.2009, insgesamt 1-mal bearbeitet
// map (phys addr <---> virt addr) from 0x0 to the end of used memory
ULONG i=0, counter=0;
while (i < placement_address)
{
alloc_frame( get_page(i, 1, kernel_directory), 0, 0);
i += PAGESIZE;
++counter;
}
C/C++ Code:
1 2 3 4 5 6 7 8
// map (phys addr <---> virt addr) from 0x0 to the end of used memory
ULONG i=0, counter=0;
while (i < placement_address)
{
alloc_frame( get_page(i, 1, kernel_directory), 0, 0);
i += PAGESIZE;
++counter;
}
C/C++ Code:
1 2 3 4 5 6 7 8
// map (phys addr <---> virt addr) from 0x0 to the end of used memory
ULONG i=0, counter=0;
while (i < placement_address)
{
alloc_frame( get_page(i, 1, kernel_directory), 0, 0);
i += PAGESIZE;
++counter;
}
Eigentlich müsste die Funktion mit gesetztem make-Flag 'make_page(...)' heißen.
Sollte man diese Funktion in eine get_page(...) und eine make_page(...) zerlegen oder ist dies eurer Meinung so o.k.?
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 22:09:15 16.04.2009, insgesamt 1-mal bearbeitet
Ich habe da eine Funktion - basierend auf dem Sourcecode von James Molloy - die dem Compiler beständig eine Warnung entlockt:
C/C++ Code:
1 2 3 4 5 6 7 8 9
1 2 3 4 5 6 7 8 9
static ULONG first_frame() // find the first free frame in frames bitset
{
ULONG index, offset;
for(index=0; index<(NFRAMES/32); ++index)
if(frames[index] != 0xFFFFFFFF)
for(offset=0; offset<32; ++offset)
if( !(frames[index] & 1<<offset) ) // bit set to zero? return (index*32 + offset);
}
C/C++ Code:
1 2 3 4 5 6 7 8 9
static ULONG first_frame() // find the first free frame in frames bitset
{
ULONG index, offset;
for(index=0; index<(NFRAMES/32); ++index)
if(frames[index] != 0xFFFFFFFF)
for(offset=0; offset<32; ++offset)
if( !(frames[index] & 1<<offset) ) // bit set to zero? return (index*32 + offset);
}
C/C++ Code:
1 2 3 4 5 6 7 8 9
static ULONG first_frame() // find the first free frame in frames bitset
{
ULONG index, offset;
for(index=0; index<(NFRAMES/32); ++index)
if(frames[index] != 0xFFFFFFFF)
for(offset=0; offset<32; ++offset)
if( !(frames[index] & 1<<offset) ) // bit set to zero? return (index*32 + offset);
}
Zitat:
warning: control reaches end of non-void function
Diese Warnung erhält man, wenn eine Funktion beendet wird, ohne einen Rückgabewert zu erzeugen, obwohl dies laut Deklaration erfolgen sollte. Hat jemand eine Idee, wie man diese nicht einfach lesbare Funktion gesamtheitlich am besten umbaut, damit diese Warnung entfällt.
Anwendung dieser modul-eigenen static-Funktion bisher nur hier:
C/C++ Code:
1 2 3 4 5 6 7 8 9 10 11
1 2 3 4 5 6 7 8 9 10 11
void alloc_frame(page_t* page, int is_kernel, int is_writeable) // allocate a frame
{
if (!(page->frame_addr) )
{
ULONG index = first_frame(); //search first free page frame if (index == (ULONG)-1)
printformat("message from alloc_frame: no free frames!!!");
\\...
}
}
C/C++ Code:
1 2 3 4 5 6 7 8 9 10 11
void alloc_frame(page_t* page, int is_kernel, int is_writeable) // allocate a frame
{
if (!(page->frame_addr) )
{
ULONG index = first_frame(); //search first free page frame if (index == (ULONG)-1)
printformat("message from alloc_frame: no free frames!!!");
\\...
}
}
C/C++ Code:
1 2 3 4 5 6 7 8 9 10 11
void alloc_frame(page_t* page, int is_kernel, int is_writeable) // allocate a frame
{
if (!(page->frame_addr) )
{
ULONG index = first_frame(); //search first free page frame if (index == (ULONG)-1)
printformat("message from alloc_frame: no free frames!!!");
\\...
}
}
Man könnte first_frame() z.B. void deklarieren und eine Argument 'index' als Pointer übergeben, aber vielleicht habt ihr eine bessere Idee.
mach ans ende der funktion return (ULONG)-1;
das ist ja der wert für einen ungültigen index, den der aufrufer auch auswertet.
(ULONG)-1 sollte man hier besser 0xFFFFFFFF schreiben?
Ich habe übrigens von James Molloy schriftlich die Freigabe, seinen - aus meiner Sicht nicht uninteressanten - Sourcecode als Basis für eigene Entwicklungen im Rahmen des Tutorials nutzen zu dürfen. Man muss ja nicht immer wieder das Rad neu erfinden, wie man so schön sagt.
Gerade die Verwendung des Bitsets beim Paging ist effizient. Ich habe hier seine #defines durch get_Index_and_Offset(...) ersetzt, so dass man diesen Teil nicht ständig komplett in set/clear/test wiederholen muss:
Er hat in seinem Tutorial (Kap. 6-10) folgende Reihenfolge aufgeboten:
6) Paging
7) Heap
8) Virtual File System
9) Multitasking
10) User mode and syscalls
Sein Code und seine Erläuterungen sind aus meiner Sicht aber nur schwer verständlich für Einsteiger in die Materie. Da arbeite ich dran, das zu entflechten und didaktisch zugänglich zu machen.
Suche didaktisch noch nach einem guten Weg, die eminente bedeutung von paging und heap wirklich begreiflich zu machen. Aktiviert man momentan die Funktion create_heap in ckernel.c, dann erfolgt ein 'page fault'.
Der nächste Schritt wäre genau die Überwindung dieses Punkts.
Aber es gibt didaktisch noch viele Probleme, z.B. würde ich gerne die Speichernutzung transparent machen, weiß aber nicht wie ich das visualisieren soll. Woher weiß ich, wo der kernel endet ('end' im Linker-Skript? einer meiner Schwächen)? Wo gehört die Page-Verwaltung hin? Warum wird nicht gleich alles gepaged? Wo gehört ein Heap hin? Wo das placement_address? So richtig wird das alles ja erst beim Multitasking klar. Aber man muss vorher damit sinnvoll experimentieren. Falls da jemand geniale Ideen hat.
James Molloy baut das wie folgt auf (aber keine Ahnung, ob das wirklich gut ist):
- Ende des Kernels (via Linker Script, wie könnte man bei uns diese Adresse sinnvoll besorgen?)
- Bitset ( frames[...] )
- page_directory
// Map some pages in the kernel heap area.
// Here we call get_page but not alloc_frame. This causes page_table_t's
// to be created where necessary. We can't allocate frames yet because they
// they need to be identity mapped first below, and yet we can't increase
// placement_address between identity mapping and enabling the heap! int i = 0;
for (i = KHEAP_START; i < KHEAP_START+KHEAP_INITIAL_SIZE; i += 0x1000)
get_page(i, 1, kernel_directory);
// We need to identity map (phys addr = virt addr) from
// 0x0 to the end of used memory, so we can access this
// transparently, as if paging wasn't enabled.
// NOTE that we use a while loop here deliberately.
// inside the loop body we actually change placement_address
// by calling kmalloc(). A while loop causes this to be
// computed on-the-fly rather than once at the start.
// Allocate a lil' bit extra so the kernel heap can be
// initialised properly.
i = 0;
while (i < placement_address + 0x1000)
{
// Kernel code is readable but not writeable from userspace.
alloc_frame( get_page(i, 1, kernel_directory), 0, 0);
i += 0x1000;
}
// Map some pages in the kernel heap area.
// Here we call get_page but not alloc_frame. This causes page_table_t's
// to be created where necessary. We can't allocate frames yet because they
// they need to be identity mapped first below, and yet we can't increase
// placement_address between identity mapping and enabling the heap! int i = 0;
for (i = KHEAP_START; i < KHEAP_START+KHEAP_INITIAL_SIZE; i += 0x1000)
get_page(i, 1, kernel_directory);
// We need to identity map (phys addr = virt addr) from
// 0x0 to the end of used memory, so we can access this
// transparently, as if paging wasn't enabled.
// NOTE that we use a while loop here deliberately.
// inside the loop body we actually change placement_address
// by calling kmalloc(). A while loop causes this to be
// computed on-the-fly rather than once at the start.
// Allocate a lil' bit extra so the kernel heap can be
// initialised properly.
i = 0;
while (i < placement_address + 0x1000)
{
// Kernel code is readable but not writeable from userspace.
alloc_frame( get_page(i, 1, kernel_directory), 0, 0);
i += 0x1000;
}
// Map some pages in the kernel heap area.
// Here we call get_page but not alloc_frame. This causes page_table_t's
// to be created where necessary. We can't allocate frames yet because they
// they need to be identity mapped first below, and yet we can't increase
// placement_address between identity mapping and enabling the heap! int i = 0;
for (i = KHEAP_START; i < KHEAP_START+KHEAP_INITIAL_SIZE; i += 0x1000)
get_page(i, 1, kernel_directory);
// We need to identity map (phys addr = virt addr) from
// 0x0 to the end of used memory, so we can access this
// transparently, as if paging wasn't enabled.
// NOTE that we use a while loop here deliberately.
// inside the loop body we actually change placement_address
// by calling kmalloc(). A while loop causes this to be
// computed on-the-fly rather than once at the start.
// Allocate a lil' bit extra so the kernel heap can be
// initialised properly.
i = 0;
while (i < placement_address + 0x1000)
{
// Kernel code is readable but not writeable from userspace.
alloc_frame( get_page(i, 1, kernel_directory), 0, 0);
i += 0x1000;
}
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 00:25:58 17.04.2009, insgesamt 8-mal bearbeitet
static ULONG first_frame() // find the first free frame in frames bitset
{
ULONG index, offset;
for(index=0; index<(NFRAMES/32); ++index)
if(frames[index] != 0xFFFFFFFF)
for(offset=0; offset<32; ++offset)
if( !(frames[index] & 1<<offset) ) // bit set to zero? return (index*32 + offset);
return 0xFFFFFFFF;
}
C/C++ Code:
1 2 3 4 5 6 7 8 9 10
static ULONG first_frame() // find the first free frame in frames bitset
{
ULONG index, offset;
for(index=0; index<(NFRAMES/32); ++index)
if(frames[index] != 0xFFFFFFFF)
for(offset=0; offset<32; ++offset)
if( !(frames[index] & 1<<offset) ) // bit set to zero? return (index*32 + offset);
return 0xFFFFFFFF;
}
C/C++ Code:
1 2 3 4 5 6 7 8 9 10
static ULONG first_frame() // find the first free frame in frames bitset
{
ULONG index, offset;
for(index=0; index<(NFRAMES/32); ++index)
if(frames[index] != 0xFFFFFFFF)
for(offset=0; offset<32; ++offset)
if( !(frames[index] & 1<<offset) ) // bit set to zero? return (index*32 + offset);
return 0xFFFFFFFF;
}
C/C++ Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
void alloc_frame(page_t* page, int is_kernel, int is_writeable) // allocate a frame
{
if( !(page->frame_addr) )
{
ULONG index = first_frame(); //search first free page frame if( index == 0xFFFFFFFF )
printformat("message from alloc_frame: no free frames!!!");
void alloc_frame(page_t* page, int is_kernel, int is_writeable) // allocate a frame
{
if( !(page->frame_addr) )
{
ULONG index = first_frame(); //search first free page frame if( index == 0xFFFFFFFF )
printformat("message from alloc_frame: no free frames!!!");
void alloc_frame(page_t* page, int is_kernel, int is_writeable) // allocate a frame
{
if( !(page->frame_addr) )
{
ULONG index = first_frame(); //search first free page frame if( index == 0xFFFFFFFF )
printformat("message from alloc_frame: no free frames!!!");
// Static function to find the first free frame. static u32int first_frame()
{
u32int i, j;
for (i = 0; i < INDEX_FROM_BIT(nframes); i++)
{
if (frames[i] != 0xFFFFFFFF) // nothing free, exit early.
{
// at least one bit is free here. for (j = 0; j < 32; j++)
{
u32int toTest = 0x1 << j;
if ( !(frames[i]&toTest) )
{
return i*4*8+j;
}
}
}
}
}
// Static function to find the first free frame. static u32int first_frame()
{
u32int i, j;
for (i = 0; i < INDEX_FROM_BIT(nframes); i++)
{
if (frames[i] != 0xFFFFFFFF) // nothing free, exit early.
{
// at least one bit is free here. for (j = 0; j < 32; j++)
{
u32int toTest = 0x1 << j;
if ( !(frames[i]&toTest) )
{
return i*4*8+j;
}
}
}
}
}
// Static function to find the first free frame. static u32int first_frame()
{
u32int i, j;
for (i = 0; i < INDEX_FROM_BIT(nframes); i++)
{
if (frames[i] != 0xFFFFFFFF) // nothing free, exit early.
{
// at least one bit is free here. for (j = 0; j < 32; j++)
{
u32int toTest = 0x1 << j;
if ( !(frames[i]&toTest) )
{
return i*4*8+j;
}
}
}
}
}
wieso verunstaltest du eigentlich diese, gut lesbare funktion, zu einem grausam verfrickelten ungetüm wie da oben? alle geschweiften klammern und temporären variablen wegzulöschen, aus i++ ++i machen und macros von hand aufzulösen ist zwar voll 1337-mässig, bringt aber überhaupt nix. wenn du auf verständlichkeit des codes wert legst, dann lass solchen unsinn besser sein.
ok, aber es soll doch ein tutorial sein. wenn du die funktionen ihrer lesbarkeit beraubst und eventuell dadurch fehler einbaust (z.b. i++ ist oft nicht das selbe wie ++i), dann machst du's dir und deinen lesern nur unnötig schwer.
An ein paar Klammern soll es wahrlich nicht scheitern:
C/C++ Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
static ULONG first_frame() // find the first free frame in frames bitset
{
ULONG index, offset;
for(index=0; index<(NFRAMES/32); ++index)
{
if(frames[index] != ULONG_MAX)
{
for(offset=0; offset<32; ++offset)
{
if( !(frames[index] & 1<<offset) ) // bit set to zero? return (index*32 + offset);
}
}
}
return ULONG_MAX; // no free page frames
}
C/C++ Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
static ULONG first_frame() // find the first free frame in frames bitset
{
ULONG index, offset;
for(index=0; index<(NFRAMES/32); ++index)
{
if(frames[index] != ULONG_MAX)
{
for(offset=0; offset<32; ++offset)
{
if( !(frames[index] & 1<<offset) ) // bit set to zero? return (index*32 + offset);
}
}
}
return ULONG_MAX; // no free page frames
}
C/C++ Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
static ULONG first_frame() // find the first free frame in frames bitset
{
ULONG index, offset;
for(index=0; index<(NFRAMES/32); ++index)
{
if(frames[index] != ULONG_MAX)
{
for(offset=0; offset<32; ++offset)
{
if( !(frames[index] & 1<<offset) ) // bit set to zero? return (index*32 + offset);
}
}
}
return ULONG_MAX; // no free page frames
}
Mir gefällt das sprechende index u. offset aber besser als i und j.
Das große NFRAMES finde ich auch besser, weil konstant:
C/C++ Code:
ULONG NFRAMES = PHYSICAL_MEMORY / PAGESIZE;
C/C++ Code:
ULONG NFRAMES = PHYSICAL_MEMORY / PAGESIZE;
C/C++ Code:
ULONG NFRAMES = PHYSICAL_MEMORY / PAGESIZE;
Ich verwende das Präinkrement bevorzugt vor dem Postinkrement. In der for-Schleife spielt es keine Rolle.
Welches #define ... würdest Du denn für 0xFFFFFFFF verwenden? Da hat mich Dein Vorschlag sogar überzeugt, das könnte man noch austauschen, man nimmt wohl am besten das aus limits.h:
#define ULONG_MAX 4294967295UL also ULONG_MAX
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 19:32:36 17.04.2009, insgesamt 2-mal bearbeitet
An ein paar Klammern soll es wahrlich nicht scheitern:
alle daumen hoch^^
Erhard Henkes schrieb:
Mir gefällt das sprechende index u. offset aber besser als i und j.
das ist ok, aussagekräftige bezeichner sind immer gut, solange sie auch das richtige ausdrücken.
Erhard Henkes schrieb:
Ich verwende das Präinkrement bevorzugt vor dem Postinkrement.
naja, weil's dir vielleicht besser gefällt, aber für eine änderung gibt's keinen sachlichen grund, erst recht nicht, wenn der code vorher schon gut funktionierte. ich behaupte mal, die meisten c-programmierer benutzen eher das post-increment. mir jedenfalls sticht ein pre-increment immer ins auge und ich denke mir 'warum macht das hier jemand so? das hat mehr zu bedeuten, als nur die variable hochzuzählen'.
Erhard Henkes schrieb:
Welches #define ... würdest Du denn für 0xFFFFFFFF verwenden? Da hat mich Dein Vorschlag sogar überzeugt, das könnte man noch austauschen.
das 0xFFFFFFFF bedeutet hier doch 'ungültiger wert' oder sowas, dann könnteste es z.b. INVALID_VALUE o.ä. taufen.
btw, und was selbstdefinierte typen angeht: ULONG sieht aus wie ein #define (alles gross geschrieben). nimm doch als typedef uint32_t. das wäre dann sogar richtig modern, C99-mässig
Ich finde dieses angehängte _t für Standardtypen irgendwie lästig.
C/C++ Code:
typedef unsigned int UINT;
typedef unsigned char UCHAR;
typedef unsigned short USHORT;
typedef unsigned long ULONG;
typedef signed char CHAR;
C/C++ Code:
typedef unsigned int UINT;
typedef unsigned char UCHAR;
typedef unsigned short USHORT;
typedef unsigned long ULONG;
typedef signed char CHAR;
C/C++ Code:
typedef unsigned int UINT;
typedef unsigned char UCHAR;
typedef unsigned short USHORT;
typedef unsigned long ULONG;
typedef signed char CHAR;
findet man in os.h. Schön ist dies wirklich nicht, aber auf jeden Fall kürzer als unsigned xxx. Zumindest habe ich nicht BYTE, WORD und DWORD verwendet.
Die Code-Einfärbung kennt dies auch noch nicht:
C/C++ Code:
uint32_t zahl
C/C++ Code:
uint32_t zahl
C/C++ Code:
uint32_t zahl
Da mir Didaktik wichtig ist, wie sieht dies die Allgemeinheit?
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 20:15:48 17.04.2009, insgesamt 2-mal bearbeitet
ich behaupte mal, die meisten c-programmierer benutzen eher das post-increment. mir jedenfalls sticht ein pre-increment immer ins auge und ich denke mir 'warum macht das hier jemand so? das hat mehr zu bedeuten, als nur die variable hochzuzählen'.
Dabei ist es doch genau umgekehrt, pre-incement zählt nur hoch, post-increment kann mehr bedeuten.
Ist das ein ernsthaftes Problem? Beim verwendeten DJGPP entspricht int nämlich 32 Bit. ...
Für PC-Programmierer ist es wahrscheinlich kein Problem.
Bei dem Compiler, den ich grade verwende (verwenden muss ), ist z.B. sizeof(int) == 4 (32 Bit) und sizeof(long) == 5 (40 Bit)...
Bei dem Compiler, den ich grade verwende (verwenden muss ), ist z.B. sizeof(int) == 4 (32 Bit) und sizeof(long) == 5 (40 Bit)...
Danke für das Feedback. Momentan möchte ich diese Zahlen in den Typen vermeiden. Bei seltsamen Breiten von long kann man ja in os.h im typedef von long auf int umschalten, oders ehe ich das verkehrt?
Ich habe da noch eine Warnung im Compiler, bei der ich nicht ganz sicher bin, wie man sie am saubersten abschaltet:
Wie muss die Rückgabezeile aussehen? Ich möchte nicht void* als Rückgabewert verwenden, sondern ULONG, das in PrettyOS für 32-Bit-Speicheradressen Verwendung findet.
Selbe Frage wie hier: http://www.c-plusplus.de/forum/viewtopic-var-p-is-1697586.html
Ich habe mal folgendes probiert, was offenbar funktioniert, bin bei der AT&T Syntax nie ganz sicher, aber offenbar wird es langsam:
Noch ein Punkt: Ich möchte "Files" in die kommende "RAM disk" laden und dort mit dem virtuellen Filesystem bearbeiten. Dabei habe ich die Aufgabe eine bin- oder img-Datei von Floppy (hat nicht jeder) oder Festplatte (gefährlich bei Fehler) an eine feste Stelle im Speicher zu laden. Dies erfolgt typisch mittels GRUB, unser System soll ja aber ohne GRUB laufen. Welches Tool nimmt man da bei MS Windows in makefile, um ein file.img (sagen wir mal von Festplatte) sicher an eine bestimmte Stelle im RAM zu transportieren?
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 11:43:35 18.04.2009, insgesamt 1-mal bearbeitet
Danke für das Feedback. Momentan möchte ich diese Zahlen in den Typen vermeiden. Bei seltsamen Breiten von long kann man ja in os.h im typedef von long auf int umschalten, oders ehe ich das verkehrt?
Was ist dann der Sinn und Zweck von diesen Typdefinitionen... Man strebt eine gewisse Portabilität, z.B. mit der folgenden Typdefinition:
C/C++ Code:
typedef long LONG;
C/C++ Code:
typedef long LONG;
C/C++ Code:
typedef long LONG;
weiss man immer noch nicht, wie viele Bits soll denn ein LONG sein? 32, 40 oder 64 Bit? Es geht ja nicht darum, den Typ long irgendwie zu verstecken, sondern "neue" Typen einzuführen, wo man genau weiss, dass sie so und so viele Bits breit sind.
Erhard Henkes schrieb:
Ich habe da noch eine Warnung im Compiler, bei der ich nicht ganz sicher bin, wie man sie am saubersten abschaltet...
Was würde dagegen sprechen, diese Funktion ganz in Assembler zu realisieren. Diese asm im Quellcode ist wie ein Dorn im Auge Wieder mal meine Phylosophie...
Also in Assembler z.B. so:
Assembler Code:
.global _fetchESP
.section .text
_fetchESP:
movl %esp, %eax ret
Assembler Code:
.global _fetchESP
.section .text
_fetchESP:
movl %esp, %eax ret
Assembler Code:
.global _fetchESP
.section .text
_fetchESP:
movl %esp, %eax ret
Assemblieren geht so:
Code:
as datei.s -o datei.o
Code:
as datei.s -o datei.o
Code:
as datei.s -o datei.o
Dann in irgendeiner Header Datei dem Compiler sagen, wie die Funktion aussieht:
C/C++ Code:
extern uint32_t fetchESP(void);
C/C++ Code:
extern uint32_t fetchESP(void);
C/C++ Code:
extern uint32_t fetchESP(void);
Und die Objektdatei datei.o noch beim Linken angeben...
Erhard Henkes schrieb:
Benötigt man hier movl oder geht auch mov? Ist asm volatile notwendig?
Bei Befehlen, bei denen der Assembler erkennen kann, wie breit die Operanden sind, kann man l (steht für long) auch weglassen. Ich persönlich mach das nicht, weil defensive Programmierung usw.
Dank der AT&T Syntax sicherlich. Dafür hat es den Vorteil, dass man den Code im gleichen Modul betrachten kann. In obigem Fall sicher nicht so kritisch, da sprechende Funktionen.
In PrettyOS haben wir eine Mischung aus beidem, so dass der geneigte Leser/Entwickler selbst entscheiden kann, wie er es gerne hätte.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 13:48:33 18.04.2009, insgesamt 1-mal bearbeitet
Dank der AT&T Syntax sicherlich. Dafür hat es den Vorteil, dass man den Code im gleichen Modul betrachten kann.
Für mich nicht wegen der AT&T Syntax, sondern wegen diesem gcc-spezifischen Inline und der ganzen Schar von Regeln, die man dabei beachten muss. Diese c-Datei ist nun auf den gcc-Compiler festgenagelt, was ja der Philosophie der C-Sprache, im Sinne höhere Sprache, plattformunabhängig, portabel usw., widerspricht...
Momentan kämpfe ich auf Basis des leider blutleeren Tutorials und mit Grub Multiboot Daten und festen Parametern durchzogenen Codes von James Molloy mit diesen wirklich interessanten aber auch komplexen Themen:
- Paging (läuft sehr gut, fehlt noch die richtige Visualisierungsidee für die Pages),
- Heap (läuft, RAM Disk landet z.B. dort),
- Virtual File System (tolles Feature, weiß noch nicht, wie ich die Daten von HD oder Floppy in die zu erzeugende RAM Disk bringen soll, aber da wird mir noch etwas einfallen),
- Multitasking / Kernel & User Mode/ System calls (komplex, aber interessant)
... und suche nach einem vernünftigen didaktischem Konzept und guten Ideen der "Begreiflichmachung" dessen, was da vor und hinter den "Mauern" (PM) des Kernels vor sich geht. Das ist ein größeres Problem als ich dachte. Ich verzichte darauf, die Probleme detailliert darzustellen, wühle zunächst weiter. Wenn mich jemand konkret unterstützen will, kann er mich gerne kontaktieren.
Ich bin froh, dass ich diese konkrete Grundlage von JM (auf Basis des alten Bran's Tut) gefunden habe. Sobald die von mir modifizierten Module transparent, verständlich(!) und korrekt zusammen arbeiten, werde ich das Ganze hier zur Diskussion stellen und parallel im Tutorial verarbeiten. Kann aber leider dauern, da ich momentan didaktisch noch(!) keine klare Linie sehe, also bitte Geduld oder konkrete Mithilfe.
Was mir am meisten fehlt, ist eine standardisierte Diagnose-Funktion für die Memory-Darstellung. Vielleicht muss ich da meine "ODA" noch mit ein paar Daten auf Stand halten, die ich dann bei Bedarf loggen kann. Hauptproblem sind momentan page faults.
Das mit dem FIFO (20 Keys) bei den Tasten-Infos hat ja sehr gut geklappt. Danke für den Vorschlag. Die exakte Auswertung der Scans kann man später noch ausgefeilt aufbauen, wenn die Anwendungen (z.B. eine Shell) diese wirklich brauchen.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 10:19:52 19.04.2009, insgesamt 8-mal bearbeitet
Hier könnte ich noch Hilfestellung brauchen:
"Which tool could you recommend (MS Windows) for transfering files/data from external mediums to the memory before or after the boot process?"
Vielleicht denke ich da auch verkehrt herum. Bisher habe ich ja noch keine Einrichtung im OS für einen File-Transfer. Wäre hilfreich, weil man da einige Demo-Daten in die "RAM Disk" schieben könnte. Hat aber noch Zeit, über das eigene Filesystem muss man hierbei ja auch vertieft nachdenken.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Das hier ist z.B. die "niedliche" Umschaltung auf User Mode. Da hinten dran wühle ich gerade herum. Jetzt fehlen noch brauchbare syscalls usw. Kämpfe noch mit page faults, weil der user nicht einfach auf kernel code zugreifen darf. PM schlägt voll zu (schön!). Endlich passt alles zusammen.
// Set up a stack structure for switching to user mode (ring 3 => RPL = 0x3)
// 0x200 is IF flag (enabling interrupts)
// 0x20|0x3=0x23 // 0x18|0x3=0x1B
// $1F means "the address of the next label '1:', searching forward"
// User Mode: asm volatile(" \
cli; \
mov $0x23, %ax; \
mov %ax, %ds; \
mov %ax, %es; \
mov %ax, %fs; \
mov %ax, %gs; \
\
mov %esp, %eax; \
pushl $0x23; \
pushl %esp; \
pushf; \
pop %eax; \
or $0x200, %eax; \
push %eax; \
pushl $0x1B; \
push $1f; \
iret; \
1: \
");
Danach hat man allerdings keinen direkten Zugriff mehr auf den eigenen Code in ckernel.c! Wenn man nach dem Umschalten in den user mode nicht per "page fault" ausgehebelt werden will, muss man dem user sozusagen Supervisor-Rechte geben, funktioniert voll!
User als Supervisor:
settextcolor(2,0); printformat("switch to user mode\n"); settextcolor(15,0);
switch_to_user_mode();
settextcolor(2,0); printformat("behind switch to user mode with ring 0 supervisor privilege!\n"); settextcolor(15,0);
settextcolor(2,0); printformat("switch to user mode\n"); settextcolor(15,0);
switch_to_user_mode();
settextcolor(2,0); printformat("behind switch to user mode with ring 0 supervisor privilege!\n"); settextcolor(15,0);
settextcolor(2,0); printformat("switch to user mode\n"); settextcolor(15,0);
switch_to_user_mode();
settextcolor(2,0); printformat("behind switch to user mode with ring 0 supervisor privilege!\n"); settextcolor(15,0);
Fragen:
- Welche Privilegstufen sollte man einrichten? Nur 0 und 3, oder auch 1 und 2?
--- Kernel (0) und User (3) ist klar, wozu sollte man den Rest nutzen?
- Wie würdet ihr die "sys calls" (call to the kernel) praktisch einrichten?
--- software interrupt (linux: 0x80), ich würde 0x7F bevorzugen wegen der unsigned char Begrenzung in unserem OS?
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 13:10:30 19.04.2009, insgesamt 2-mal bearbeitet
Danke für den Link! Was hältst Du von der Software-Interrupt-Methode (software interrupt bei Linux: 0x80), ich würde allerdings 0x7F bevorzugen wegen der aktuellen Begrenzung auf 0..127 in unserem OS?
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Was hältst Du von der Software-Interrupt-Methode ...
geht im prinzip auch, aber sysenter ist schneller. diese software-interrupt methode funzt ausserdem sogar auf 368'ern. 'sysenter' gibt's ja erst ab pentium-ähnlichen und späteren intel- und AMD-prozessoren. aber ich bin nicht so der x86-freak, warten wir doch mal ab, was nobuo dazu zu erzählen hat.
btw: bei syscalls musste extrem drauf achten, dass du keine backdoors einbaust. das problemchen hatten z.b. die ersten versionen von win-nt 4, die man mit zufallsparametern in syscalls crashen, oder sehr einfach code in den kernel einschleusen konnte. daher auch der imageverlust von windoofs, an dem mickrigsoft bis heute zu knabbern hat.
Na, klasse! Bereits die erste Sicherheitsdiskussion. Ist das z.B. dieses Thema:
Zitat:
One thing that is important to note is that the kernel, when executing interrupt handling code, requires a valid stack to work with. If it doesn't have one, the processor will double fault (and then eventually triple fault because the double fault handler needs a valid stack too!). This would obviously be a very easy way for a malicious user to bring down your system, so it is normal practice to, on mode change from ring 3 to ring 0, switch to a new stack designed solely for use by the kernel, and which is guaranteed to be valid.
'sysenter' gibt's ja erst ab pentium-ähnlichen und späteren intel- und AMD-prozessoren.
Eines meiner Ziele ist, dass PrettyOS ab 80386 lauffähig ist, denn die späteren CPU haben an der prinzipiellen inneren Technik (GDT, IDT, ...) fest gehalten. Daher auch die komplizierte Umschaltung von einem Gate auf das andere, die bei modernen Pentiums hoffentlich leichter geht.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 14:56:18 19.04.2009, insgesamt 2-mal bearbeitet
also ich würde euch empfehlen, das ganze über interrupts zu machen. da gibts dann später bei multitasking keine probleme, da bei einem interrupt autamtisch das i-flag in den eflags gelöscht wird, sodass der kernel nicht unterbrochen werden wird, bis er zumindest einen stack gewechselt hat bzw. bis ihr das halt wieder zulasst... Aber nachdem das ja ein Sprung in den Kernel ist, könnte es sein, dass dann zwei Tasks gleichzeitig springen und im Kernel dann Datenstrukturen zerstört werden, weil das nicht synchron geschieht.
Aber das ist nur meine bescheidene Meinung
Die Interrupt-Technik habe ich bereits vorbereitet (0x7F = 127).
Falls jemand den aktuellen Sourcecode mal anschauen / testen / weiter entwicklen möchte, hier eine hoffentlich stabile Zwischen-Version (ohne sys calls, also user mode auf Privileg 0 gesetzt. Einfach mal in gtd.c und task.c auf Privileg 3 umstellen, dann gibt es einen error. PM wirkt also! Didaktisch sehr schön.):
http://www.henkessoft.de/OS_Dev/Downloads/30_prettyOS.zip
testwise implemented: paging, heap, virtual file system & ram disk, multitasking, move of stack, switch to another gate (based on tutorial/code of James Molloy)
path forward: real user mode (ring 3) & sys calls, safety against malicious code
Danke an alle, die mich bisher soweit getragen haben.
Jetzt habe ich soviel OS-Blut geleckt, dass ich keinesfalls aufhöre.
Diesem Assembler-Forum möchte ich für seine konstruktive und offene Haltung ein ganz großes Lob aussprechen. Das findet man sehr selten!
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 15:45:09 19.04.2009, insgesamt 2-mal bearbeitet
also ich würde euch empfehlen, das ganze über interrupts zu machen. da gibts dann später bei multitasking keine probleme, da bei einem interrupt autamtisch das i-flag in den eflags gelöscht wird, sodass der kernel nicht unterbrochen werden wird...
interrupts global zu sperren (macht man das mit dem i-flag?) ist, wenn nicht unbedingt erforderlich, eigentlich keine gute idee, weil dann interrupt-anforderungen verloren gehen können, wenn die sperre zu häufig eingesetzt wird oder lange dauert. und ich weiss nicht, ob die pc-architektur 'nested' interrupts und interrupt-prioritäten kennt (ich glaub' die hardware unterstützt es nicht, aber es geht mit software-tricks).
blitzmaster schrieb:
Aber nachdem das ja ein Sprung in den Kernel ist, könnte es sein, dass dann zwei Tasks gleichzeitig springen und im Kernel dann Datenstrukturen zerstört werden, weil das nicht synchron geschieht.
in einem multitasking-os sollten systemfunktionen sowieso immer reentrant-fähig sein. dass mehrere task gleichzeitig im kernel rumschwirren, oder gar die selbe systemfunktion nutzen, ist keine seltenheit.
in einem multitasking-os sollten systemfunktionen sowieso immer reentrant-fähig sein. dass mehrere task gleichzeitig im kernel rumschwirren, oder gar die selbe systemfunktion nutzen, ist keine seltenheit.
Klingt wirklich herausfordernd, erinnert mich an multithreading. Da lauern neben "malicious codes" bestimmt schon die "deadlocks".
Zitat:
interrupt-prioritäten
Je kleiner die Zahl, je höher die Priorität, zumindest habe ich es so gelesen. Also Timer > Keyboard > ...
Hat eigentlich jemand einen Tipp, wie ich "Files" - ohne GRUB - möglichst einfach in diese RAM disk tranferieren kann, so dass man den VFS-Mechanismus mal vorführen könnte?
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 15:54:15 19.04.2009, insgesamt 1-mal bearbeitet
also ich würde euch empfehlen, das ganze über interrupts zu machen. da gibts dann später bei multitasking keine probleme, da bei einem interrupt autamtisch das i-flag in den eflags gelöscht wird, sodass der kernel nicht unterbrochen werden wird...
interrupts global zu sperren (macht man das mit dem i-flag?) ist, wenn nicht unbedingt erforderlich, eigentlich keine gute idee, weil dann interrupt-anforderungen verloren gehen können, wenn die sperre zu häufig eingesetzt wird oder lange dauert. und ich weiss nicht, ob die pc-architektur 'nested' interrupts und interrupt-prioritäten kennt (ich glaub' die hardware unterstützt es nicht, aber es geht mit software-tricks).
Jo, das IF ignoriert praktisch die IRQ-Line der CPU (nicht aber NMIs, exceptions, usw.).
AFAIR: Selbst der PIC (programmable Interrupt Controller) in den ersten PCs kannte schon eine Prioritaetsbehandlung von IRQs. Das Ding wurde dann zunaechst in typischer x86-manier erweitert, indem man quasi den gleichen Controller nochmal hinten dran geklatscht hat (toll: jetzt mit 16 IRQs!!) und inzwischen gibt es den in die CPU integrierten APIC.
IAR geht also nichts verloren, wenn das IF geloescht wird ... es sei denn, das bleibt ueber Sekunden so (daher auch meine Ermahnung, schnelle IRQ-handler zu schreiben).
Dennoch hast du recht: IRQs sollte man nur ausschalten, wenn unbedingt noetig, was bei x86ern mit ihren ganzen atomaren spezial-Befehlen im Prinzip relativ selten vorkommen duerfte.
+fricky schrieb:
blitzmaster schrieb:
Aber nachdem das ja ein Sprung in den Kernel ist, könnte es sein, dass dann zwei Tasks gleichzeitig springen und im Kernel dann Datenstrukturen zerstört werden, weil das nicht synchron geschieht.
in einem multitasking-os sollten systemfunktionen sowieso immer reentrant-fähig sein. dass mehrere task gleichzeitig im kernel rumschwirren, oder gar die selbe systemfunktion nutzen, ist keine seltenheit.
Richtig. Da kann man entweder alle moeglichen Teile des Kerns einzeln in kritische Sektionen packen, oder man tuetet den ganzen Kern einfach in eben eine solche. Zu Demonstrationszwecken im Rahmen eines Tutorials waere das IMHO uebersichtlicher. Dabei kann man dann auch problemlos IRQs zulassen.
Int loescht uebrigens nicht das IF. AFAIR sysenter genau so wenig.
Die Entscheidung gegen sysenter und fuer int befuerworte ich uebrigens (wie ich schon sagte) : Kein neuer Spezial-Befehl, Gebastel mit AMD vs. Intel, einfache Handhabung, abwaertskompatibel. KISS halt.
BTW ist die Benutzung eines eigenen Stacks fuer Kern-Aufrufe (auch IRQ- und Exception-Handler) wie gesagt dringend zu empfehlen. Praktisch fast wie ein TASK-Switch.
in einem multitasking-os sollten systemfunktionen sowieso immer reentrant-fähig sein. dass mehrere task gleichzeitig im kernel rumschwirren, oder gar die selbe systemfunktion nutzen, ist keine seltenheit.
Klingt wirklich herausfordernd, erinnert mich an multithreading.
isses auch. multithreading und -tasking bedeutet, je nach kontext, mal was anderes, mal das selbe. windows-user verstehen unter multitasking, dass gleichzeitig mehrere prozesse möglich sind, von denen jeder mehrere threads (==parallel ausführbare einheiten) haben kann. in der RTOS-terminologie wird das, was unter win 'thread' heisst, oft 'task' oder 'prozess' genannt.
Nobuo T schrieb:
BTW ist die Benutzung eines eigenen Stacks fuer Kern-Aufrufe (auch IRQ- und Exception-Handler) wie gesagt dringend zu empfehlen. Praktisch fast wie ein TASK-Switch.
dann haste aber ein ziemlich schwergewichtiges interface zwischen kernel und userland, das syscalls relativ unperformant macht. eigentlich braucht man doch nur einen privilegwechsel beim sprung in den kernel und bei der rückkehr. natürlich musste noch checken, ob die parameter gültig sind, genug platz auf dem stack ist, pointer sinnvollen inhalt haben und ähnliche kleinigkeiten. einen kompletten, task-switch ähnlichen mechanismus, halte ich für übertrieben. aber vielleicht geht's mit den x86 software-int befehlen nur so (weshalb später 'sysenter' eingeführt wurde).
Die system calls laufen. Man kann also nun z.B. die Funktion puts(...) als syscall_puts(...) aufrufen. Ich benötige mal eure Hilfe, da ich momentan etwas nicht verstehe und nicht sicher bin, ob das genau so richtig ist oder ob im OS etwas nicht stimmt (was mir wahrscheinlicher erscheint).
switch to user mode
After set_kernel_stack. tss_entry.esp0: 0000083Ch
Right before user mode
>>>IRQ 127<<<Hello, user world!
behind switch to user mode with ring 0 supervisor privilege!
SS: 00000010h, ESP: 0018FFE0h, EBP: 0018FFF0h, CS: 00000018h, DS: 00000020h
Das "Hello, user world!" wird mittels syscall_puts(...) ausgegeben. Daher weiß ich, dass die system calls nun funktionieren. Baut man auf diese Weise ein API auf? (habe das bisher noch nie gemacht )
C/C++ Code:
switch_to_user_mode();
syscall_puts("Hello, user world!\n");
C/C++ Code:
switch_to_user_mode();
syscall_puts("Hello, user world!\n");
C/C++ Code:
switch_to_user_mode();
syscall_puts("Hello, user world!\n");
Im zweiten Fall:
Zitat:
switch to user mode
After set_kernel_stack. tss_entry.esp0: 0000083Ch
Right before user mode
Page Fault
>>>Exception. System Halted! <<<
Ich bin etwas irritiert darüber, dass im echten User Mode der Syscall (ring 3 ruft ring 0) vor dem Page Fault (wegen unerlaubtem Zugriff auf Kernel code) nicht durchgegangen ist. Vielleicht liegt es daran, dass auf dem Weg zur Ausführung des Syscalls (... -> IRQ127 -> syscall_handler -> ...) bereits ein "Page Fault" erfolgte.
Sorry, dass der Code im IRQ-Bereich momentan etwas "gestöpselt" ist. Das muss ich noch aufräumen, wollte jetzt nur erst mal das Tasking und die Syscalls zum Laufen bekommen, denn die Schnittstelle zwischen Kernel und Anwendungen im User Mode sind ja genau das Interessante.
Wenn ich das so nebeneinander sehe, wundere ich mich gerade über die Leerzeile zwischen Right before user mode und >>>IRQ 127<<<Hello, user world!.
Die nächste Aufgabe ist jetzt, den vorhandenen Code vernünftig zu überarbeiten, damit er eine wirklich brauchbare Basis für ein OS ergibt, möchte nicht völlig chaotisch weiter hetzen, weil dann die Didaktik und vor allem das Tutorial auf der Strecke bleibt. Schlechten Code erklärt und zeigt man nicht gerne, sondern ist froh, wenn er läuft. Damit Experimentieren ist zumeist auch nicht gerade lustig. Vielleicht, wenn man die Module noch ordentlich sortiert und hier und da entkoppelt, kann das noch etwas Sinnvolles werden?
Vielleicht kann sich ein Assembler-Spezialist die IRQ/ISR/exception-Story mal anschauen (z.Z. eine wilde Mischung aus ASM und C). Da blicke ich kaum noch selbst durch. Den int127 (128 ging nicht wegen Überlauf, muss ja auch nicht sein, wäre nur wegen Linux 0x80) habe ich schon an den fault_handler, der eigentlich nur für exceptions gedacht war, "provisorisch angeschraubt", ging irgendwie nicht anders. Aber so ist wahrscheinlich auch Windows und Linux entstanden.
Inzwischen habe ich gehörig Respekt vor der OS-Entwicklung.
... und das Schwierigste liegt ja eher noch vor mir.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 21:45:35 19.04.2009, insgesamt 4-mal bearbeitet
Den int127 (128 ging nicht wegen Überlauf, muss ja auch nicht sein, wäre nur wegen Linux 0x80) habe ich schon an den fault_handler, der eigentlich nur für exceptions gedacht war, "provisorisch angeschraubt", ging irgendwie nicht anders.
page faults und fehler wie access violations sind sich sehr ähnlich. kann es sein, dass beim x86 beides sowieso über den selben handler gehen muss? wenn ja, musste irgendwie unterscheiden, was was ist.
Erhard Henkes schrieb:
Aber so ist wahrscheinlich auch Windows und Linux entstanden.
naja, was win angeht, so hat mickrigweich dereinst die halbe crew von DEC abgeworben, die VMS entwickelt hat. die ur-version von windoof-nt wurde ursprünglich für irgend so'nen RISC prozessor gebaut. ich kann mir gut vorstellen, dass die m$-programmierer, bei der portierung nach x86, auch mal den einen oder anderen schrei- und weinkrampf hatten.
Erhard Henkes schrieb:
Inzwischen habe ich gehörig Respekt vor der OS-Entwicklung.
jetzt erst? hättest mal weniger copy-and-paste von dem engländer machen sollen.
hättest mal weniger copy-and-paste von dem engländer machen sollen.
James hat mir schriftlich freie Hand gelassen. Ich bin weder Student noch in einem Wettrennen, sondern bearbeite dieses Thema einzig und allein, weil es mich interessiert und weil ich anderen die Materie - soweit ich das schaffe - verständlich machen will. Noch steht der Code von James Molloy, der auf älterem Code von Bran's Tutorial aufbaut, nicht in meinem Tutorial. Ich lasse mir Zeit, bis ich weiß, was wirklich wichtig ist. Die Diskussion hier und teilweise auch in anderen Foren hilft mir dabei.
Gut finde ich bisher,
- dass nicht der GRUB Loader verwendet wird
- den sauberen Übergang von Assembler zum C-Kernel (thanks to Nobuo T)
- die Bitset-Verwaltung beim Paging (effizient)
- das VFS analog Linux (leider noch nicht richtig in Aktion)
Schlecht finde ich
- das Interrupt-Gerödel (richtig zusammen geschustert, da verwende ich sogar noch den alten Aufbau, weil James das auch nicht wirklich auf die Reihe bekam, ist jedoch im Wesentlichen Fleißarbeit, das zu ordnen)
- die AT&T Syntax innerhalb C (ziemlich unverständlich, das gehört wirklich alles als Intel Syntax ausgelagert, da hat abc.w Recht)
Unsicher bin ich mir bei
- der Heap-Erzeugung/-Verwaltung (schwieriges aber langweiliges Thema, Hauptsache läuft erstmal)
- der Tasking-Steuerung
Das wird aber alles noch richtig gut, da bin ich ganz sicher.
Ich musste für mich schnell Zusammenhänge praktisch durchspielen, daher der Rückgriff auf Vorhandenes, um zu sehen, was wirklich voneinander abhängt. Übrigens geht es anderen auch so. Selbst, wenn ich den Sourcecode alleine erfinde, bringt das niemand weiter. Für andere ist der Code immer "fremd". Was zählt, sind gute Erklärungen. Da sieht es in James Molloy's Tutorial allerdings düster aus.
Was mir bisher noch weitgehend fehlt, ist die Darstellung der Arbeitsweise des OS. Ich möchte kein "unsichtbares" OS, sondern einen "offenen Wecker". Da habe ich bisher überhaupt noch keinen Ansatz gefunden, das ist bei vielen "Educational OS" lauter in sich geschlossenes unverständliches "Gefrickel".
Ich habe in einigen Foren sogar das Gefühl, das sich die OSDEVer gegenseitig nicht wirklich verstehen.
Es gibt übrigens noch einige offene Fragen, auf die bisher niemand eingegangen ist.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 00:06:25 20.04.2009, insgesamt 2-mal bearbeitet
page faults und fehler wie access violations sind sich sehr ähnlich. kann es sein, dass beim x86 beides sowieso über den selben handler gehen muss? wenn ja, musste irgendwie unterscheiden, was was ist.
So sieht momentan ein typischer Bildschirmausdruck zur Überprüfung aus:
Zitat:
GDT, IDT, ISRS, IRQ, timer, keyboard install
paging install
frames: 01000000h NFRAMES: 524288
kernel_directory->physicalAddr: 01006000h
placement_address: 01013000h number of allocated frames: 4371
HEAP start: 40081000h end: 40100000h max: 4FFFF000h kernel: 0 read-only: 0
hole 40081000h hole-size: 520192
frames: 01000000h NFRAMES: 524288
kernel_directory->physicalAddr: 01006000h
placement_address: 01014020h number of allocated frames: 4371
tasking install
after moving stack, ESP: 0018FFD0h
After init first task (kernel task), placement_address: 01014020h
After k_mallocing kernel_stack), placement_address: 01014020h
VFS & RAM Disk install
placement_address: 40090000h
fs_root->name: initrd fs_root->ptr: 0
switch to user mode
After set_kernel_stack. tss_entry.esp0: 0000083Ch
Right before user mode
>>>IRQ 127<<<
Hello, user world!
behind switch to user mode with ring 0 supervisor privilege!
SS: 00000010h, ESP: 0018FFE0h, EBP: 0018FFF0h, CS: 00000018h, DS: 00000020h
Bezüglich Visualisierung des Pagings habe ich mal Folgendes probiert:
Um das Bitset mit seinen Nullen und Einsen und den Bezug zu den Speicheradressen darzustellen, habe ich die unten stehende Funktion entworfen. Sie bleibt 'sec' Sekunden stehen, wenn eine oder mehrere Einsen (allokierte Pages) auftauchen, bei Nullen flitzt sie durch. Damit kann man recht praktisch den Speicher scannen und sich in Bochs mit copy von der jeweils interessanten Region screenshots machen:
Diese Funktion geht sämtliche Pages durch und stellt jeweils 32 in einer Zeile je nach Allokation als Null oder Eins dar. Vorne steht die erste Hex-Speicheradresse der ersten Page (also jeweils 4KB * 32 = 128KB in einer Reihe).
Bsp.: Erstes Ende der kompakten Allokierung oberhalb 18 MB (1200000h) :
// The error code gives us details of what happened. int present = !(r->err_code & 0x1); // Page not present int rw = r->err_code & 0x2; // Write operation? int us = r->err_code & 0x4; // Processor was in user-mode? int reserved = r->err_code & 0x8; // Overwritten CPU-reserved bits of page entry? int id = r->err_code & 0x10; // Caused by an instruction fetch?
// Output an error message.
printformat("Page Fault (");
if (present) printformat("page not present");
if (rw) printformat("read-only - write operation");
if (us) printformat("user-mode");
if (reserved) printformat("overwritten CPU-reserved bits of page entry");
if (id) printformat("caused by an instruction fetch");
printformat(") at %x - EIP: %x\n", faulting_address, r->eip);
}
// The error code gives us details of what happened. int present = !(r->err_code & 0x1); // Page not present int rw = r->err_code & 0x2; // Write operation? int us = r->err_code & 0x4; // Processor was in user-mode? int reserved = r->err_code & 0x8; // Overwritten CPU-reserved bits of page entry? int id = r->err_code & 0x10; // Caused by an instruction fetch?
// Output an error message.
printformat("Page Fault (");
if (present) printformat("page not present");
if (rw) printformat("read-only - write operation");
if (us) printformat("user-mode");
if (reserved) printformat("overwritten CPU-reserved bits of page entry");
if (id) printformat("caused by an instruction fetch");
printformat(") at %x - EIP: %x\n", faulting_address, r->eip);
}
// The error code gives us details of what happened. int present = !(r->err_code & 0x1); // Page not present int rw = r->err_code & 0x2; // Write operation? int us = r->err_code & 0x4; // Processor was in user-mode? int reserved = r->err_code & 0x8; // Overwritten CPU-reserved bits of page entry? int id = r->err_code & 0x10; // Caused by an instruction fetch?
// Output an error message.
printformat("Page Fault (");
if (present) printformat("page not present");
if (rw) printformat("read-only - write operation");
if (us) printformat("user-mode");
if (reserved) printformat("overwritten CPU-reserved bits of page entry");
if (id) printformat("caused by an instruction fetch");
printformat(") at %x - EIP: %x\n", faulting_address, r->eip);
}
Der Page Fault wurde provoziert durch Streichen des Extra-Speichers oberhalb der placement_address:
C/C++ Code:
1 2 3 4 5 6 7 8 9 10
1 2 3 4 5 6 7 8 9 10
// Allocate a little bit extra so the kernel heap can be initialised properly.
// map (phys addr <---> virt addr) from 0x0 to the end of used memory
ULONG counter=0;
i=0;
while( i < placement_address /*+ 0x100000*/ ) //important to add more!
{
alloc_frame( get_page(i, 1, kernel_directory), 0, 0);
i += PAGESIZE;
++counter;
}
C/C++ Code:
1 2 3 4 5 6 7 8 9 10
// Allocate a little bit extra so the kernel heap can be initialised properly.
// map (phys addr <---> virt addr) from 0x0 to the end of used memory
ULONG counter=0;
i=0;
while( i < placement_address /*+ 0x100000*/ ) //important to add more!
{
alloc_frame( get_page(i, 1, kernel_directory), 0, 0);
i += PAGESIZE;
++counter;
}
C/C++ Code:
1 2 3 4 5 6 7 8 9 10
// Allocate a little bit extra so the kernel heap can be initialised properly.
// map (phys addr <---> virt addr) from 0x0 to the end of used memory
ULONG counter=0;
i=0;
while( i < placement_address /*+ 0x100000*/ ) //important to add more!
{
alloc_frame( get_page(i, 1, kernel_directory), 0, 0);
i += PAGESIZE;
++counter;
}
Dadurch kann man heraus finden, wieviel Speicher allokiert werden muss, um den Kernel Heap zu initialisieren. Endlich mal eine Aufgabe für die Leser.
Antwort: Bei 0x0 0der 0x1000 erfolgt ein Page Fault. Die Info lautet:
Zitat:
Page Fault (page not present) at 01014004h - EIP: 0000AD36h
Über die Info 01014000h 00000000h weiß man, dass nur bis 01013000h frames allokiert wurden. Daher muss man mindestens 0x1001 addieren, welches dann auf 0x2000 "aligned" wird. Dann klappt das.
So langsam bekomme ich das Thema Paging didaktisch in den Griff. Jetzt fehlt eigentlich nur noch die Darstellung des nicht-linearen Mappings.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 23:01:32 22.04.2009, insgesamt 2-mal bearbeitet
Für das Laden von vorgefertigten File-Daten in den Speicher für die Ram Disk des VFS hat mir jemand einen möglichen Weg gewiesen, nämlich via NASM-Anweisung incbin und Linken mit ld. Diesen Befehl kannte ich bisher noch nicht.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
int retValFork1 = fork(); //create a new process in a new address space which is a clone of this int retValGetPid1 = getpid();
printformat("fork() returned: %x and getpid() returned: %x\n", retValFork1, retValGetPid1);
//...
task_switch();
printformat("getpid() %x\n", getpid());
sleepSeconds(3);
task_switch();
//...
C/C++ Code:
1 2 3 4 5 6 7 8 9
int retValFork1 = fork(); //create a new process in a new address space which is a clone of this int retValGetPid1 = getpid();
printformat("fork() returned: %x and getpid() returned: %x\n", retValFork1, retValGetPid1);
//...
task_switch();
printformat("getpid() %x\n", getpid());
sleepSeconds(3);
task_switch();
//...
C/C++ Code:
1 2 3 4 5 6 7 8 9
int retValFork1 = fork(); //create a new process in a new address space which is a clone of this int retValGetPid1 = getpid();
printformat("fork() returned: %x and getpid() returned: %x\n", retValFork1, retValGetPid1);
//...
task_switch();
printformat("getpid() %x\n", getpid());
sleepSeconds(3);
task_switch();
//...
Den Scheduler kann man dann über den Timer aufrufen.
Interessant ist, wenn man die Zeile task_switch(); settextcolor(getpid(),0); an eine andere Stelle in der while-Schleife setzt, dann gibt es einen eindrucksvollen "General protection fault":
http://en.wikipedia.org/wiki/General_protection_fault#Switching
Zitat:
Faults can occur in the task state segment (TSS) structure when:
* switching to a busy task during a call or jump instruction
* switching to an available task during an interrupt return (IRET) instruction
* using a segment selector on a switch pointing to a TSS descriptor in the LDT
Nun muss ich mich noch mit dem Fileformat, dem Transfer in die RAM Disk und dem Zugriff im Programm via Virtual File System beschäftigen. Sobald das alles klappt und die Didaktik passt, werde ich stepwise am Tutorial weiter schreiben (verflixt viel Material und Background) und ein Basis Modell für die Weiterentwicklung von PrettyOS stabilisieren.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 00:32:06 25.04.2009, insgesamt 4-mal bearbeitet
Nun muss ich mich noch mit dem Fileformat, dem Transfer in die RAM Disk und dem Zugriff im Programm via Virtual File System beschäftigen. Sobald das alles klappt und die Didaktik passt, werde ich stepwise am Tutorial weiter schreiben (verflixt viel Material und Background) und ein Basis Modell für die Weiterentwicklung von PrettyOS stabilisieren.
nun wird es auch an der zeit sein, dir grundsätzliche überlegungen zu machen, ob du dabeibleiben oder abweichen solltest.
nur ein kleines beispiel: ich bekomme als spielstandslader oder compilerbauer vom bs (win oder linux hier echt gleich) per filemapping daten hereingeschubst. Shade nannnte es dieser datenhaltung diesbezüglich afair NonCopyStrings, ich splitte bei zum beispiel immer bei \n und habe "zeilen", die aus struct{char *begin,*end} bestehen. das bs gibt mir einen bei read() z.B. 8K großen sektor un ich gebe ihn zurück, wenn ich ihn nicht mehr brauche. natürlich kann ich als old-style-lib ein kopierendes read() im userspace anbieten. vielleicht wäre ein no-copy-read verflixt viel schneller, wenn die anwendung das neue paradigma verwendet. in dieser richtung isses nicht nervig. andersrum schon: ich habe viele strings, ich habe ausschließlich stings mit begin/end in meiner anwendung (naja, c++ halt) und muß bei jedem syscall erstmal speicher für nullterminierten string anlegen, würg.
vielleicht wäre das an sich gar kein problem, wenn man strings von vorn herein mit begin/end auch im kernel schon angedacht hätte.
vielleicht wäre das ganze startup/shuutdown-geschmuse gar nicht notwendig, wenn ein prozess==programm leben würde, sobald es installiert ist und runterfahren/hochfahren nur betriebssystem-kacke wäre, die die anwendungen gar nicht sehen.
nur mal so als meinen kleinen einwurf. es könnte möglich sein, daß anscheinend voll verrückte konzepte aus versehen zugleich dir voll arbeit sparen, das BS schneller machen und für den anwendungsprogrammierer auch noch netter sind.
nur ein kleines beispiel: ich bekomme als spielstandslader oder compilerbauer vom bs (win oder linux hier echt gleich) per filemapping daten hereingeschubst. Shade nannnte es dieser datenhaltung diesbezüglich afair NonCopyStrings, ich splitte bei zum beispiel immer bei \n und habe "zeilen", die aus struct{char *begin,*end} bestehen. das bs gibt mir einen bei read() z.B. 8K großen sektor un ich gebe ihn zurück, wenn ich ihn nicht mehr brauche.
hört sich gut an. eine proprietäre high-speed datenschnittstelle zwischen OS und user-mode hat vorteile, z.b. wenn irgendwann netzwerkprotokolle, filesystems, etc. dran sind, die dann bequem im userspace laufen könnten, was der stabilität des system zugute kommen würde. knackpunkt ist aber das zurückgeben der buffer ans system, es wäre ja doof, wenn die anwendung das vergisst und der kernel irgendwann 'out of memory' ist.
volkard schrieb:
hihi, ich möchte nicht in deinen schuhen stecken.
ich auch nicht, aber erhard hat durchhaltevermögen und auf den kopf gefallen ist er auch nicht.
deswegen sollteste frühzeitig das ganze in überschaubare teilkomponenten zerlegen
Genau dies werde ich versuchen, soweit das möglich ist. Paging, Heap, Task Switching, User Mode vs Kernel Mode, Syscalls, VFS sind solche Einheiten, die allerdings zum einfachen Begreifen teilweise zu groß sind. Da versuche ich gerade, Unterelemente als elementare Module weiter heraus zu schneiden.
Noch wichtiger ist die Visualisierung / Verständlichmachung dessen, was passiert, wenn man dies oder jenes ändert, weil viele Leute beim Verstehen und Begreifen eher praktisch an die Dinge heran gehen.
Ich denke gerade über diesen so lässig dahin geworfenen Begriff nach:
Processes read from and write to the file view using pointers, just as they would with dynamically allocated memory. The use of file mapping improves efficiency because the file resides on disk, but the file view resides in memory. ...
Der Schwachpunkt von PrettyOS besteht momentan noch darin, dass noch kein Fileformat festgelegt wurde (jedoch kein Problem) und noch kein Mechanismus zum Übertragen von "Files" (Laden/Lesen Schreiben/Speichern), z.B. aus der RAM Disk in den Speicher und umgekehrt existiert. Sobald diese Mechanismen funktionieren, komme ich auf dieses Thema "proprietäre high-speed Datenschnittstelle zwischen OS und User Mode" zurück. Es geht nichts verloren. Wie sehen die entsprechenden Pendants bei Linux und Windows genau aus, damit ich das im Detail studieren kann?
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 11:30:52 25.04.2009, insgesamt 1-mal bearbeitet
@+fricky: Danke für den Link!
Momentan interessiert mich dies hier am meisten, da ich davon bisher nur einen Bruchteil habe, und mir genau der Rest fehlt: http://www.i.u-tokyo.ac.jp/edu/training/ss/lecture/new-documents/Lectures/02-VirtualMemory/VirtualMemory.pdf
PrettyOS muss aber nicht wirklich performant sein, sondern vor allem verständlich, eben als Experimentier-Plattform geeignet. Ich bin mir noch nicht sicher, ob mir das gelingt, hängt davon ab, inwieweit mir geeignete Visualisierungs-Funktionen für die einzelnen Module und Exceptions gelingen.
Gibt es eine Übersicht, wie man weitere Infos aus Registern erhält, um Exceptions möglichst vollständig zu analysieren? Dann könnte man diese Funktion, die bisher nur Page Faults genauer unter die Lupe nimmt, weiter ausbauen:
// The error code gives us details of what happened. int present = !(r->err_code & 0x1); // Page not present int rw = r->err_code & 0x2; // Write operation? int us = r->err_code & 0x4; // Processor was in user-mode? int reserved = r->err_code & 0x8; // Overwritten CPU-reserved bits of page entry? int id = r->err_code & 0x10; // Caused by an instruction fetch?
// Output an error message.
//...
}
printformat("%s >>> Exception. System Halted! <<<", exception_messages[r->int_no]);
for (;;); // currently STOP!
}
// The error code gives us details of what happened. int present = !(r->err_code & 0x1); // Page not present int rw = r->err_code & 0x2; // Write operation? int us = r->err_code & 0x4; // Processor was in user-mode? int reserved = r->err_code & 0x8; // Overwritten CPU-reserved bits of page entry? int id = r->err_code & 0x10; // Caused by an instruction fetch?
// Output an error message.
//...
}
printformat("%s >>> Exception. System Halted! <<<", exception_messages[r->int_no]);
for (;;); // currently STOP!
}
// The error code gives us details of what happened. int present = !(r->err_code & 0x1); // Page not present int rw = r->err_code & 0x2; // Write operation? int us = r->err_code & 0x4; // Processor was in user-mode? int reserved = r->err_code & 0x8; // Overwritten CPU-reserved bits of page entry? int id = r->err_code & 0x10; // Caused by an instruction fetch?
// Output an error message.
//...
}
printformat("%s >>> Exception. System Halted! <<<", exception_messages[r->int_no]);
for (;;); // currently STOP!
}
r->err_code und das Register cr2 dürften wohl der Schlüssel dazu sein. Wenn man beim "Spielen" mit dem Code in ckernel.c auf eine Exception trifft, sollte man heraus finden können, was man eigentlich falsch gemacht hat. Das ist momentan noch nicht ausreichend gegeben. Das ist aus meiner Sicht noch das Hauptproblem.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 13:15:35 25.04.2009, insgesamt 3-mal bearbeitet
Try to create some code for installing fault handlers, so each fault handler does have its own function. Check if a custom handler is installed, and execute it. If not, write down a message on the screen and halt CPU (cli, hlt, for(;
Keeping them apart this way like, is very handy for dynamically installing and removing handlers. Also, when having a Virtual86Mode implemented, you will need #GP (general protection fault) very often, and write some big routines for it.
for the Debug and Break exception you can write some nice simple handler. Just dump all registers given as argument in function call.
The Page Fault handler:
Receive the CR3 AND CR2. CR2 contains faulting address. CR3 the PHYSICAL address of the page directory. Then, split the faulting address in some parts.
PageDirectoryIndex (cr2 >> 22, when using 4kb pages on a 32-bit system)
PageTableIndex ((cr2 >> 12) & 0x3ff)
PageOffset ((cr2 & 0xfff), not necessary for now)
Then, when page was not present, check the PDE for present bit, then the existence of the PageTable (PDE->frame << 12 and then to virtual), then present of PTE, and then existence of the page mapped to the PTE. (PTE->frame != 0x0)
Solve any of these things, but keep patience: be sure caller should be able to write or read something at the address in cr2.
When having r/w (read/write) page fault and is present, kill the process. It is trying to access kernel space. Same way with u/s (user/supervisor).
When looking at the Intel Manual, we see that there is NO ID bit in the error code of a #PF (page fault).
Reserved, if problems with that, kill process. (or maybe just continuing is fine too)
I will look at this topic often to help here
I am busy with kmalloc.
The memory management part is the most boring, tricky and frustrating part of OS development.Don't quit :P
Also: be sure you always print out messages of exceptions, especially at the page fault exception. There shouldn't be ANY exceptions occur when programming kernel itself.
There shouldn't be ANY exceptions occur when programming kernel itself.
even if the kernel is 100% bug free, third-party modules (device drivers and similar stuff running in kernel space) can crash the system. erhard should implement an exception handling mechanism, so that common errors (like wild pointers, divison by zero, etc.) are handled properly.
so
int a = 5/0;
a will be zero, instead of killing process.
no, that keeps the error. the error creeps forward and will cause weird behaviour of the whole thing. instead, exceptions should be 'handled' in a controlled manner (like this):
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13
1 2 3 4 5 6 7 8 9 10 11 12 13
...
begin_guarded_section();
{
...
int a = x/0;
...
}
if (get_exception_code() == DIV_BY_ZERO) // enter exception handler
{
// deal with division by 0
}
end_guarded_section(); // stop guarding
... // process will be killed at this point, if another exceptions occur
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13
...
begin_guarded_section();
{
...
int a = x/0;
...
}
if (get_exception_code() == DIV_BY_ZERO) // enter exception handler
{
// deal with division by 0
}
end_guarded_section(); // stop guarding
... // process will be killed at this point, if another exceptions occur
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13
...
begin_guarded_section();
{
...
int a = x/0;
...
}
if (get_exception_code() == DIV_BY_ZERO) // enter exception handler
{
// deal with division by 0
}
end_guarded_section(); // stop guarding
... // process will be killed at this point, if another exceptions occur
^^all can be done using c-functions. there's no need for a modfied compiler (like that is the case with structured exception handling under m$-windoofs).
of course, kernel must handle that, but must not terminate the application if an exception occurs inside guarded code. after that, the application should ask the kernel for exception code and (if there is one), retry the operation, notify the user, abort the program or something like that.
Yes, exception handling has to be fine-tuned in PrettyOS. Currently, there is only a message and a big stop sign. Page faults are already analyzed deeper.
I worked on the VFS and the RAM disk, because it is very important to bring data, specialized kernel modules or user code into the memory of the OS. The way how it works now is as follows:
1) produce a binary image from various files (with "make_initrd.exe", source code: "make_initrd.c"). I renamed the resulting image to "file_data.dat". This is the target for incbin in process.asm.
2a) include it in process.asm with incbin, global addresses:
Code:
; data for ramdisk
global _file_data_start
global _file_data_end
_file_data_start:
incbin "file_data.dat"
_file_data_end:
Code:
; data for ramdisk
global _file_data_start
global _file_data_end
_file_data_start:
incbin "file_data.dat"
_file_data_end:
Code:
; data for ramdisk
global _file_data_start
global _file_data_end
_file_data_start:
incbin "file_data.dat"
_file_data_end:
2b) Linker transfers it to the memory at &file_data_start
Currently the framework for booting, loading asm kernel, switch to PM and C-kernel, interrupts, write/read key queue, set system timer frequency (I use 100 Hz), paging, heap, virtual file system, RAM disk, multitasking, syscalls now basically work.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 02:43:15 26.04.2009, insgesamt 2-mal bearbeitet
Aha, jetzt wird's interessanter und praxisbezogener...
Die letzten beiden Wochen habe ich mich - zumindest grundlegend - um die komplexen Themen Memory-, Task- und File-Management (VFS, RAM Disk) sowie Syscalls gekümmert, um zu sehen, wie man dies am besten aufbauen und darstellen könnte. Manchmal sucht man auch nur "ewig" nach einem Fehler im Sourcecode. Hierbei behebt man dann zunächst noch alle möglichen anderen Baustellen, die man bei genauerem Hinsehen als nicht korrekt aufspürt. Bei mir liegen die "üblen" Fehler immer im Zeiger-Bereich. Kleines Beispiel:
Mit k_memshow(...) sah ich zufrieden, dass die "eingelinkten" Daten gefunden und sauber an den Anfang der RAM Disk kopiert wurden.
Das Schlimme ist, dass durch die Pointer-Arithmetik zu wenige Daten kopiert wurden. Über die Blödsinnigkeit dieses Fehlers (es gibt nur wenige geniale Fehler) möchte ich hier nicht reden, so etwas passiert eben ab und zu, wenn man den cast auf die Schnelle vergisst. Dieses Wechselspiel zwischen Speicheradresse und Darstellung als unsigned long integer (ULONG) ist eine der Schwächen des Systems. Zwei kleine Casts nach ULONG, und schon funktionierte es perfekt:
Das Schlimme ist, dass man den Fehler an völlig anderer Stelle - nämlich den neu eingebauten Mechanismen - sucht. Auslöser ist, dass man große Speicherbereiche nicht auf einfache Weise wie bei einem Hex-Editor durchscrollen kann. Mir ist der Fehler erst aufgefallen, als ich mich mit k_memshow(...) auf die konkrete Stelle im Speicher gesetzt und dort nur Nullen gefunden habe.
Solche Erfahrungen sind in einem Tutorial wichtig und sollten auch dargestellt werden, damit andere an solche Trivialitäten denken, wenn Sie Fehler suchen. Denn das "Aufgeben" kommt oft daher, dass man Fehler nicht findet, keine geeigneten Vorbilder hat und/oder in einem Forum (http://forum.osdev.org/ ist ein Musterbeispiel) arrogant behandelt wird.
Momentan schreibe ich an dem Tutorial noch nicht weiter, weil die grundlegenden Mechanismen zwar funktionieren, ich aber mit der Visualisierung noch nicht zufrieden bin.
Im Tasking-Bereich ist mir bisher nur die Darstellung der PID als Farbe beim "Tippen" (Schreiben/Lesen KeyQueue) eingefallen.
Vor dem Heap-Gebäude stehe ich noch etwas zögerlich bezüglich Darstellung der "blocks and holes". Man hat im Textmode mit 80*25 mit wenigen brauchbaren Farben auch nicht allzu viele Möglichkeiten.
Didaktische Ideen sind daher immer willkommen.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 14:07:09 26.04.2009, insgesamt 3-mal bearbeitet
There is the function task_switch of PrettyOS: 1 -> 2 -> 3 -> ... -> N -> 1 ->...
This could be triggered by the system timer according to allowed timeslices, but not done until now. I tried it out, but got General Protection Fault. You can't switch at each time. Thus, the question is how to avoid and switch on the right time?
//the scheduler: void schedule(int irq)
{
ELEMENT* proz;
if(!(queue_empty(&roundrobin_prozesse))&&p->prozess_timetorun<1)
{
p->prozess_timetorun=10;//refill timeslice.
//remove process from the head of the queue.
proz=remove_first_element_from_queue(&roundrobin_prozesse,0);
//put the element to the rear of the queue.
add_element_to_queue_rear(&roundrobin_prozesse,proz);
}
//pick the process at the head of any queue. (f. ex. round robin queue)
//and put it in p.
choose_prozess(irq);
}
//the scheduler: void schedule(int irq)
{
ELEMENT* proz;
if(!(queue_empty(&roundrobin_prozesse))&&p->prozess_timetorun<1)
{
p->prozess_timetorun=10;//refill timeslice.
//remove process from the head of the queue.
proz=remove_first_element_from_queue(&roundrobin_prozesse,0);
//put the element to the rear of the queue.
add_element_to_queue_rear(&roundrobin_prozesse,proz);
}
//pick the process at the head of any queue. (f. ex. round robin queue)
//and put it in p.
choose_prozess(irq);
}
//the scheduler: void schedule(int irq)
{
ELEMENT* proz;
if(!(queue_empty(&roundrobin_prozesse))&&p->prozess_timetorun<1)
{
p->prozess_timetorun=10;//refill timeslice.
//remove process from the head of the queue.
proz=remove_first_element_from_queue(&roundrobin_prozesse,0);
//put the element to the rear of the queue.
add_element_to_queue_rear(&roundrobin_prozesse,proz);
}
//pick the process at the head of any queue. (f. ex. round robin queue)
//and put it in p.
choose_prozess(irq);
}
What do you think about it?
How to prohibit general protection faults by the task switch?
Zitat:
And did you make your filesystem manager usable for programmers, who want to make a filesystem driver like Ext2, Ext3, FAT etc?
Not yet, but it should be possible by the VFS.
Currently I use a very primitive filesystem, only magic number, name and content (initrd.h/initrd.c):
C/C++ Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
#include "os.h"
#include "fs.h" // Virtual File System (UNIX idea: everything is a file)
typedef struct {
ULONG nfiles; // The number of files in the ramdisk.
} initrd_header_t;
typedef struct {
ULONG magic; // Magic number, for error checking.
CHAR name[64]; // Filename.
ULONG off; // Offset in the initrd that the file starts.
ULONG length; // Length of the file.
} initrd_file_header_t;
// Installs the initial ramdisk. It gets passed the address, and returns a completed filesystem node.
fs_node_t* install_initrd(ULONG location);
C/C++ Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
#include "os.h"
#include "fs.h" // Virtual File System (UNIX idea: everything is a file)
typedef struct {
ULONG nfiles; // The number of files in the ramdisk.
} initrd_header_t;
typedef struct {
ULONG magic; // Magic number, for error checking.
CHAR name[64]; // Filename.
ULONG off; // Offset in the initrd that the file starts.
ULONG length; // Length of the file.
} initrd_file_header_t;
// Installs the initial ramdisk. It gets passed the address, and returns a completed filesystem node.
fs_node_t* install_initrd(ULONG location);
C/C++ Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
#include "os.h"
#include "fs.h" // Virtual File System (UNIX idea: everything is a file)
typedef struct {
ULONG nfiles; // The number of files in the ramdisk.
} initrd_header_t;
typedef struct {
ULONG magic; // Magic number, for error checking.
CHAR name[64]; // Filename.
ULONG off; // Offset in the initrd that the file starts.
ULONG length; // Length of the file.
} initrd_file_header_t;
// Installs the initial ramdisk. It gets passed the address, and returns a completed filesystem node.
fs_node_t* install_initrd(ULONG location);
I was very glad, when it worked.
Looking at the path forward, I think the next milestones could be:
- loading executables (file format?)
- kernel modules
- shell
How do you / will you load executables? (one of the text files in the RAM disk of the last example could have been an executable file (elf, com, exe, ...) ). What's the best way to start it?
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 18:35:28 26.04.2009, insgesamt 12-mal bearbeitet
I recognise some JamesMolloy code in your multitasker :P
I will see if I find something. Try to use cli(); at start of the handler. And keep patience of the order you set the registers.
Loading executables. I will start with having possibility to load more executable-types. First, ELF. You got to read out the header, and copy-on-write the data to user-process memory. Allocate a stack, and don't forget to make space for .bss section. (should be filled with zero's). Just read ELF specifications and start simple. (just the data, bss, text sections).
When loading executable, you are in Ring0, you are in the address space you will load the executable in. When done loading, jump to ring 3 and start at the beginning of the executable.
I haven't been working on executables. But this is what I think it should work.
Success!
// PHPnerd
_________________ Being Dutch. Talking English. On a German community.
I did, but the GPF didn't leave. In between I was told that C might be not perfect for the task switch. The above presented function for task_switch does not save all registers. That might be the real problem in some situations.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 20:55:23 26.04.2009, insgesamt 1-mal bearbeitet
I checked task switch code with my old, its the same (diff is what I added, like threads and states). I put a cli() just after very first statement. (if(!task)).
And where exactly (line) does the #GP trigger? (just try putting chars like ! on as much as lines as possible, and count numbeer of printed when running)
// PHPnerd
_________________ Being Dutch. Talking English. On a German community.
You really do? Dont be so sure. What does the ASM do? Yes, it switches task, it chnages page directory, and the instrcution pointer after that code, we continue the task, and not the code of the scheduler.
Or am i wrong, and is the ! showed at the other switchs?
// PHP
_________________ Being Dutch. Talking English. On a German community.
In between I was told that C might be not perfect for the task switch. The above presented function for task_switch does not save all registers. That might be the real problem in some situations.
that's true. even if most c-compilers have support for interrupt routines, you can't access cpu registers directly in C (only few compilers for embedded systems support that). you have to code this very low level stuff in assembly. all real life RTOS (although portable among many cpus), with preemptive schedulers, use asm for the heart of the task switcher.
Are you using Bochs? I hope so, do following for me:
put cli, hlt, for(;; before the big assembly part. Shutdown, and open the bochsout file. Put the register info in here. Btw: remove any calls to the scheduler and call it the way it will fault. So it will give us the right values.
// PHPnerd
_________________ Being Dutch. Talking English. On a German community.
You see that task_switch() will never be called? you declare a variable, does 1 time incrmeent, and checks for 200?
///////////
Ok, good.
Try the same, but than in the assbly part itself, each time one line, until the GPF occurs. then we know which opcode makes the fault. (Maybe it is just the jump).
If that fails, try getting all values of the registers when GPF triggers (print out all registers etc from the Registers given at the handler and post them, and maybe a disassembly of your schedule() (so we can exactly see when it trigger according to RIP's. We cant put cli hlt etc into the assembly part
// PHPnerd
_________________ Being Dutch. Talking English. On a German community.
Jinix
Zuletzt bearbeitet von PHPnerd am 23:44:39 26.04.2009, insgesamt 1-mal bearbeitet
ebx and edx were accepted, edi and esi gave failure ("can't find a register in class `GENERAL_REGS' while reloading `asm'").
2) I accelerated the task switch:
Just, remove the ! and # and task switch printouts :P
Do you have a keyboard handler? Try to make a simple console system. Use the Function keys to switch to another console. Each task is linked to a console. When switching console, you copy the data from 0xb8000 to the data of current console. And write data of new console to 0xb8000.
Something like that will work.
Then, you will see on each console the output of each task apart nice for testing your multitasker.
About the GP: The code of JamesM tutorials uses the structure registers. Filled in some assebly code. Use that structure to printout eip, esp etc, and get the control registers and print them out too. Error flag meanings can be found in the Intel Manual Volume 1.
// PHPnerd
_________________ Being Dutch. Talking English. On a German community.
Jinix
Zuletzt bearbeitet von PHPnerd am 01:25:38 27.04.2009, insgesamt 2-mal bearbeitet
Try to make a simple console system. Use the Function keys to switch to another console. Each task is linked to a console.
This sounds good. I have a keyboard handler and an own circular key queue. A console in your concept is an array of 4000 Bytes which is swapped with the video RAM by the task switcher or by function keys? Not bad.
Zitat:
Well done!
Now I have exchanged the GPF for Page Faults:
Zitat:
paging install
frames: 00300000h NFRAMES: 524288
kernel_directory->physicalAddr: 00306000h
placement_address: 0030B000h allocated frames: 795
HEAP start: 40081000h end: 40100000h max: 4FFFF000h kernel mode: 0 read-only: 0
hole start: 40081000h end: 40100000h max: 4FFFF000h kernel mode: 0 read-only: 0
hole 40081000h hole-size: 0007F000h
after create_heap: placement_address: 0030C020h allocated frames: 795
tasking install
after moving stack, ESP: 0018FFD0h
After init first task (kernel task), placement_address: 0030C020h
After k_mallocing kernel_stack), placement_address: 0030C020h kernel_stack: 4010
1000h
VFS & RAM Disk install
placement_address after ram disk install: 40200000h
ramdisk_start: 40081000h file_data_start: 0000A2E0h file_data_end: 0000B652h
After set_kernel_stack ==> tss_entry.esp0: 40101800h
>>> IRQ 127 <<<
Hello, user world!
Found file dev
(directory)
Found file f1
contents: "PrettyOS: My filename is test1.txt!"
age Fault (page not present) at 0F000123h - EIP: 0F000123h
Page Fault >>> Exception. System Halted! <<<
Dependent on the value of c in the timer handler the page fault comes earlier or later. With c=2000 (means 20 sec) it came with the fisrt task switch:
Zitat:
ksldfgjskldfgjldfksjgklsdfgklsdfjgklsdfjglksdfjglksdfjgsklgjklsd;jglkdsfjglkdsfg
jlsk;dfjgskldfjgslkdjglk;sdgjldk;sfjglk;sdfjgl;ksdjgkl;sdgjklsdfgjsl;dkjglsdk;fg
js 0000D794h H: 0000D794h WRITE: 156 Read: 156 *T: g 103 *H: g 103
T: 0000D78Eh H: 0000D78Eh WRITE: 162 Read: 162 *T: f 102 *H: f 102
Page Fault (page not present) at 0040FC20h - EIP: 0040FC20h
Page Fault >>> Exception. System Halted! <<<
...
Hence, I think the EIP gets a wrong value under certain circumstances.
The function read_eip() is defined in Assembler (process.asm):
Code:
global _read_eip
_read_eip:
pop eax ; Get the return address
jmp eax ; Return. Can't use RET because return
; address popped off the stack.
Code:
global _read_eip
_read_eip:
pop eax ; Get the return address
jmp eax ; Return. Can't use RET because return
; address popped off the stack.
Code:
global _read_eip
_read_eip:
pop eax ; Get the return address
jmp eax ; Return. Can't use RET because return
; address popped off the stack.
Zitat:
About the GP: The code of JamesM tutorials uses the structure registers. Filled in some assebly code. Use that structure to printout eip, esp etc, and get the control registers and print them out too. Error flag meanings can be found in the Intel Manual Volume 1.
Thanks, great idea.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 02:03:40 27.04.2009, insgesamt 2-mal bearbeitet
When switching tasks, you change the registers, and the cr3. When a task does something with a device, and there is not good code implemented to handle multitasking in device drivers, the device will move data. But when in that time a task switch occurs, some problems may come. The task switch at 20seconds is very bad. The process doesn't need to know it is EVER switched. 100Hz may be nice (100 switches each second). Play something with it.
Consoles: you got it
C/C++ Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
typedef struct
{
uint32_t id; // console id, just for making some things easier
uint8_t keystroke[KEYSTROKE_LENGTH]; // make this the way you did with keystrokes
uint32_t *videoBuffer; // a pointer to some place in memory, size: width*height*2 bytes (probably 80*25*2)
uint32_t cursorX;
uint32_t cursorY; // coordinates of the cursor for this console
uint8_t colorAttribute; // attributes curently used by console
} console_t
struct task
{
....
console_t *console; // put console in task
....
};
C/C++ Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
typedef struct
{
uint32_t id; // console id, just for making some things easier
uint8_t keystroke[KEYSTROKE_LENGTH]; // make this the way you did with keystrokes
uint32_t *videoBuffer; // a pointer to some place in memory, size: width*height*2 bytes (probably 80*25*2)
uint32_t cursorX;
uint32_t cursorY; // coordinates of the cursor for this console
uint8_t colorAttribute; // attributes curently used by console
} console_t
struct task
{
....
console_t *console; // put console in task
....
};
C/C++ Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
typedef struct
{
uint32_t id; // console id, just for making some things easier
uint8_t keystroke[KEYSTROKE_LENGTH]; // make this the way you did with keystrokes
uint32_t *videoBuffer; // a pointer to some place in memory, size: width*height*2 bytes (probably 80*25*2)
uint32_t cursorX;
uint32_t cursorY; // coordinates of the cursor for this console
uint8_t colorAttribute; // attributes curently used by console
} console_t
struct task
{
....
console_t *console; // put console in task
....
};
That is an example for a console structure. Be sure of:
- Create one console when initializing screen (VGA), set it into a global variable, currentConsole
- Be sure the functions work to the right console. Each time a task switch occurs, it should use another console structure. But not the currentConsole, because that is the console currently SHOWED. En when not watching task 2, it should be able to print text, so the user can see it later.
You can determine the console of the current task by just using currentTask->console(->cursorX, ->cursorY etc.)
It is simple, but much work. Also be sure to make a backup from all you have first, it is a lot of work to remove the console code from your VGA driver.
Good luck, I think it will work with this.
// PHPnerd
_________________ Being Dutch. Talking English. On a German community.
Jinix
Zuletzt bearbeitet von PHPnerd am 12:08:39 27.04.2009, insgesamt 2-mal bearbeitet
Ich habe mit HLT im asm-code probiert, der page fault erfolgt beim jmp *%%ecx;
Was bedeutet eigentlich dieser * vor %%ecx ? Wenn ich ihn weglasse, passiert genau das gleiche.
Ich habe eip,esp,ebp,cur_dir->phys direkt vor dem asm-code geloggt:
task_switch before asm: eip: 0000D306h esp: 0018FF3Ch ebp: 0018FF54h cur_dir->phys: 00001000h
Page Fault (page not present) at 0F000123h - EIP: 0F000123h
Schalten nach 1 sec: (dann wurden zumindest die neuen Tasks angelegt)
Zitat:
task_switch before asm: eip: 0000D4C2h esp: 0018FFD8h ebp: 0018FFF0h cur_dir->phys: 0041E000h
Page Fault (page not present) at 0040FCA0h - EIP: 0040FCA0h
Wenn ich das richtig sehe, liegt das Problem eher bei current_directory->physicalAddr als bei eip, oder?.
Im Lowlevel Forum meinte man dazu, dass das Problem vom C-Compiler kommen könnte (nicht alle register wurden gespeichert und zurück geschrieben), und man empfahl mir das komplett in Assembler zu machen analog tyndur.
Ohne die Clobber List kommt übrigens wieder der GPF:
task_switch before asm: eip: 0000D4C2h esp: 0018FFD8h ebp: 0018FFF0h cur_dir->phys: 0041E000h
General Protection Fault >>> Exception. System Halted! <<<
Erst, wenn ich sowohl ebx als auch edx "clobbere", kommt der PF, einzeln GPF. edi und esi kann ich leider nicht clobbern wegen Fehlermeldung des Compilers.
DJ Delorie: The clobber list tells gcc which registers (or memory) are changed by the asm, but not listed as an output. That way, gcc knows not to leave a value in there and expect it to be the same after the asm. If you clobber enough registers, gcc has to start saving values on the stack instead of in unclobbered registers. ... You can overlap clobbers and *input* registers.
Das kommt mir inzwischen alles suspekt vor, was gcc da schafft. Mit dem Clobbern habe ich wenig Erfahrung.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 04:36:03 28.04.2009, insgesamt 6-mal bearbeitet
Are you sure that the currentDir->physical is physical?
hmmm, I have to look deeper into that. EDIT:
C/C++ Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
// This structure defines a 'task' - a process. typedef struct task
{
int id; // Process ID.
ULONG esp, ebp; // Stack and base pointers.
ULONG eip; // Instruction pointer.
page_directory_t* page_directory; // Page directory.
ULONG kernel_stack; // Kernel stack location. struct task* next; // The next task in a linked list.
} task_t;
// This structure defines a 'task' - a process. typedef struct task
{
int id; // Process ID.
ULONG esp, ebp; // Stack and base pointers.
ULONG eip; // Instruction pointer.
page_directory_t* page_directory; // Page directory.
ULONG kernel_stack; // Kernel stack location. struct task* next; // The next task in a linked list.
} task_t;
// This structure defines a 'task' - a process. typedef struct task
{
int id; // Process ID.
ULONG esp, ebp; // Stack and base pointers.
ULONG eip; // Instruction pointer.
page_directory_t* page_directory; // Page directory.
ULONG kernel_stack; // Kernel stack location. struct task* next; // The next task in a linked list.
} task_t;
And are the new page tables, and page directories you made, page aligned?
That is an interesting question! I use task_switch() with clobbering ebx and edx leading to the page fault. The new tasks have just been generated by fork():
Alles verflixt komplex. Ich weiß hier nicht genau, was ich mit "phys" anfangen soll. 0x1 ist sicher kein brauchbarer Wert. Warum beeinflusst das EIP???
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 21:06:57 28.04.2009, insgesamt 5-mal bearbeitet
Ich habe nun noch eine Variable für die physikalische Adresse in fork() eingefügt, um diese dort auszugeben:
C/C++ Code:
// Create a new process.
ULONG phys;
task_t* new_task = (task_t*) k_malloc( sizeof(task_t),0,&phys );
printformat("placement_address after creating a new process: %x phys: %x\n", placement_address, phys);
C/C++ Code:
// Create a new process.
ULONG phys;
task_t* new_task = (task_t*) k_malloc( sizeof(task_t),0,&phys );
printformat("placement_address after creating a new process: %x phys: %x\n", placement_address, phys);
C/C++ Code:
// Create a new process.
ULONG phys;
task_t* new_task = (task_t*) k_malloc( sizeof(task_t),0,&phys );
printformat("placement_address after creating a new process: %x phys: %x\n", placement_address, phys);
Zitat:
VFS & RAM Disk install
placement_address after ram disk install: 40200000h
ramdisk_start: 40081000h file_data_start: 0000A2E0h file_data_end: 0000B652h
After set_kernel_stack ==> tss_entry.esp0: 40101800h
>>> IRQ 127 <<<
Hello, user world!
...
placement_address after creating a new process: 40200000h phys: 0041CBE8h
fork() returned: 00000002h and getpid() returned: 00000001h
placement_address after creating a new process: 40200000h phys: 0041CC18h
fork() returned: 00000003h and getpid() returned: 00000001h
placement_address after creating a new process: 40200000h phys: 0041CC48h
fork() returned: 00000004h and getpid() returned: 00000001h
SS: 00000010h, ESP: 0018FFE0h, EBP: 0018FFF0h, CS: 00000018h, DS: 00000020h
task_switch before asm: eip: 0000D4CFh esp: 0018FFD8h ebp: 0018FFF0h cur_dir->ph
ys: 0041E000h
Page Fault (page not present) at 0040FCC0h - EIP: 0040FCC0h
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
size = size (doh)
align = page aligned, this makes your address page aligned, usable for paging structures and TSS (and others)
phys = get the physical address. You can put NULL in it, or a pointer to a variable. Like:
Getting physical address and doing nothing with it won't solve your problem.
The task doesn't have to be page aligned, since it is Software Multitasking, and you made it your own (nah, JamesM did :P)
The kmalloc code is 100% fine. (I used it my own some time ago)
// PHPnerd
_________________ Being Dutch. Talking English. On a German community.
Yes, the k_malloc(...) of James Molloy (I have his written permission to use his source code as a base for own experiments in an OS tutorial, if I like, but I am not sure, if his code is really robust enough) is usable and is not the problem (Other people think, JM has put too much into paging.h/paging.c, because phys./virt. memory management should be separated clearly. This can be done easily.).
OK, alignment is not important. I was not quite sure about it.
Currently, I do not understand the difference in the result of EIP between cases 1) and 3). In 3) I just save the physical address at 0x01 or a variable phys. The reason must be in the way of saving details of the tasks. Hence, the clobbering can influence the deficient process.
I think, I should go back to the test code where the task switch really works and analyze the effects by looking at all the details again. I just did and the page fault came, too. Thus, the failure has another reason. That's good.
As soon as I have analyzed it, I will come back.
@PHPnerd: Thanks for staying with me in times of confusion.
I use the following "stable version":
ckernel.c (like main.c):
kjsdfhsdkjksdjfhksdjfhsdkjfhksdjfhksdjfhsdkjfhsdkjfhksdjfhkjsdfhksjdhfß
T: 0000D80Fh H: 0000D80Fh WRITE: 66 Read: 66 *T: d 100 *H: d 100rnel_stack: 4010
task_switch before asm: eip: 0000D303h esp: 0018FFD8h ebp: 0018FFF0h cur_dir->ph
ys: 00426000hk install
placement_address after ram disk install: 40200000h
ramdisk_start: 40081000h file_data_start: 0000A2C0h file_data_end: 0000B632h
After set_kernel_stack ==> tss_entry.esp0: 40101800h
>>> IRQ 127 <<<
Hello, user world!
Found file dev
(directory)
Found file f1
contents: "PrettyOS: My filename is test1.txt!"
Found file f2
contents: "PrettyOS: My filename is test2.txt!"
Found file f3
contents: "PrettyOS: My filename is test3.txt!"
placement_address after creating a new process: 40200000h phys: 0041CBE8h
fork() returned: 00000002h and getpid() returned: 00000001h
placement_address after creating a new process: 40200000h phys: 0041CC18h
fork() returned: 00000003h and getpid() returned: 00000001h
placement_address after creating a new process: 40200000h phys: 0041CC48h
fork() returned: 00000004h and getpid() returned: 00000001h
SS: 00000010h, ESP: 0018FFE0h, EBP: 0018FFF0h, CS: 00000018h, DS: 00000020h
While entering keystrokes, the task is switched after every sixth keystroke.
The logs of 'task_switch before asm:' with the original and 3 forked tasks are:
Only in the while loop at the above shown location. I think that the failure is at another place, perhaps concerning kernel_stack of the task. I come back to that.
EDIT: One failure was in fork(): now new_task->kernel_stack instead of current_task->...
I log the processes with its data, the first task looks strange:
To my mind, the crucial point is that the source code stays transparent.
There was a create_heap(...) inside of paging_install(). I have taken it out, because the heap is more complicated to visualize by printformat(...), and process' stacks were created in the heap!
Now there is a new exception, if you put the task_switch() to practice inside the time_handler (k_m_nonheap means "pushing" placement_address, no heap):
task_switch before asm: cur_task->p_dir: 00412000h task_switch before asm:
eip: 0000D379h esp: 0018FFD8h ebp: 0018FFF0h cur_dir->phys: 00413000h PID: 2
Invalid Opcode >>> Exception. System Halted! <<<
Wie kann man den "Invalid Opcode" besser auswerten? z.B. Invalid Opcode ... at address ...? Ich habe mir z.B. r->eip angeschaut. Das passt einfach nicht zu dem in task_t. Verflixt!
Tippt man vor dem Timer Handler schnell noch ein paar task switches, dann findet man:
Zitat:
klxdfgjkldfgjdfkgjkldfgjkldfgjkldfgjkldfjglkdfgjlkdfgjkldfjgkldfjglk
task_switch before asm: cur_task->p_dir: 0040E000h task_switch before asm: eip: 0000D1BBh esp: 0018FFD8h ebp: 0018FFF0h cur_dir->phys: 0040F000h PID: 10h ebp: 0018FFF0h cur
Invalid Opcode >>> Exception. System Halted! <<<
These should be the data direct after the asm-code in task switch.
address: 24Bh (eip) cannot be correct, if we enter with D4D9h.
The ss 2097144 = 1FFFF8h looks "kaput". It should be 16 (10h).
eflags: 10000001000000010b = 10202h
The whole task_management has to be looked thru.
JM's code in this area is not robust, it was only for short demos.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 08:18:24 01.05.2009, insgesamt 12-mal bearbeitet
Übrigens: Wenn ich pushfd / popfd verwenden will, macht mein oldtimer gcc (verwendet wegen aout Format von NASM um den Asemblercode einzubinden) das nicht mit:
Zitat:
c:/djgpp/tmp/cc5s5oYe.s: Assembler messages:
c:/djgpp/tmp/cc5s5oYe.s:237: Error: no such instruction: `pushfd'
c:/djgpp/tmp/cc5s5oYe.s:237: Error: no such instruction: `popfd'
kann also nur die FLAGS nicht die EFLAGS sichern.
EDIT: Ich denke inzwischen, dass JM's Sourcecode im Bereich (multi)tasking für PrettyOS einfach unbrauchbar ist.
Auf jeden Fall hat mir die Beschäftigung mit den Themen viel gebracht, bin nun auch oft genug gegen den Baum gefahren, um zu wissen, was etwas taugt und was einem bei Problemen nur noch den Rest gibt.
Daher mache ich nun einen kompletten "roll-back" und setze am Ende des Tutorials an. !!!Schnitt!!!
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 11:08:30 01.05.2009, insgesamt 4-mal bearbeitet
Zunächst mal einige Fragen an euch zu den Tools (nasm/gcc/ld):
1) Im Tutorial werden gcc 3.1 und ld 2.13 (im Rahmen von DJGPP, Download-Link von osdever.net empfohlen: http://www.osdever.net/downloads/compilers/DJGPP-Installer-nocpp.exe ) verwendet.
Einer der Gründe besteht darin, dass wir damit das aout-Format (Linux a.out object file) linken konnten. abc.w konnte dieses Problem, soweit ich es verstanden habe, auch nicht lösen, sodass wir dabei blieben. Mit dem gcc 3.1 kommt man allerdings immer suspekt daher. Ein Fehler könnte ja am Uralt-Compiler liegen.
Mögliche NASM output-Formate sind:
Zitat:
valid output formats for -f are (`*' denotes default):
* bin flat-form binary files (e.g. DOS .COM, .SYS)
aout Linux a.out object files
aoutb NetBSD/FreeBSD a.out object files
coff COFF (i386) object files (e.g. DJGPP for DOS)
elf32 ELF32 (i386) object files (e.g. Linux)
elf ELF (short name for ELF32)
elf64 ELF64 (x86_64) object files (e.g. Linux)
as86 Linux as86 (bin86 version 0.3) object files
obj MS-DOS 16-bit/32-bit OMF object files
win32 Microsoft Win32 (i386) object files
win64 Microsoft Win64 (x86-64) object files
rdf Relocatable Dynamic Object File Format v2.0
ieee IEEE-695 (LADsoft variant) object file format
macho NeXTstep/OpenStep/Rhapsody/Darwin/MacOS X object files
Verwendet man für die NASM outputs COFF bzw. WIN32, dann gibt es folgende nette Meldung:
kernel.asm: ... : error: COFF format does not support non-32-bit relocations
Soweit ich das verstanden habe, hängt dies an unserem eleganten Übergang von 16 Bit nach 32 Bit. Hier erhält man von "Fachleuten" den Rat, GRUB einzusetzen und sich auf den C-Kernel zu konzentrieren. Das widerspricht aber dem Geist des bisherigen Tutorials, das die Dinge bottom-up zeigen will. Ich habe außer diesem aout-Format bisher kein Format gefunden, dass sowohl 16- als auch 32-Bit verarbeitet und von ld zum restlichen C-Code gebunden wird. Gibt es ein modernes Format, das dies kann?
Ich bin nicht sicher, inweiweit dies ein echtes Problem oder nur Ansichtssache ist. GCC 3.1 stammt ja immerhin schon von May 15, 2002. Also so richtig uralt ist er nicht.
Warum wurde z.B. pushfd / popfd im asm-Code nicht akzeptiert? Das kann doch nicht am gcc-Baudatum 2002 liegen, den 386er asm-Set gibt es seit den 80ern.
2) zur AT&T-Syntax: %eax %%eax *%%eax
wo muss man genau was verwenden?
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 13:04:36 01.05.2009, insgesamt 1-mal bearbeitet
Auf jeden Fall hat mir die Beschäftigung mit den Themen viel gebracht, bin nun auch oft genug gegen den Baum gefahren, um zu wissen, was etwas taugt und was einem bei Problemen nur noch den Rest gibt.
meiner meinung nach hast du zu viele themen gleichzeitig bearbeitet. das liegt wohl auch daran, dass es keine richtige trennung zwischen den komponenten des 'referenzsystems' von JM gibt (vermutlich ist er ein opfer der verfrickelten x86-technologie geworden, die einem instabile software-konstrukte aufzwingt). dabei kann man virtual memory, multitasking, heaps und exceptions ziemlich unabhängig voneinander betrachten. aber egal, nur nicht aufgeben...
Den Mut habe ich keinesfalls verloren, ganz im Gegenteil! Man kann ein solches Tutorial keinesfalls schreiben, wenn man schon 20 verschiedene OS entwickelt hat, weil man sich dann für garnix mehr entscheiden kann und alles nur noch theoretisch/abstrakt sieht, aber auch nicht aus dem Nichts. Inzwischen verstehe ich, warum JM nicht weiter macht.
Zitat:
dabei kann man virtual memory, multitasking, heaps und exceptions ziemlich unabhängig voneinander betrachten
Ja, das stimmt. Es gibt daneben noch weitere Punkte, nämlich die Trennung des physikalischen und virtuellen MM, die Software-Interrupts, die Syscalls und die Ausführung von Programmen. Das VFS und die RAM-Disk gefallen mir gut von JM, wobei sein VFS noch nicht ausgefeilt ist, aber das kann man ja noch machen. Heap-Mechanismus funktioniert auch gut. Vielleicht liefert uns PHPnerd etwas Besseres, aber sein Ansatz klingt für mich gleich zu JM, aber ich bin kein Informatiker.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 14:40:59 01.05.2009, insgesamt 1-mal bearbeitet
Was mir wirklich Sorgen macht, ist, dass ich mit DJGPP arbeite, gilt als obsolet. leider haben wir 16-bit im Gepäck. Vielleicht kann man das auf 32-Bit abändern und auf COFF umsteigen.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 23:24:10 01.05.2009, insgesamt 2-mal bearbeitet
Wird es nicht mehr weiter entwickelt oder was?????
Ganz im Gegenteil, jetzt geht es erst richtig los, allerdings geordnet, so robust wie möglich und transparent, damit eigenes Experimentieren gelingt. Einfach dabei bleiben, oder noch besser, zusammen mit mir entwickeln. Ich werde nicht aufgeben. Das Thema OSDEV ist faszinierend. Ich werde ab jetzt aber jede einzelne Code-Zeile - egal ob fremd oder von mir selbst - auf die Goldwaage legen. Anders geht es nicht.
Es gibt bezüglich Teil 2 folgende technische Fragestellungen:
1) GRUB oder Tiny Bootloader?
2) DJGPP oder neuere gcc-ld-binutils-Toolchain
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 19:08:19 02.05.2009, insgesamt 3-mal bearbeitet
Let's all take the Intel Manuals, close the jamesMolloy website, and start by ourself. We just use GRUB to load, so we take the GRUB specifications too.
We read the GRUB specifications, and we find out how to make our starting OS multiboot able for GRUB. When done, we move to C, by just calling our C function.
Read Intel manuals, on IDT, GDT, ISR and IRQ's (exceptions), work out some code.
We already know howto use VGA in protected mode: writing to 0xb8000, write some nice code to make our life easier.
Then, go on to intel manuals again, and go starting writing your PMM and VMM. (Volume 3A, Protected Mode Memory Management)
Then, when all is working, do some research on internet about different algorithms of memory allocation (heap management). Write your own, without looking to others code.
Debug debug debug all you got. Try to get your friends starting it and doing weird things.
When done, Take Intel manuals and find the chapters about task management, but especially (when you want to use Software Multitasking) the chapter about the architecture itself. I think it is chapter 3 of Volume 1, not sure
Then go on writing some code to change the registers of the CPU. Just try to switch static tasks, then try make dynamic ones (so a task can start a new task).
Go on to write code for device drivers and file systems, write some drivers, and
support a nice filesystem (FAT32 for example). Then support executables, port newlib, compile the whole GCC packet. Run it on your OS, compile NASM. Write some simple nice text editor. Do not forget to make a command line interface for executing your commands.
Now you can make your OS on your OS. And you can take a drink.
When someone got so far, tip me, and i will tell you next steps (LOL, I will be back after some years and check the forum (just joking, will still be here)).
Success you all! When you want some details (tips), ask for them.
// PHPnerd
_________________ Being Dutch. Talking English. On a German community.
...but don't rely on too much intel crap. only the very low level stuff (which must be coded in asm, anyway) should be hooked on x86. separate cpu specific code from the rest of the system, so you can...
1. easily port your OS to another architecture
2. run and debug parts of the OS inside a test bench (perhaps under windoze)
2) zur AT&T-Syntax: %eax %%eax *%%eax
wo muss man genau was verwenden?
Man muss unterscheiden zwischen Assembler-Syntax für den GNU Assembler und dem Assembler-Inline Formalismus für den gcc Compiler.
Für den GNU Assembler gilt: Alle Register-Bezeichnungen, weil sie was besonderes sind, im Sinne, keine Variablen uns so was, bekommen ein % Zeichen. Man kann ruhig Variablen anlegen, die eax, ebx und sonst wie heissen, und auf diese Variablen zugreifen:
.section .text
_Helper:
movl %eax, eax # Speichere Inhalt des CPU-Registers EAX (%eax) in der Variable eax (eax)
movl %ebx, ebx # Speichere Inhalt des CPU-Registers EBX (%ebx) in der Variable ebx (ebx)
movl $eax, %eax # Lade Addresse der Variable eax ($eax) ins CPU-Register EAX (%eax)
movl $ecx, %ecx # Lade Anfangsadresse des Arrays ecx ($ecx) ins CPU-Register ECX (%ecx)
movl %eax, (%ecx) # Speichere Inhalt des CPU-Registers EAX (%eax) im Array-Element ecx[0]
movl %ebx, 4(%ecx) # Speichere Inhalt des CPU-Registers EBX (%ebx) im Array-Element ecx[1]
movl %ecx, 8(%ecx) # Speichere Inhalt des CPU-Registers ECX (%ecx) im Array-Element ecx[2]
ret
Bei indirekten Sprüngen ist es so, dass ein Sternchen benützt werden muss/soll:
Assembler Code:
_Sprung:
jmpl *1000 # Effektive Adresse 1000
jmpl *%eax # Effektive Adresse im CPU-Register EAX (%eax)
jmpl *4(%eax) # Effektive Adresse im CPU-Register EAX (%eax) und Displacement = 4
jmpl *(%eax, %ebx) # Basis im CPU-Register EAX (%eax), Index im CPU-Register EBX (%ebx)
jmpl *32(%eax, %ebx, 4) # Basis im CPU-Register EAX (%eax), Index im CPU-Register EBX, Skalierung = 4, Displacement = 32
ret
Assembler Code:
_Sprung:
jmpl *1000 # Effektive Adresse 1000
jmpl *%eax # Effektive Adresse im CPU-Register EAX (%eax)
jmpl *4(%eax) # Effektive Adresse im CPU-Register EAX (%eax) und Displacement = 4
jmpl *(%eax, %ebx) # Basis im CPU-Register EAX (%eax), Index im CPU-Register EBX (%ebx)
jmpl *32(%eax, %ebx, 4) # Basis im CPU-Register EAX (%eax), Index im CPU-Register EBX, Skalierung = 4, Displacement = 32
ret
Assembler Code:
_Sprung:
jmpl *1000 # Effektive Adresse 1000
jmpl *%eax # Effektive Adresse im CPU-Register EAX (%eax)
jmpl *4(%eax) # Effektive Adresse im CPU-Register EAX (%eax) und Displacement = 4
jmpl *(%eax, %ebx) # Basis im CPU-Register EAX (%eax), Index im CPU-Register EBX (%ebx)
jmpl *32(%eax, %ebx, 4) # Basis im CPU-Register EAX (%eax), Index im CPU-Register EBX, Skalierung = 4, Displacement = 32
ret
Vergisst man ein Sternchen zu setzen, kommt vom gas eine Warnung:
Zitat:
... Warning: indirect jmp without `*'
Warum das so ist, weiss ich ehrlich gesagt nicht, habe im Manual dazu nichts gefunden und habe ehrlich gesagt, nicht intensiv danach gesucht.
Bezüglich Assembler-Inline im C-Code kann ich leider nicht viel sagen. Ich weiss nur, dass man bei Registern zwei % Zeichen setzen muss. Ansonsten lässt sich, denke ich, obige Info auch so übernehmen. Und es gibt noch eine weitere Schar von Regeln für den Inline, die man kennen sollte...
Ich hoffe, ich konnte bisschen Licht auf die AT&T Syntax werfen (die ja zu unrecht verteufelt wird)...
Ich fange an, mich daran zu gewöhnen. Die C-Syntax ist ja auch nicht jedes Coders Lieblingsspeise.
Momentan kämpfe ich gerade mit dem Thema Paging und versuche die Dinge so weit wie möglich zu visualisieren.
Dabei habe ich eine Frage zum Alignment: Wenn die memory_placement_address nach einem k_malloc bei exakt 200000h liegt bei PAGESIZE = 1000h und man belegt nun weitere 2000h, wo muss es dann weiter gehen, bei 202000h oder 203000h? Der Algo, den ich von JM übernommen habe, macht momentan bei 203000h weiter, kommt mir verschwenderisch vor. Aber vielleicht habe ich da etwas noch nicht ganz verstanden.
Wenn ( placement_address == (placement_address & 0xFFFFF000) ) könnte man das Addieren der PAGESIZE doch sparen. Aufrunden muss man doch nur bei placement_address > (placement_address & 0xFFFFF000) ?
Beispiel:
placement_address startet mit 200000h. Wegen align=0 startet es auch genau dort. Bei align=1 hätte es bei 201000h begonnen. Ist das korrekt oder muss ich obige Abfrage auf die letzten 3 Hex-Nuller ergänzen?
The Intel manuals tells everything about the x86(-64) architecture, including paging. Why would you do that stuff in ASM? But that's you own choice, what I posted is how I would go work.
Open the WIndows calulcator, science mode, and do something in it like this. Just putting in some number, anding with 0x00000fff or 0xfffff000.
Something everybody here should know: each part of a hexidecimal number (0x12345678, 1 is a part, 2 a part, etc.) is 4 bits in length. so 0xfffff000 makes 12 bits zero
Success
// PHPnerd
_________________ Being Dutch. Talking English. On a German community.
Yeah, results the same, but my way will be lots faster. (some cycles, so some billions of a second). Mine is better to understand, i dont understand yours
// PHPnerd
_________________ Being Dutch. Talking English. On a German community.
Jinix
Zuletzt bearbeitet von PHPnerd am 20:31:28 02.05.2009, insgesamt 1-mal bearbeitet
I said it was better :P, I think you miss something with the objdump, because your code didnt actually do everything you wrote in C. (do a check on alignment, A == (A AND 0xfffff000) THEN A AND 0xfffff000, ADD A, 0x1000)
// PHPnerd
_________________ Being Dutch. Talking English. On a German community.
Please excuse. You were talking so much about Intel that I thought they have published it in there manual.
I corrected it in the thread (Intel -> PHPnerd).
Da gibt es noch Unklarheiten bei der Abfrage, ob eine page table wirklich schon assigned ist. JM fragt hier mittels if(dir->tables[table_index]) ab, während PHPnerd der Meinung ist, dass dies nicht reicht und man auch das Present Bit abfragen sollte. Ich habe mal das der Physical Tables beid er Abfrage hinzu gefügt, weil das im else-Zweig auf 1 gesetzt wird. Sollte soweit gehen.
Da bin ich noch nicht ganz sicher in diesem Bereich des Pagings, auch was die Visualisierung der Baumstruktur CR3 -> PD mit PDE -> PT mit PTE -> Page mit Offset -> physikalische Adresse und der jeweils zugehörigen Zustands-Bits von PD, PT und Page angeht.
PHPnerd ist der Meinung, JM's Code im VMM-Bereich sei sehr schlecht und empfiehlt eine "Neukonstruktion" auf Basis Intel Manual 3A (da steht aber nichts drinnen, was ich nicht schon wusste, die Sekundärliteratur ist inzwischen ziemlich gut). Ich verwende zunächst mal obige set_page(...), bis ich etwas Besseres habe.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 20:15:44 03.05.2009, insgesamt 8-mal bearbeitet
Nochmal eine ganz andere Frage an die Assembler-Spezialisten zum Teil 1:
http://www.henkessoft.de/OS_Dev/OS_Dev1.htm#mozTocId412221
Dort finden sich [Bits 16] und [Bits 32] in einer Übersetzungseinheit (Übergang von RM nach PM). Vielleicht kann man das so verändern, so dass man COFF (32bit, stringentes Format, akzeptiert keinen 16 bit Bereich) anstelle aout (16 und 32 bit gemischt, tolles Format!) verwenden kann? Benötigen wir diesen Eintrag [Bits 16] wirklich? Andere erzählen mir, man sollte den 16/32 Bit Zirkus im Bootloader abwickeln und den Kernel komplett in 32 Bit halten. Ich weiß, dass ich dies versucht habe (man kann kernel.asm ja als verlängerten Bootloader ansehen), aber ein Sprung aus dem Binär-Format nach C-Funktionen war nicht erlaubt.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 12:17:28 03.05.2009, insgesamt 2-mal bearbeitet
Jetzt versteht man die Zusammenhänge besser und z.B. auch, warum der Bereich von 205000h bis 206000h noch gemappt wurde:
map phys addr to virt addr from 0x0 to the end of used memory:
?old: 00205000h sz: 00001000h a: 1 new: 00206000h
Nun ist schon besser. Das werde ich noch ins Tut einbauen, vielleicht noch mit den wichtigsten Bits der Pages hinter den Adressen.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 21:04:27 05.05.2009, insgesamt 4-mal bearbeitet
übrigend funktioniert der source code vom c-kernel bei mir auch nicht.
weder mit make.exe noch mit einer batch
die gcc.exe und ld.exe habe ich aber ins verzeichniss reinkopiert, die fehlen im zip
gcc.exe und ld.exe habe ich aber ins verzeichniss reinkopiert
Das solltest Du nicht machen, sondern diese nach C:\djgpp installieren. Pfad und Env korrekt setzen wie beschrieben. Nimm genau den DJGPP aus dem im Tutorial angegebenen Link wegen dem aout-Format. Falls dann etwas nicht klappt, bitte erst Fehlermeldung lesen und probieren. Wenn Du nicht mehr weiter kommst, genaue Meldung posten. Hast Du schon mit Assembler und C-Compiler gearbeitet?
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 20:32:31 07.05.2009, insgesamt 2-mal bearbeitet
Das Thema Heap ist nun ebenfalls im Tutorial auf Basis des Algos von Doug Lea grundlegend verarbeitet. Wer sich als Leser tiefer hinein bohren möchte, kann es mit den Links machen, für PrettyOS ist nur die Anwendung wichtig.
http://www.henkessoft.de/OS_Dev/OS_Dev2.htm#mozTocId620585
Der Mechanismus funktioniert grob wie folgt:
Zitat:
- Kleinstes Loch ("hole") für die gesuchte Größe in einem geordneten Array suchen. Wenn dies nicht klappt: Heap expandieren etc.
- Loch in zwei Teile zerlegen
- Nicht mehr existentes ursprüngliches Loch vom Index streichen
- Neuen Header und Footer für den allokierten Block schreiben
- Bei der Zweiteilung erzeugtes zweites nicht besetztes Loch in den Index eintragen
- Rückgabe der Adresse des allokierten Blocks plus sizeof(header_t)
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 19:49:36 07.05.2009, insgesamt 2-mal bearbeitet
//ckernel.c int main()
{
k_clear_screen();
printformat("end of kernel %x\n", get_end_kernel());
//...
}
liefert:
Zitat:
end of kernel F000FF53h
Nach meinen Berechnungen sollte natürlich ein deutlich niedrigerer Wert heraus kommen. 0x8000 (start) + 0x3540 (Hexeditor MyOS.bin) - 0x0200 (bootloader sector) = 0xB340
Auch wenn ich _endkernel = .; hinter data setze, kommt das Gleiche heraus. Was mache ich verkehrt?
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 22:00:23 07.05.2009, insgesamt 1-mal bearbeitet
die Variable als Zeiger; das ist noch korrekt, aber du willst da die Adresse, und nicht den Inhalt an dieser Stelle, also
C/C++ Code:
ULONG get_end_kernel(){ return endkernel; }
C/C++ Code:
ULONG get_end_kernel(){ return endkernel; }
C/C++ Code:
ULONG get_end_kernel(){ return endkernel; }
(beachte: ohne stern!) somit kannst du den Pointer auch vom Typ void* oder char* machen, da du ja die Daten, die am Ende deines Kernels sind nicht auslesen möchtest, oder vl. schon^^
bei der ckernel.c, wozu ist der string gut?
der wird doch nirgendwo ausgegeben oder?
bei mir werdeb nur die strings aus dem bootloader ausgegeben.
mfg
lukas
ps:
es wäre cool, wenn du dein tutorial mit einem dateisystem erweitern würdest.
Multitasking, also z.B. Timer-gesteuertes Umschalten zwischen Prozessen mit allem Drum und Dran. Schwieriges Thema, zumindest für mich.
das grundprinzip ist doch klar, oder? task wird vom timer-interrupt unterbrochen, der speichert alle registerinhalte der task und beim verlassen setzt er registerinhalte für die nächste task. schwierig sind dabei nur irgendwelche abgefahrenen scheduling-algorithmen, aber darum brauchste dich am anfang nicht zu kümmern, machste einfach primitivstes round-robin scheduling, d.h. einfach mit 'ner festen frequenz immer weiterswitchen und nachdem die letzte task dran war, kommt wieder die erste dran.
darum brauchste dich am anfang nicht zu kümmern, machste einfach primitivstes round-robin scheduling, d.h. einfach mit 'ner festen frequenz immer weiterswitchen und nachdem die letzte task dran war, kommt wieder die erste dran.
Der Mechanismus funktioniert bereits, über mit vier Tasks.
Zitat:
das grundprinzip ist doch klar, oder?
Ja, mir macht die Abstimmung zwischen TSS entry (nur eine), CR3 (Physikalische Adresse der Page Directory des jeweiligen virtuellen Adressraums), Struktur task_t (für jede Task eine) und eigener Kernel_Stack pro Task noch etwas Probleme. Da stecke ich noch etwas fest. Ich möchte den Task Switch prinzipiell auch außerhalb eines Interrupts durchführen können, um ihn zu studieren. Denn im timer_handler macht es schnell bumm, dann ist es schwer zu analysieren, was eigentlich los ist (GPF, OpcodeF, DebugF, Reset, doing nothing, ...). Erst mit vollständigen log-Funktionen - passend zu den Strukturen task_t und tss_entry_t erkennt man, was im Hintergrund abgeht:
Die Struktur task_t muss sicher noch erweitert werden. ich möchte diese für das Tutorial aber auch nicht überfrachten, damit man den Überblick nicht verliert.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 17:45:57 10.05.2009, insgesamt 4-mal bearbeitet
pop ebx ; reload the original data segment descriptor
mov ds, bx
mov es, bx
mov fs, bx
mov gs, bx
popa ; Pops edi,esi,ebp...
add esp, 8 ; Cleans up the pushed error code and pushed ISR number
sti
iret ; pops 5 things at once: CS, EIP, EFLAGS, SS, and ESP
pop ebx ; reload the original data segment descriptor
mov ds, bx
mov es, bx
mov fs, bx
mov gs, bx
popa ; Pops edi,esi,ebp...
add esp, 8 ; Cleans up the pushed error code and pushed ISR number
sti
iret ; pops 5 things at once: CS, EIP, EFLAGS, SS, and ESP
pop ebx ; reload the original data segment descriptor
mov ds, bx
mov es, bx
mov fs, bx
mov gs, bx
popa ; Pops edi,esi,ebp...
add esp, 8 ; Cleans up the pushed error code and pushed ISR number
sti
iret ; pops 5 things at once: CS, EIP, EFLAGS, SS, and ESP
Ein wesentlicher Punkt, auf den Du unbedingt achten mußt:
Du musst unbedingt etwas installieren, was das Taskswitching kurzfristig stoppt, denn sonst kommt in der kritischen Phase des Anhängens einer neuen Task just in dem Moment ein switch und alles geht daneben. Bei dem 16-bit-taskswitching, was ich Dir in der Mail geschickt habe, war das ein entscheidender Faktor.
Da ist übrigens auch eine Funktion mit drin, die einen Taskswitch erzwingt.
Hab mich aber nie getraut, das alles auf 32-bit zu erweitern.
Du musst unbedingt etwas installieren, was das Taskswitching kurzfristig stoppt, denn sonst kommt in der kritischen Phase des Anhängens einer neuen Task just in dem Moment ein switch
Danke für den Hinweis! Ich werde da noch ein ts-flag in meiner ODA (OS Data Area) Struktur einbauen, das zuvor abgefragt wird. Die nutze ich sowieso viel zu wenig.
C/C++ Code:
1 2 3 4 5 6 7 8 9 10 11 12 13
1 2 3 4 5 6 7 8 9 10 11 12 13
typedef struct oda
{
//...
//tasking
UCHAR ts_flag; // 0: taskswitch off 1: taskswitch on
}oda_t;
//...
pODA->ts_flag = 1;
//... if( pODA->ts_flag == 1 )
task_switch();
C/C++ Code:
1 2 3 4 5 6 7 8 9 10 11 12 13
typedef struct oda
{
//...
//tasking
UCHAR ts_flag; // 0: taskswitch off 1: taskswitch on
}oda_t;
//...
pODA->ts_flag = 1;
//... if( pODA->ts_flag == 1 )
task_switch();
C/C++ Code:
1 2 3 4 5 6 7 8 9 10 11 12 13
typedef struct oda
{
//...
//tasking
UCHAR ts_flag; // 0: taskswitch off 1: taskswitch on
}oda_t;
//...
pODA->ts_flag = 1;
//... if( pODA->ts_flag == 1 )
task_switch();
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 20:10:31 11.05.2009, insgesamt 2-mal bearbeitet
und dann überleg mal, wie sich die Timerfunktion eventuell verhalten kann.
Bei mir wurde der Originalvektor auf eine Routine umgelenkt, der erst den Originalvektor bedient hat, und dann noch einen Ticker inkrementiert hat.
Was ich auch noch Wichtiges gefunden habe - Turbo C hatte da eine Semaphore namens INDOS. Solange die gesetzt war, durfte der Switcher nicht umschalten.
So ein Festplattenkopf kann da genervt reagieren
Zuletzt bearbeitet von Bitsy am 05:40:19 12.05.2009, insgesamt 1-mal bearbeitet
Momentan habe ich auf die task_switch Technik von tyndur (OS einer Community) umgestellt, irgendwo ist aber noch ein müder Fehler versteckt, der zu GPF führt.
Ich benutze nur ein TSS. Was muss alles in diese TSS geschrieben werden beim task_switch?
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 22:29:23 12.05.2009, insgesamt 1-mal bearbeitet
Der Counter ermöglicht es Paare von freeze und unfreeze auf verschiedenen Ebenen zu bilden. Erst wenn das 'oberste' unfreeze wieder gerufen wurde, gibt er das switching wirklich wieder frei. Wenn Du nur ein einfaches Bit nimmst, verlierst Du früher oder später den Überblick.
Nimm mal an, Du sperrst per Bit vor einem Diskzugriff und gibst es später wieder frei. Was ist, wenn der User auch einen Grund hatte das Switching zu sperren, und unmittelbar nach dem Diskzugriff wird es aber wieder freigegeben?
Mit dem Counter erschlägst Du alle Probleme. (Außer dem, daß Du Fehler bei der Paarbildung machst )
Habt ihr in anderen Fällen nicht auch schon solche Counter drin? Ich dachte, ich hätte da schon was gelesen.
Was in die TSS muß - tja, für 32-bit-mode weiß ich's eben auch nicht!
Zuletzt bearbeitet von Bitsy am 07:56:36 13.05.2009, insgesamt 1-mal bearbeitet
Jetzt hast Du Dir aber mal ein freies Wochenende verdient - das Wetter soll gut werden (jedenfalls Samstag und Sonntag). Ab in den Biergarten und das Projekt feiern
Jetzt geht es doch erst richtig los mit PrettyOS. Ich denke gerade über die Darstellung von interessanten Möglichkeiten für Dispatcher (short-term scheduler) und long/middle-term Scheduler nach.
Wo bekommt man ein lauffähiges kleines Programm (32 bit ohne BIOS-INT) her? Das könnte man doch über den incbin-Trick und evtl memcpy an eine bestimmte Position laden und anstelle moo() oder baa() ausführen, oder? Man könnte doch z.B. das alte COM-Format als flat Binaries verwenden. Man müsste dort doch nur noch einen Task erstellen (create_task ist ja bereits eingebaut), die Segmentregister sauber setzen und eip auf das erste Byte der Datei einstellen. Das Reinspringen in den Code könnte wieder der Interrupthandler übernehmen, der das auch schon bei den Kerneltasks gemacht hat.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 01:11:30 16.05.2009, insgesamt 2-mal bearbeitet
vorschlag: create_task() sollte was zurückgeben, z.b. einen pointer auf die task_t oder die task-ID bzw. im fehlerfall 0 oder einen ungültigen ID-wert, damit der aufrufer die task kontrolliern kann und weiss, ob das erzeugen der task überhaupt geklappt hat. ausserdem wär's gut, der task-entry funktion noch einen void* mitzugeben, damit der aufrufer startparameter an die neue task übergeben kann.
create_task() sollte was zurückgeben, z.b. einen pointer auf die task_t
Klar, das ist notwendig, damit man auf die Task-Struktur (noch sehr rudimentär) zugreifen kann, wurde daher sofort erledigt.
Zitat:
task-entry funktion noch einen void* mitzugeben, damit der aufrufer startparameter an die neue task übergeben kann.
Ich experimentiere gerade mit Varianten von create_task, die verschiedene Anzahlen Parameter übernehmen. Ich habe z.B. settextcolor(schriftfarbe,hintergrundfarbe) so aufgerufen, dass ich der Task diese beiden Argumente neben einer Dummy-Rücksprungadresse auf den Stack übergebe und dann vom Task aus den Code von "settextcolor" als Konstante mit call aufrufe.
Das Schlimme und Gute zugleich bei der OS-Entwicklung ist, dass alle Verständnislücken, die man so hat, gleichzeitig und hinterrücks zuschlagen.
Ich kann nur hoffen, dass das Tutorial anderen bei dem Finden des richtigen Weges etwas helfen kann.
Das Schlimme und Gute zugleich bei der OS-Entwicklung ist, dass alle Verständnislücken, die man so hat, gleichzeitig und hinterrücks zuschlagen.
das liegt nur an der mit, der heissen nadel gestrickten, x86-architektur. ich behaupte mal, jeder andere prozessor würde einem weniger kopfzerbrechen bereiten.
das liegt nur an der mit, der heissen nadel gestrickten, x86-architektur. ich behaupte mal, jeder andere prozessor würde einem weniger kopfzerbrechen bereiten.
Vorsicht, vorsicht, das Eis auf dem Du Dich bewegst, ist sehr sehr dünn...
das liegt nur an der mit, der heissen nadel gestrickten, x86-architektur. ich behaupte mal, jeder andere prozessor würde einem weniger kopfzerbrechen bereiten.
Vorsicht, vorsicht, das Eis auf dem Du Dich bewegst, ist sehr sehr dünn...
welcher ist schlimmer (von totalen exoten mal abgesehen)?
Ein lokaler Stack auf der Heap ist mithilfe eines speziellen 'Registers' einfach realisierbar - so funktionierte der 68000er Prozessor.
A7 war das Heapregister, A6 der Userstack.
Die Kommandos link und unlink wurden in etwa so realisiert:
(A7 und A6 beide long).
Du findest Stacks auf dem (die?) Heap also auch nicht schlimm? Hat ja Vorteile wegen der einfachen Speicherfreigabe bei Task-Beendigung.
spricht ja erstmal nix dagegen, alle speicherressourcen eines prozesses (stack, handle-tabellen, usw.) auf dem system-heap anzulegen. der heap, den der prozess selber verwendet (für seine malloc/free aufrufe) sollte natürlich ein anderer sein.
Inzwischen habe ich mich auch an die Idee des Stacks auf dem Heap gewöhnt. Das hat gegenüber dem Schieben der placement_address ins "Unendliche" eindeutig die Vorteile eines vernünftigen Speichermanagements.
Das Tutorial wurde um das Thema Virtuelles Filesystem und Initial RAM Disk erweitert. Hier habe ich den Code von JM übernommen, da er entsprechend POSIX klar strukturiert und für PrettyOS zunächst tauglich ist: http://www.henkessoft.de/OS_Dev/OS_Dev2.htm#mozTocId676802
Ich habe direkt hinter die RAM Disk, die beispielhaft mit 3 Files bestückt wurde, noch das kleine Test-Programm gehängt.
Vielleicht habt ihr bessere Ideen für einen veränderten RAM-Disk- und File-Header. JM hat hier im RAM-Disk-Header lediglich die Anzahl Files eingetragen und in den File-Headern den Namen, den Offset und die Größe angegeben. Da könnte man evtl. eine interessante Lösung umsetzen, wobei dies keine besondere bedeutung hat, außer das der Leser solche Formate vom Typus her generell kennen lernt.
Interessant wäre es vielleicht, in die RAM Disk nicht nur langweilige Text-Files, sondern mehrere Programm-Files aufzunehmen und diese mit einer Auswahl-Routine Run(FileName) aus verschiedenen Tasks im Multitasking zu starten. Damit kämen wir dem Laden/Starten eines Programms/Moduls, in diesem Fall von einer virtuellen Disk auf einem abstrakten File-Node im VFS, doch schon etwas näher? Habt ihr Ideen für kleine Demo-Programme? Assembler-Programmierer nach vorne! Bisher habe ich nur dieses winzige Programm, dessen Ausgabe man nur mit dem Debugger wahr nehmen kann:
Die Programme dürfen ja noch keine externen Ressourcen anfordern, weil dieses Prozess- und I/O-Management noch fehlt.
EDIT: Syscalls und Usermode (z.Z. leider noch mit Supervisor-Privileg, weil es ansonsten mit PF oder GPF endet) ist in meinem Experimentierfeld bereits eingebaut. Ich frage mich gerade, wie eine API von innen her gesehen funktioniert. Von außen verwendet man diese typischerweise mit api.h/api.lib.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 11:14:29 21.05.2009, insgesamt 11-mal bearbeitet
Ich wollte an dieser Stelle darauf hinweisen, dass die deutsche Übersetzung der dritten Auflage von Tanenbaum's Meisterwerk verfügbar ist und kann dieses Grundlagen- und Nachschlagewerk nur empfehlen: Andrew S. Tanenbaum et.al., Moderne Betriebssysteme, 3. Auflage, Pearson Studium, April 2009 (Übersetzung von "Modern Operating Systems", Prentice Hall, Dec 2007). Der wesentliche Makel der deutschen Ausgabe ist, dass das IMHO gelungene Titelbild des Originals nicht enthalten ist.
deutsch: http://www.pearson-studium.de/media_local/shop_u1bigs_3d/9783827373427.jpg (April 2009)
amerikanisch: http://vig-fp.prenhall.com/bigcovers/0136006639.jpg (Dec 2007)
Für mich neben den Intel Manuals und einem Assemblerbuch eine wesentliche Fundgrube für Fakten und Ideen.
Allerdings staune ich immer wieder, dass selbst so ein Standardwerk noch Fehler, didaktische Luecken und Ungereimtheiten enthaelt.
Gibt es eigentlich schon so etwas wie "OS Development for Dummies" in engl. oder deutsch? Ist mir bisher nicht unter gekommen.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 15:13:13 30.05.2009, insgesamt 4-mal bearbeitet
Für echte User-"Programme" benötigt man ja auch noch Stack und Heap im User-Privileg. Da denke ich gerade nach, wie man das einfach möglichst einfach in Ring3 mappen/allokieren kann.
Bei obigem Programm gibt es noch Probleme, wenn man das while in test5 streicht. Da überlege ich gerade eine Umhüllung oder ein exit. Wenn da jemand eine Idee hätte?
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 09:16:42 23.05.2009, insgesamt 5-mal bearbeitet
Ich hab noch eine kleine Anmerkung zum Stack.
Zwar weiß ich nicht, was Du da jetzt alles drüber abwickeln willst, (sagst ja, lokale Speicheranforderung extra etc.), und ich weiß deshalb nicht, ob hier ein Problem entstehen kann. Solltest aber einen klitzekleinen Moment drüber verwenden, ob hier memory fragmentation reinspielen kann oder nicht. Wenn ja, unterschätze den Effekt nicht. Ich habe damals an die DJGPP-newsgroup ein sample geschickt (okay, das ist jetzt lokale Speicheranforderung), was den gcc abschiessen konnte, obwohl alles korrekt war. Hatte einfach zufällige Mengen von Speicheranforderungen und -freigaben in rauher Menge laufen lassen.
Auf die Bemerkung, das wäre ja nun sehr willkürlich, habe ich einfach gekontert, dass bei einer OOP-Sprache wie C++ doch kaum noch absehbar ist, was in den vielen kleinen Blackboxes alles passiert. Das haben sie dann doch ernst genommen, und das ganze Kapitel so überarbeitet, dass sich mein Sample auf den Kopf stellen konnte - es blieb stabil! Vielleicht kann man durch debuggen/disassemblen des new-Befehls mal schauen, was für ein Grundprinzip dahinter steckt.
Kann schlecht abschätzen, ob Du so etwas an der bewußten Stelle auch brauchst.
Reine fifo-Dinge werden es wohl auch nicht mehr sein.
Zuletzt bearbeitet von Bitsy am 10:58:55 22.05.2009, insgesamt 2-mal bearbeitet
@Bitsy: Danke für den Hinweis. Momentan kämpfe ich dermaßen mit Prozessen (Multitasking/Privilegien/Syscall/Interrupts), dass ich gar keine Zeit für das Speichermanagement habe.
Du meinst, wenn ständig irgendein Prozess 4 KB Stack anfordert und anschließend wieder frei gibt? Wir betreiben das Spiel mit den Stacks auf dem Heap. Ich muss mal schauen, wie man diese geordenete Liste aller "freien Löcher" bzw. "belegten Blocks" (Heap Code von James Molloy, Kernel Tutorial, Kap. 7, übernommen) visualisieren kann. Dann könnten wir den Algo testweise stressen.
Vielleicht hat jemand Zeit/Lust für so eine Sonderaufgabe?
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 10:44:45 23.05.2009, insgesamt 2-mal bearbeitet
Das ist wohl eher ein task_yield(), da der Prozess wieder auftaucht und nicht aus der Liste der lauffaehigen Prozesse ausgetragen wird. Prozesse geben normalerweise nicht freiwillig auf, und wenn, dann mit exit endgueltig.
Das ist wohl eher ein task_yield(), da der Prozess wieder auftaucht und nicht aus der Liste der lauffaehigen Prozesse ausgetragen wird. Prozesse geben normalerweise nicht freiwillig auf, und wenn, dann mit exit endgueltig.
Ja, alles richtig, werde dies auf ein exit() umbauen. Obiger Mechanismus koennte evtl. fuer ein thread_yield verwendet werden.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Ich denke mal, es soll dazu dienen, zu verstehen, wie ein OS funktioniert. Deswegen wird es von Grund auf programmiert. Und da so etas nicht einfach ist, muss man etwas geduld mitbringen.
was soll dieses os überhaupt bringen? es gibt doch schon minix für genau diesen zweck. und das ist auch x86. tut es wirklich not?
Wie matze schon meinte, es dient zur Übung und ein OS ist eine gute Herausforderung. Hätte ich das Wissen und die Zeit dazu, würde ich mich auch dran versuchen. Wer kann schon von sich sagen, er habe alleine sein eigenes OS gebaut?
nein, ihr habt mein posting nicht verstanden. es gibt schon minix, wsa genau für diesen zweck geschrieben wurde und in einem umfangreichen buch schritt für schritt, sowohl in der theorie als auch in der praxis (anhand des quellcodes) erläutert wie ein OS funktioniert. also wozu genau das gleiche hier nochmal?
weil es menschen gibt, die besser lernen wenn sie es selbst von grund auf durcharbeiten. wenn sie andere an der arbeit teilhaben lassen kommt dies dem eigenen lernprozess und dem der anderen zu gute. deswegen existieren unzählige halbfertige betriebssystem-kernel, und das ist auch gut so.
und all dies für dieses frickelicke baumhaus mit zig anbauten namens x86. naja wers sich gern unnötig schwermacht
wenn ich mal zeit habe schreibe ich auch mal ein buch über ein kleines betriebssystem, aber das läuft dann auf einem schönen MIPS oder ARM oder so, damit der leser sich aufs wesentliche konzentrieren kann und ned auf x86 frickelei.
Schaut man sich die in Tanenbaum "Modern OS" konkret untersuchten OS an, so findet man Unix/Linux, MS Vista und Symbian OS. Die meisten Hobby-OS sind wohl Linux/Unix-Clones.
Ich wollte zunächst mit dem starten, was viele als Teenager - in diesem Alter versuchen einige begabte und ehrgeizige jungen Leute ein erstes eigenes OS zu basteln, um die Grundlagen besser zu verstehen - zur Verfügung haben, nämlich einen 80x86 Rechner mit MS Windows. Daher schreibe ich das Tutorial in deutsch und starte von diesem Bezugspunkt.
Das Design-Thema habe ich auf Teil 3 verschoben. Teil 2 ist zunächst noch dem spielerischen Kennenlernen weiterer notwendiger Techniken wie Speichermanagement (Paging, Heap, Virtual File System, Ram Disk) und Prozessmanagement (Multitasking) gewidmet. Das Thema User-Lib und -Programme gehört dort auch noch dazu.
Wie ich GRUB und Cross-Compiler einbeziehen werde, weiß ich noch nicht sicher. Zunächst werde ich das Community-OS tyndur näher unter die Lupe nehmen, um handwerklich dazu zu lernen. Hier existiert ein "noch" lebendiges deutsches Forum zum Thema OS Development, das eine Unterstützung/Belebung verdient und arbeitsteilig seit einigen Jahren ein interessantes Hobby-OS entwickelt:
http://lowlevel.brainsware.org/wiki/index.php/Tyndur
Das Kernproblem für den Einsteiger ist nicht die komplexe x86-Technik, die in den Intel Manuals gut beschrieben ist, sondern die Herausforderung, mit den notwendigen Tools (Intel- und AT&T Assembler-Syntax, Linker-Skripte, Cross Compiler, GRUB Image, Bochs Debugger, ...) und Informationsquellen (Intel Manuals, Foren, Tutorials, Bücher, Sourcecode einiger Vorbild-OS, Wechselspiel C und Assembler, ...) umfassend und tiefschürfend klar zu kommen.
Minix3 http://www.minix3.org/ spielt hierbei nach wie vor nur eine akademische Rolle als Beispiel für einen "Mikrokernel", was nichts über seine Bedeutung für die Zukunft aussagt. Es wurde historisch schlicht und einfach von Linux besiegt. Hier hat der mutige Student über den sturen Professor gesiegt.
In der Praxis findet man momentan bevorzugt monolithische Systeme.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 04:30:48 13.06.2009, insgesamt 6-mal bearbeitet
Das Kernproblem für den Einsteiger ist nicht die komplexe x86-Technik, die in den Intel Manuals gut beschrieben ist...
x86 prozessoren sind schon übel genug. protected/real-mode, descriptortabellen, segmentregister usw. kommen doch nur daher, dass eine uralt-cpu mit immer mehr funktionalität versehen wurde, aber trotzdem kompatibel zur ursprungsversion bleiben sollte. hinzu kommt noch die hürde der verfrickelten PC-architektur (boot-loader, bussysteme, video-ram, bios, usw). alles in allem eine suboptimale konstruktion, so als hätte jemand einen formel-1 wagen aus 'nem trabbi zusammengezimmert.
Erhard Henkes schrieb:
sondern die Herausforderung, mit den notwendigen Tools (Intel- und AT&T Assembler-Syntax, Linker-Skripte, Cross Compiler, GRUB Image, Bochs Debugger...
hier bietet sich vielleicht an (als unterprojekt z.b.) eine toolchain zusammenzustellen, die ein benutzer leicht installieren (oder im idealfall einfach auf seinen rechner kopieren) kann, um dein OS zu bauen, zu testen und damit zu experimentieren.
Erhard Henkes schrieb:
"Mikrokernel", was nichts über seine Bedeutung für die Zukunft aussagt. Es wurde historisch schlicht und einfach von Linux besiegt.
...
In der Praxis findet man momentan bevorzugt monolithische Systeme.
auch hier, würde ich sagen, hat nicht das bessere gesiegt. ein modularer kernel ist doch wartbarer und leichter erweiterbar, als ein monolithischer klotz. ein system, in dem alle komponenten über wohldefinierte schnittstellen verbunden sind, ist im endeffekt auch weniger komplex.
x86 prozessoren sind schon übel genug. protected/real-mode, descriptortabellen, segmentregister
Wenn man die Beiträge zu der "üblen" Situation der x86 Architektur liest, fragt man sich, warum diese komplizierte Struktur - bedingt durch Abwärtskompatibiltät - nicht schon längst durch eine überlegene einfachere Struktur überholt wurde?
Zitat:
toolchain
Ja, das ist richtig. Langfristig bleibt aber einem "OSDever", der in einer OS-Entwickler-Community (Deutschland: lost/tyndur; lowlevel.brainsware.org) eingebettet sein will, nur der Weg über Linux als Hostsystem und GRUB als luxuriöser Bootloader. Das gilt neben Bochs als Emulator und Debugger als Quasi-Standard im PC-Bereich. Linux ist insgesamt ein übermächtiges Vorbild für die gesamte Szene, die alles andere an die Wand drückt. Das ist schade und sollte durchbrochen werden. Vielleicht schaffen wir das noch, sind ja erst ganz am Anfang. Eine OS benötigt Jahre bis ein halbwegs stabiles Team mit Freude daran arbeiten kann. Redesigns und Neuanfänge inbegriffen.
Zitat:
auch hier, würde ich sagen, hat nicht das bessere gesiegt. ein modularer kernel ist doch wartbarer und leichter erweiterbar, als ein monolithischer klotz. ein system, in dem alle komponenten über wohldefinierte schnittstellen verbunden sind, ist im endeffekt auch weniger komplex.
Akzeptiert. Welches Vorbild schwebt Dir hier vor? Minix3?
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Ich denke mal, es soll dazu dienen, zu verstehen, wie ein OS funktioniert. Deswegen wird es von Grund auf programmiert. Und da so etwas nicht einfach ist, muss man etwas Geduld mitbringen.
Ja, Geduld und die Bereitschaft tief zu bohren, gehören dazu. Ich denke, dass so etwas lange Zeit braucht, bis es "fliegt".
Dann gibt es allerdings ein anderes Problem, nämlich Einsteigern in die Materie die Zusammenhänge darzustellen. Beispielsweise ist es extrem schwierig in "tyndur" (lost) einzusteigen, weil nirgends eine didaktisch durchdachte Step-by-Step-Anleitung, Modul-/Funktions-Übersichten oder grafische Darstellungen zu finden sind, wie dieses OS im Inneren arbeitet. Man muss über die ganz harte Tour zum Insider werden oder man lässt es schnell wieder.
Auf jeden Fall werde ich den Noobie nicht aus den Augen verlieren. Ein Buch "Betriebssystementwicklung - leicht gemacht" ist ein mögliches Ziel, oder zumindest eine brauchbare Tutorial-Reihe. Der Brückenschlag zu den Hobby-Robotern bietet sich ebenfalls an. Microsoft hat mit "Robotics" (seit 2006) http://msdn.microsoft.com/de-de/robotics/default(en-us).aspx einen Anfang gemacht. Daneben ist aber noch viel Platz für Alternativen.
Bisher haben wir nur einige "Bausteine" gezeigt und etwas damit herum experimentiert. Immerhin habe ich bereits argumentativ mit geholfen, dass James Molloy einsieht, dass er seine Tutorial-Reihe komplett überarbeiten sollte.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 01:54:26 14.06.2009, insgesamt 2-mal bearbeitet
Wenn man die Beiträge zu der "üblen" Situation der x86 Architektur liest, fragt man sich, warum diese komplizierte Struktur - bedingt durch Abwärtskompatibiltät - nicht schon längst durch eine überlegene einfachere Struktur überholt wurde?
schwer zu sagen. wahrscheinlich sinds wirtschaftliche interessen, an denen sich hard- und softwarehersteller ständig gegenseitig hochziehen. warum eine etablierte technik über den haufen werfen, wenn man einfach was dranflicken und der kundschaft als innovation verkaufen kann? solange der geldhahn sprudelt, besteht kein grund für 'ne radikale änderung, ja es wäre sogar riskant.
Erhard Henkes schrieb:
Welches Vorbild schwebt Dir hier vor? Minix3?
nix konkretes, einfach nur ein gutes, modulares design. klare trennung der aufgaben, hardwareabstraktion, schichtenmodell usw.
Die Kommunikation muss über die Schnittstellen jeder einzelnen Zwischenschicht erfolgen. Das kostet Zeit und ist der Preis dieser sauberen Architektur.
sowas lässt sich sowieso kaum verhindern, beispiel: filesystem->sektorbasierter zugriff->bustreiber->hardwaretreiber, oder netzwerk->transport protokoll->paketorientierter zugriff->hardwaretreiber, usw. hauptsache verschiedene teilkomponenten sind dabei austauschbar bzw. parallel ausführbar (z.b. ein filesystem kann gleichzeitig mit festplatten, sd-karten und cd-roms arbeiten, was ja unter allen grossen OS selbstverständlich ist).
Erhard Henkes schrieb:
Damit fiele Minix3 als puristischer Microkernel weg, wenn ich das richtig sehe.
Vielleicht kann man mal jemand drüber schauen, ob Fehler/Unklarheiten enthalten sind.
Vielleicht wäre es noch wichtig zu erwähnen, dass es bestimmte Aufrufkonventionen gibt, wie cdecl, stdcall, pascal und fastcall (gibt es noch welche?) und dass damit geregelt wird, wie die Parameter auf dem Stack abgelegt werden, also in welcher Reihenfolge, und wer sie aufräumen muss (caller oder callee)...
Vielleicht kann man mal jemand drüber schauen, ob Fehler/Unklarheiten enthalten sind
du solltest noch dazuschreiben, auf welchen compiler du sich beziehst (GCC version 4.x nehme ich an) ein anderer compiler könnte vieles anders machen. ach ja, die benamung der funktion 'exit_task()' finde ich nicht sonderlich gelungen. dem namen nach könnte man vermuten, dass die funktion die task beendet, so dass sie nicht wieder dran kommt (vergl. exit() in standard C). womöglich wäre next_task(), switch_now(), yield(), oder sowas eine alternative?
Ja, das ist eindeutig falsch. "switch_context()" klingt gut, hat vor allem keinen Bezug zu irgendwelchen POSIX-Begriffen. Normalerweise machen Prozesse so was nicht "freiwillig", nur Threads kennen yield(). Da jede task ihren eigenen Adressraum hat, handelt es sich eindeutig um Prozesse. Das Ganze ist noch relativ experimentell und rudimentär. Ich denke gerade über eine vernünftige Weiterentwicklung nach, aber vielleicht ist es an dieser Stelle dafür noch zu früh, denn Scheduling und Threading ist ein zwar wichtiges aber leider ebenso komplexes Thema mit vielen Möglichkeiten. Das Schlimme dabei ist, dass es keine optimale Lösung gibt. Selbst Linux hat hier im Laufe seiner Entwicklung einen vollen Salto hingelegt. Das Thema Deadlock und entsprechende Deadlock-Algos zur Vermeidung oder Vorbeugung lauert bei Multithreading ebenso bereits um der Ecke.
Zitat:
GCC version 4.x nehme ich an
Das ist ein ganz übles Thema, da ich wegen des eigenen Bootloaders und dieses blöden aout-Formats (16/32-Bit-Code gemischt in kernel.asm) beim Linker immer noch an die Version gcc 3.1, ld 2.13 (in DJGPP) gebunden bin.
Hier hilft - nach meinem bisherigen Wissensstand - nur der typische Ausstieg nach Linux und GRUB. Dies werde ich in Teil 3 wohl auch machen müssen. Dann kommen allerdings die ganzen GRUB-Themen (magic numbers, ...) hinzu. Jemand, der auf längere Sicht OS-Development betreibt, muss sich allerdings diese Tool-Basis schaffen, um fremde OS kompilieren/testen zu können.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 22:28:44 14.06.2009, insgesamt 1-mal bearbeitet
Bin in dem neuen Artikel nur über eine eher unwesentliche Kleinigkeit gestolpert:
Zitat:
Es gibt übrigens eine Konvention, dass der callee bevorzugt die Register EAX (z.B. zum Rechnen, Transferieren und als Rückgabewert), ECX (z.B. als Schleifenzähler) und EDX (in obigem Bsp. z.B. zum Rechnen) verwendet.
Das ist weniger eine Konvention sondern mehr eine Vorgabe des Prozessors. Manche Instruktionen arbeiten nur mit bestimmten Registern. "LOOP" als Beispiel funktioniert nur mit ECX als Zähler.
Die Register EAX, ECX, und EDX sind reserviert für die Verwendung innerhalb der Funktion, werden also unter Umständen verändert. Rückgabewerte werden im EAX-Register zurückgegeben.
Scratch registers are registers that can be used for temporary storage without restrictions (also called caller-save or volatile registers): EAX, ECX, EDX
Zitat:
Callee-save registers are registers that you have to save before using them and restore after using them (also called non-volatile registers). You can rely on these registers having the same value after a call as before the call: EBX, ESI, EDI, EBP
Ich habe diese beiden Links in den Artikel übernommen.
Danke für die Durchsicht.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 00:38:44 15.06.2009, insgesamt 2-mal bearbeitet
Hier hilft - nach meinem bisherigen Wissensstand - nur der typische Ausstieg nach Linux und GRUB.
das wäre doch doof. die meisten haben windosen. sich extra noch ein OS zu installieren, um dein OS zu bauen, ist nervig.
Erhard Henkes schrieb:
Dies werde ich in Teil 3 wohl auch machen müssen. Dann kommen allerdings die ganzen GRUB-Themen (magic numbers, ...) hinzu. Jemand, der auf längere Sicht OS-Development betreibt, muss sich allerdings diese Tool-Basis schaffen, um fremde OS kompilieren/testen zu können.
es geht bestimmt mit jedem compiler z.b. msvc express edition, watcom, oder dem LCC. natürlich nicht von allein, etwas basteln muss man schon. vorteil: du wärst diese ätzende AT&T-syntax endlich los.
Das ist ein ganz übles Thema, da ich wegen des eigenen Bootloaders und dieses blöden aout-Formats (16/32-Bit-Code gemischt in kernel.asm) beim Linker immer noch an die Version gcc 3.1, ld 2.13 (in DJGPP) gebunden bin.
Hier hilft - nach meinem bisherigen Wissensstand - nur der typische Ausstieg nach Linux und GRUB.
Wieso muss es eigentlich im aout Format sein? Ich habe vor kurzem das OS + Bootloader unter Ubuntu 9.04 mit recht aktuellem gcc 4.3.3, ld 2.19.1 und nasm 2.05.1 compiliert. Dabei habe ich unter anderem das aout in elf geändert, und es lief ohne Probleme. Das aktuelle DJGPP hat ähnliche Versionen, daher sollte das doch eigentlich auch gehen, oder gibt es da noch einen anderen Unterschied?
Im NASM Manual steht auch, wenn ich das richtig verstehe, dass 16 Bit im ELF Format möglich ist, da NASM die nötigen Informationen übergibt, damit ld weiß was damit zu tun ist: http://www.nasm.us/doc/nasmdoc7.html (7.7.6)
Unter http://pastebin.com/m3b4d8bcd ist mein Makefile. Das Basis Makefile war die aktuelle Testversion am 20. Mai. Ansonsten habe ich nur noch bei sämtlichen Variablen und Funktionen, die von c benutzt wurden, aber in asm definiert waren, den Unterstrich entfernen müssen. Anscheinend ist es nicht mehr Standard, dass gcc den Unterstrich benutzt und ich habe auch kein Compilerflag dafür gefunden das wieder zu aktivieren.
Zuletzt bearbeitet von Tobiking2 am 17:07:55 15.06.2009, insgesamt 1-mal bearbeitet
das wäre doch doof. die meisten haben windosen. sich extra noch ein OS zu installieren, um dein OS zu bauen, ist nervig.
Interessante Bemerkung. Wenn ich das mit dem Compiler mit ELF (16 u. 32 Bit gemischt) unter Windows schaffe (es gibt ja auch cross-compiler), dann bleibt nur noch GRUB als Thema. Alternativ kann man den bootloader aufmotzen. Mal sehen.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 02:48:54 16.06.2009, insgesamt 6-mal bearbeitet
Wenn ich das mit meinem DJGPP (gcc 3.1) ausprobiere erhalte ich vom Linker (ld 2.13) folgende Fehlermeldung:
Zitat:
kernel.o: file not recognized: File format not recognized
Habs grad mal mit dem aktuellen djgpp probiert und das gleiche Problem. DJGPP benutzt nur COFF und unterstützt ELF gar nicht (http://www.delorie.com/djgpp/v2faq/faq22_22.html). Bei coff ist darüber hinaus kein 16 Bit möglich.
Wenn ich das mit dem Compiler mit ELF (16 u. 32 Bit gemischt) unter Windows schaffe (es gibt ja auch cross-compiler), dann bleibt nur noch GRUB als Thema.
was willste mit 16 bit? dachte dein OS schaltet beim start schon in den (flat memory model) 32bit protected mode und bleibt da auch. oder hab' ich was übersehen?
;org 0x8000 ; code's start offset
[BITS 16] ; 16 Bit Code
[global RealMode]
[extern _main] ; this is in the c file
;;;;;;;;;;;;;
; Real Mode ;
;;;;;;;;;;;;;
RealMode:
xor ax, ax ; set up segments
mov es, ax
mov ds, ax
mov ss, ax
mov sp, ax
mov si, welcome
call print_string
cli ; clear interrupts
lgdt [gdtr] ; load GDT via GDTR (defined in file "gtd.inc")
; we actually only need to do this ONCE, but for now it doesn't hurt to do this more often when
; switching between RM and PM
in al, 0x92 ; switch A20 gate via fast A20 port 92
cmp al, 0xff ; if it reads 0xFF, nothing's implemented on this port
je .no_fast_A20
or al, 2 ; set A20_Gate_Bit (bit 1)
and al, ~1 ; clear INIT_NOW bit (don't reset pc...)
out 0x92, al
jmp .A20_done
.no_fast_A20: ; no fast shortcut -> use the slow kbc...
call empty_8042
mov al, 0xD1 ; kbc command: write to output port
out 0x64, al
call empty_8042
mov al, 0xDF ; writing this to kbc output port enables A20
out 0x60, al
call empty_8042
.A20_done:
mov eax, cr0 ; switch-over to Protected Mode
or eax, 1 ; set bit 0 of CR0 register
mov cr0, eax ;
empty_8042:
call Waitingloop
in al, 0x64
cmp al, 0xff ; ... no real kbc at all?
je .done
test al, 1 ; something in input buffer?
jz .no_output
call Waitingloop
in al, 0x60 ; yes: read buffer
jmp empty_8042 ; and try again
.no_output:
test al, 2 ; command buffer empty?
jnz empty_8042 ; no: we can't send anything new till it's empty
.done:
ret
print_string:
mov ah, 0x0E
.loop_start:
lodsb ; grab a byte from SI
test al, al ; test AL
jz .done ; if the result is zero, get out
int 0x10 ; otherwise, print out the character!
jmp .loop_start
.done:
ret
Waitingloop:
mov ebx,0x9FFFF
.loop_start:
dec ebx
jnz .loop_start
ret
;org 0x8000 ; code's start offset
[BITS 16] ; 16 Bit Code
[global RealMode]
[extern _main] ; this is in the c file
;;;;;;;;;;;;;
; Real Mode ;
;;;;;;;;;;;;;
RealMode:
xor ax, ax ; set up segments
mov es, ax
mov ds, ax
mov ss, ax
mov sp, ax
mov si, welcome
call print_string
cli ; clear interrupts
lgdt [gdtr] ; load GDT via GDTR (defined in file "gtd.inc")
; we actually only need to do this ONCE, but for now it doesn't hurt to do this more often when
; switching between RM and PM
in al, 0x92 ; switch A20 gate via fast A20 port 92
cmp al, 0xff ; if it reads 0xFF, nothing's implemented on this port
je .no_fast_A20
or al, 2 ; set A20_Gate_Bit (bit 1)
and al, ~1 ; clear INIT_NOW bit (don't reset pc...)
out 0x92, al
jmp .A20_done
.no_fast_A20: ; no fast shortcut -> use the slow kbc...
call empty_8042
mov al, 0xD1 ; kbc command: write to output port
out 0x64, al
call empty_8042
mov al, 0xDF ; writing this to kbc output port enables A20
out 0x60, al
call empty_8042
.A20_done:
mov eax, cr0 ; switch-over to Protected Mode
or eax, 1 ; set bit 0 of CR0 register
mov cr0, eax ;
empty_8042:
call Waitingloop
in al, 0x64
cmp al, 0xff ; ... no real kbc at all?
je .done
test al, 1 ; something in input buffer?
jz .no_output
call Waitingloop
in al, 0x60 ; yes: read buffer
jmp empty_8042 ; and try again
.no_output:
test al, 2 ; command buffer empty?
jnz empty_8042 ; no: we can't send anything new till it's empty
.done:
ret
print_string:
mov ah, 0x0E
.loop_start:
lodsb ; grab a byte from SI
test al, al ; test AL
jz .done ; if the result is zero, get out
int 0x10 ; otherwise, print out the character!
jmp .loop_start
.done:
ret
Waitingloop:
mov ebx,0x9FFFF
.loop_start:
dec ebx
jnz .loop_start
ret
;org 0x8000 ; code's start offset
[BITS 16] ; 16 Bit Code
[global RealMode]
[extern _main] ; this is in the c file
;;;;;;;;;;;;;
; Real Mode ;
;;;;;;;;;;;;;
RealMode:
xor ax, ax ; set up segments
mov es, ax
mov ds, ax
mov ss, ax
mov sp, ax
mov si, welcome
call print_string
cli ; clear interrupts
lgdt [gdtr] ; load GDT via GDTR (defined in file "gtd.inc")
; we actually only need to do this ONCE, but for now it doesn't hurt to do this more often when
; switching between RM and PM
in al, 0x92 ; switch A20 gate via fast A20 port 92
cmp al, 0xff ; if it reads 0xFF, nothing's implemented on this port
je .no_fast_A20
or al, 2 ; set A20_Gate_Bit (bit 1)
and al, ~1 ; clear INIT_NOW bit (don't reset pc...)
out 0x92, al
jmp .A20_done
.no_fast_A20: ; no fast shortcut -> use the slow kbc...
call empty_8042
mov al, 0xD1 ; kbc command: write to output port
out 0x64, al
call empty_8042
mov al, 0xDF ; writing this to kbc output port enables A20
out 0x60, al
call empty_8042
.A20_done:
mov eax, cr0 ; switch-over to Protected Mode
or eax, 1 ; set bit 0 of CR0 register
mov cr0, eax ;
empty_8042:
call Waitingloop
in al, 0x64
cmp al, 0xff ; ... no real kbc at all?
je .done
test al, 1 ; something in input buffer?
jz .no_output
call Waitingloop
in al, 0x60 ; yes: read buffer
jmp empty_8042 ; and try again
.no_output:
test al, 2 ; command buffer empty?
jnz empty_8042 ; no: we can't send anything new till it's empty
.done:
ret
print_string:
mov ah, 0x0E
.loop_start:
lodsb ; grab a byte from SI
test al, al ; test AL
jz .done ; if the result is zero, get out
int 0x10 ; otherwise, print out the character!
jmp .loop_start
.done:
ret
Waitingloop:
mov ebx,0x9FFFF
.loop_start:
dec ebx
jnz .loop_start
ret
Da liegt der Knackpunkt: 16/32 Bit gemischt in kernel.asm.
Vielleicht kann man das umbauen (ich sehe gerade, dass ich org 0x8000 schon auskommentiert habe, das geht offenbar bereits über das Linker-Skript), damit man coff-Format(?) verwenden kann. Vielleicht kann man auch in zwei Hälften (16 Bit u. 32 Bit Teil trennen, den ersten könnte man als bin resultieren lassen, den zweiten als o(bjekt)-Datei wegen des 'call _main')
Die wating-loop am Ende des 16-Bit-Teils sieht auch nicht sauber aus (war vorher im 32-Bit-Teil, wurde aber aus 16 Bit aufgerufen.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 20:45:39 16.06.2009, insgesamt 3-mal bearbeitet
Da liegt der Knackpunkt: 16/32 Bit gemischt in kernel.asm.
aber das ist doch ok so, oder? pc-kisten starten nun mal im real-mode, dein asm-code schaltet in den protected mode und ruft die 'main' des kernels auf. von nun an läuft alles im protected mode ab, egal ob C oder assembler. selbst die portabelsten betriebssysteme haben ein paar plattformabhängige assemblermodule. ich weiss garnicht, was dich daran stört, oder ich hab' heute ne besonders lange leitung.
Vielleicht stehe ich heute auf der Leitung. Ich benötige für kernel.asm das aout-Format (verkraftet beim Linken 16/32 bit gemischt) wegen des Linkers! Also ganz sachlich:
nasmw -O32 -f coff kernel.asm -o kernel.o
kernel.asm:21: error: COFF format does not support non-32-bit relocations
kernel.asm:26: error: COFF format does not support non-32-bit relocations
kernel.asm:55: error: COFF format does not support non-32-bit relocations
make.exe: *** [all] Error 1
Ich habe das mit mingw (ist in code::blocks enthalten) noch mal probiert:
Zitat:
C:\Programme\CodeBlocks\MinGW\bin\ld: cannot perform PE operations on non PE output file 'ckernel.bin'.
Hmmm, siehe unten!
Kennt sich da jemand genau aus? Offensichtlich lösen die Cross-Compiler dieses Problem.
daher bin ich bei meinem DJGPP (gcc 3.1, ld 2.13) und aout geblieben, habe aber das Gefühl, das man da einen Schritt vorwärts kommen sollte.
Kann mir jemand die Hintergründe, warum man diesen Cross-Compiler bauen muss, mal erklären. Da hat sich doch gegen "früher" etwas verschlechtert, denn ich arbeite doch mit dem DJGPP und dem aout-Format sehr gut! Das Problem ist der Wechsel von aout nach elf.
Creating a dedicated (cross-) compiler for your OS development work can save you many headaches. If...
* ...your system compiler drags in references to alloca() or other OS-dependent things,
* ...the ld linker complains about "PE operation on non-PE file",
* ...your compiler and your assembler can't agree on binary formats ("unresolved reference to _kmain()"), or
* ...your bootloader stubbornly insists that it cannot read your kernel binary,
* ...you are using Mac OS
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 22:55:37 16.06.2009, insgesamt 9-mal bearbeitet
ok, du kannst nasm- und gcc-output zusammenlinken, also ist doch alles gut. ich sehe jedenfalls keinen grund, die tools zu wechseln (ein solcher wäre für mich z.b. ein ernsthafter bug des gcc oder nasm).
btw, aber wenn du unbedingt wechseln willst, kannste das auch zu einem beliebigen späteren zeitpunkt tun. ist also nix, was die weiterentwicklung deines OS grossartig beeinflussen würde.
Super Topic und Themenwahl!
Ich habe ein kleines Problem! Wenn meine Kernel.bin (ASM + C-Kernel) keine Größe hat, die ohne Rest durch 512 Bytes teilbar ist, dann erhalte Ich einen Triple Fault!
Anfangs habe Ich per "times ??? db 0" die Datei richtig vergrößert, jedoch ist dies bei JEDER Änderung des Kernels notwendig!
Kennt jemand eine Lösung, um dies zu umgehen oder wenigstens das Ergebnis des Linkens auf eine Größe von ?.00 KB zu bringen?
Gruß
Chris
PS: Ich arbeite mit Windoof Vista und habe DJGPP, wie überall angepriesen installiert und in Gebrauch!
_________________ * Der Computer arbeitet so schnell, weil er nicht denkt.
Ich benötige für kernel.asm das aout-Format (verkraftet beim Linken 16/32 bit gemischt) wegen des Linkers!
Der [BITS 16]-Code in der kernel.asm passt noch in die boot.asm. Dann stünde in der kernel.asm i.e. nur noch folgendes:
Assembler Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
1 2 3 4 5 6 7 8 9 10 11 12 13 14
[BITS 32]
[extern _main]
movax, 0x10
movds, ax; data descriptor --> data, stack and extra segment
movss, ax moves, ax xoreax, eax; null desriptor --> FS and GS
movfs, ax movgs, ax movesp, 0x200000 ; set stack below 2 MB limit
call _main ; ->-> C-Kernel
(...)
Assembler Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
[BITS 32]
[extern _main]
movax, 0x10
movds, ax; data descriptor --> data, stack and extra segment
movss, ax moves, ax xoreax, eax; null desriptor --> FS and GS
movfs, ax movgs, ax movesp, 0x200000 ; set stack below 2 MB limit
call _main ; ->-> C-Kernel
(...)
Assembler Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
[BITS 32]
[extern _main]
movax, 0x10
movds, ax; data descriptor --> data, stack and extra segment
movss, ax moves, ax xoreax, eax; null desriptor --> FS and GS
movfs, ax movgs, ax movesp, 0x200000 ; set stack below 2 MB limit
call _main ; ->-> C-Kernel
(...)
Damit wäre das Linker-Problem vom Tisch. Falls du ernsthaft GRUB in Erwägung ziehst, könntest du konsequenterweise die erste Hälfte von Teil 1 des Tutorials ersatzlos streichen. Das wäre schade.
Der [BITS 16]-Code in der kernel.asm passt noch in die boot.asm.
Ja, die Idee hatte ich gestern auch, nachdem der Code da jetzt relativ kurz ist.
Zitat:
Falls du ernsthaft GRUB in Erwägung ziehst, könntest du konsequenterweise die erste Hälfte von Teil 1 des Tutorials ersatzlos streichen. Das wäre schade.
Ja, das stimmt. Mir gefällt dieser Weg ohne GRUB und Windows auch sehr gut, vor allem, weil alle es anders machen. GRUB ist eben sehr komfortabel. Dafür ist man im Endeffekt an Linux und diesen Bootloader gebunden. Vielleicht schaffen wir es gemeinsam, den Bootloader bzw. kernel.asm (da ist ja Platz, da keine 512 Byte-Grenze mehr vorhanden) den Anforderungen entsprechend zu gestalten und GRUB zu umgehen.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 22:45:46 17.06.2009, insgesamt 1-mal bearbeitet
@+gjm+: hast Du die Trennung bereits praktisch durchgeführt?
Ich habe mal versuchsweise getrennt, stürzt aber noch beim Kernel laden ab.
Ansonsten müsste ich nochmal step-by-step versuchsweise den Code in kleineren Häppchen von kernel.asm nach boot.asm schleppen.
Dann könnte man nämlich mal coff testen.
Nachfolgender Code klappt (s.u.).
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 02:29:13 18.06.2009, insgesamt 5-mal bearbeitet
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; read kernel from floppy disk ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
mov [bootdrive], dl ; boot drive stored by BIOS in DL.
; we save it to [bootdrive] to play for safety.
load_kernel:
xor ax, ax ; mov ax, 0 => function "reset"
int 0x13
jc load_kernel ; trouble? try again
mov bx, 0x8000 ; set up start address of kernel
; set parameters for reading function
; 8-bit-wise for better overview
mov dl,[bootdrive] ; select boot drive
mov al, 59 ; read n sectors
mov ch, 0 ; cylinder = 0
mov cl, 2 ; sector = 2
mov dh, 0 ; head = 0
mov ah, 2 ; function "read"
int 0x13
jc load_kernel ; trouble? try again
; show loading message
mov si,loadmsg
call print_string
;;;;;;;;;;;;;
; A20-Gate ;
;;;;;;;;;;;;;
in al, 0x92 ; switch A20 gate via fast A20 port 92
cmp al, 0xff ; if it reads 0xFF, nothing's implemented on this port
je .no_fast_A20
or al, 2 ; set A20_Gate_Bit (bit 1)
and al, ~1 ; clear INIT_NOW bit (don't reset pc...)
out 0x92, al
jmp .A20_done
.no_fast_A20: ; no fast shortcut -> use the slow kbc...
call empty_8042
mov al, 0xD1 ; kbc command: write to output port
out 0x64, al
call empty_8042
mov al, 0xDF ; writing this to kbc output port enables A20
out 0x60, al
call empty_8042
.A20_done:
;;;;;;;;;;;;;;;;;;
; jump to kernel ;
;;;;;;;;;;;;;;;;;;
jmp 0x8000 ; address of kernel. "jmp bx" might also work.
;;;;;;;;;
; Calls ;
;;;;;;;;;
print_string:
mov ah, 0x0E ; VGA BIOS fnct. 0x0E: teletype
.loop:
lodsb ; grab a byte from SI
test al, al ; NUL?
jz .done ; if the result is zero, get out
int 0x10 ; otherwise, print out the character!
jmp .loop
.done:
ret
empty_8042:
call Waitingloop
in al, 0x64
cmp al, 0xff ; ... no real kbc at all?
je .done
test al, 1 ; something in input buffer?
jz .no_output
call Waitingloop
in al, 0x60 ; yes: read buffer
jmp empty_8042 ; and try again
.no_output:
test al, 2 ; command buffer empty?
jnz empty_8042 ; no: we can't send anything new till it's empty
.done:
ret
Waitingloop:
mov cx,0xFFFF
.loop_start:
dec cx
jnz .loop_start
ret
;;;;;;;;
; data ;
;;;;;;;;
bootdrive db 0 ; boot drive
loadmsg db "bootloader message: loading kernel ...",13,10,0
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; read kernel from floppy disk ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
mov [bootdrive], dl ; boot drive stored by BIOS in DL.
; we save it to [bootdrive] to play for safety.
load_kernel:
xor ax, ax ; mov ax, 0 => function "reset"
int 0x13
jc load_kernel ; trouble? try again
mov bx, 0x8000 ; set up start address of kernel
; set parameters for reading function
; 8-bit-wise for better overview
mov dl,[bootdrive] ; select boot drive
mov al, 59 ; read n sectors
mov ch, 0 ; cylinder = 0
mov cl, 2 ; sector = 2
mov dh, 0 ; head = 0
mov ah, 2 ; function "read"
int 0x13
jc load_kernel ; trouble? try again
; show loading message
mov si,loadmsg
call print_string
;;;;;;;;;;;;;
; A20-Gate ;
;;;;;;;;;;;;;
in al, 0x92 ; switch A20 gate via fast A20 port 92
cmp al, 0xff ; if it reads 0xFF, nothing's implemented on this port
je .no_fast_A20
or al, 2 ; set A20_Gate_Bit (bit 1)
and al, ~1 ; clear INIT_NOW bit (don't reset pc...)
out 0x92, al
jmp .A20_done
.no_fast_A20: ; no fast shortcut -> use the slow kbc...
call empty_8042
mov al, 0xD1 ; kbc command: write to output port
out 0x64, al
call empty_8042
mov al, 0xDF ; writing this to kbc output port enables A20
out 0x60, al
call empty_8042
.A20_done:
;;;;;;;;;;;;;;;;;;
; jump to kernel ;
;;;;;;;;;;;;;;;;;;
jmp 0x8000 ; address of kernel. "jmp bx" might also work.
;;;;;;;;;
; Calls ;
;;;;;;;;;
print_string:
mov ah, 0x0E ; VGA BIOS fnct. 0x0E: teletype
.loop:
lodsb ; grab a byte from SI
test al, al ; NUL?
jz .done ; if the result is zero, get out
int 0x10 ; otherwise, print out the character!
jmp .loop
.done:
ret
empty_8042:
call Waitingloop
in al, 0x64
cmp al, 0xff ; ... no real kbc at all?
je .done
test al, 1 ; something in input buffer?
jz .no_output
call Waitingloop
in al, 0x60 ; yes: read buffer
jmp empty_8042 ; and try again
.no_output:
test al, 2 ; command buffer empty?
jnz empty_8042 ; no: we can't send anything new till it's empty
.done:
ret
Waitingloop:
mov cx,0xFFFF
.loop_start:
dec cx
jnz .loop_start
ret
;;;;;;;;
; data ;
;;;;;;;;
bootdrive db 0 ; boot drive
loadmsg db "bootloader message: loading kernel ...",13,10,0
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; read kernel from floppy disk ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
mov [bootdrive], dl ; boot drive stored by BIOS in DL.
; we save it to [bootdrive] to play for safety.
load_kernel:
xor ax, ax ; mov ax, 0 => function "reset"
int 0x13
jc load_kernel ; trouble? try again
mov bx, 0x8000 ; set up start address of kernel
; set parameters for reading function
; 8-bit-wise for better overview
mov dl,[bootdrive] ; select boot drive
mov al, 59 ; read n sectors
mov ch, 0 ; cylinder = 0
mov cl, 2 ; sector = 2
mov dh, 0 ; head = 0
mov ah, 2 ; function "read"
int 0x13
jc load_kernel ; trouble? try again
; show loading message
mov si,loadmsg
call print_string
;;;;;;;;;;;;;
; A20-Gate ;
;;;;;;;;;;;;;
in al, 0x92 ; switch A20 gate via fast A20 port 92
cmp al, 0xff ; if it reads 0xFF, nothing's implemented on this port
je .no_fast_A20
or al, 2 ; set A20_Gate_Bit (bit 1)
and al, ~1 ; clear INIT_NOW bit (don't reset pc...)
out 0x92, al
jmp .A20_done
.no_fast_A20: ; no fast shortcut -> use the slow kbc...
call empty_8042
mov al, 0xD1 ; kbc command: write to output port
out 0x64, al
call empty_8042
mov al, 0xDF ; writing this to kbc output port enables A20
out 0x60, al
call empty_8042
.A20_done:
;;;;;;;;;;;;;;;;;;
; jump to kernel ;
;;;;;;;;;;;;;;;;;;
jmp 0x8000 ; address of kernel. "jmp bx" might also work.
;;;;;;;;;
; Calls ;
;;;;;;;;;
print_string:
mov ah, 0x0E ; VGA BIOS fnct. 0x0E: teletype
.loop:
lodsb ; grab a byte from SI
test al, al ; NUL?
jz .done ; if the result is zero, get out
int 0x10 ; otherwise, print out the character!
jmp .loop
.done:
ret
empty_8042:
call Waitingloop
in al, 0x64
cmp al, 0xff ; ... no real kbc at all?
je .done
test al, 1 ; something in input buffer?
jz .no_output
call Waitingloop
in al, 0x60 ; yes: read buffer
jmp empty_8042 ; and try again
.no_output:
test al, 2 ; command buffer empty?
jnz empty_8042 ; no: we can't send anything new till it's empty
.done:
ret
Waitingloop:
mov cx,0xFFFF
.loop_start:
dec cx
jnz .loop_start
ret
;;;;;;;;
; data ;
;;;;;;;;
bootdrive db 0 ; boot drive
loadmsg db "bootloader message: loading kernel ...",13,10,0
Frage: wie werde ich den 16 bit Teil in kernel.asm noch los? Ich springe ja vom bootloader nach 0x8000 (Kernelstart).
Ganz einfach: Im Bootloader in den Protected Mode schalten (lgdt und cr0 laden), und dann mit jmp 0x08:0x8000 in den Kernel springen (d.h. im Bootloader ist auch kein 32 Bit Teil). Im Kernel kannst du dann direkt mit 32 Bit Code anfangen, und musst die Segmentregister erstmal laden. Danach solltest du dann nochmal eine GDT im Kernel laden, falls der Bootloader irgendwann überschrieben wird.
; jmp 0x8000
jmp 0x8:0x8000 ; Ist zwar ein 16-bit-FAR-Jmp, allerdings
; wird hier CS mit "index" 8 der GDT geladen (da in PM).
; Deshalb wird der Code ab Sprungziel von der CPU
; als 'BITS 32' interpretiert (s. CODE_Desc in der gdt.inc).
(...)
%include 'gdt.inc' ; <- Achtung : die gdt.inc ist nun hier und nicht mehr in der kernel.asm.
; jmp 0x8000
jmp 0x8:0x8000 ; Ist zwar ein 16-bit-FAR-Jmp, allerdings
; wird hier CS mit "index" 8 der GDT geladen (da in PM).
; Deshalb wird der Code ab Sprungziel von der CPU
; als 'BITS 32' interpretiert (s. CODE_Desc in der gdt.inc).
(...)
%include 'gdt.inc' ; <- Achtung : die gdt.inc ist nun hier und nicht mehr in der kernel.asm.
; jmp 0x8000
jmp 0x8:0x8000 ; Ist zwar ein 16-bit-FAR-Jmp, allerdings
; wird hier CS mit "index" 8 der GDT geladen (da in PM).
; Deshalb wird der Code ab Sprungziel von der CPU
; als 'BITS 32' interpretiert (s. CODE_Desc in der gdt.inc).
(...)
%include 'gdt.inc' ; <- Achtung : die gdt.inc ist nun hier und nicht mehr in der kernel.asm.
times 510-($-$$) hlt db 0x55
db 0xAA
Nun sollte nur noch in der boot.asm "BITS 16"-Code sein.
Das macht hier keinen Sinn weil gelten würde : "alte GDT == neue GDT". Ausserdem müssten dann die Segmentregister erst wieder mit den Deskriptoren der "neuen" GDT initialisiert werden bevor sie "Wirkung" zeigen.
Zitat:
C:\DJGPP\bin/ld.exe: warning: cannot find entry symbol KernelStart; defaulting to 00008000 <--- Wieso dies?
Das Symbol (Label ?) "KernelStart" ist nirgendwo in der kernel.asm definiert.
ich lade GDT dort auf Anraten nochmals, ...
Das macht hier keinen Sinn weil gelten würde : "alte GDT == neue GDT". Ausserdem müssten dann die Segmentregister erst wieder mit den Deskriptoren der "neuen" GDT initialisiert werden bevor sie "Wirkung" zeigen.
Also wieder weg.
Der Bereich unter 08:0x8000 wird später nicht beschrieben.
Zitat:
Das Symbol (Label ?) "KernelStart" ist nirgendwo in der kernel.asm definiert.
Mit aout war/ist das kein Problem. Braucht coff das anders? Habe da noch nichts gefunden.
kernel.asm:
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
[Bits 32]
[global KernelStart]
KernelStart:
mov ax, 0x10
mov ds, ax ; data descriptor --> data, stack and extra segment
mov ss, ax
mov es, ax
xor eax, eax ; null desriptor --> FS and GS
mov fs, ax
mov gs, ax
mov esp, 0x200000 ; set stack below 2 MB limit
[extern _main] ; entry point in ckernel.c
call _main ; ->-> C-Kernel
jmp $
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
[Bits 32]
[global KernelStart]
KernelStart:
mov ax, 0x10
mov ds, ax ; data descriptor --> data, stack and extra segment
mov ss, ax
mov es, ax
xor eax, eax ; null desriptor --> FS and GS
mov fs, ax
mov gs, ax
mov esp, 0x200000 ; set stack below 2 MB limit
[extern _main] ; entry point in ckernel.c
call _main ; ->-> C-Kernel
jmp $
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
[Bits 32]
[global KernelStart]
KernelStart:
mov ax, 0x10
mov ds, ax ; data descriptor --> data, stack and extra segment
mov ss, ax
mov es, ax
xor eax, eax ; null desriptor --> FS and GS
mov fs, ax
mov gs, ax
mov esp, 0x200000 ; set stack below 2 MB limit
[extern _main] ; entry point in ckernel.c
call _main ; ->-> C-Kernel
jmp $
Das war doch vorher auch nicht anders (da hatte ich nur RealMode).
Warum ist COFF so seltsam?
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 21:06:36 18.06.2009, insgesamt 2-mal bearbeitet
Wenn wir schon bei so grausamen Themen sind. Ich wollte mal mein opulentes makefile umstellen auf eine variable Darstellung (vor allem, damit man compiler leicht tauschen kann). Hier klappt aber die Umsetzung c --> o und asm --> coff noch nicht, wenn ich das richtig sehe:
G:\OSDev\Test\49>make
nasmw -f bin file_data.asm -o file_data.dat
nasmw -O32 -f bin boot.asm -o boot.bin
make -s link
C:\DJGPP\bin/ld.exe: cannot open kernel.o: No such file or directory (ENOENT)
make.exe[1]: *** [link] Error 1
make.exe: *** [all] Error 2
Offensichtlich baut er keine xxx.o aus den xxx.c. und kein coff aus asm.
Wo ist der Hauptfehler? Baue so was sonst nie selbst.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 22:14:39 18.06.2009, insgesamt 2-mal bearbeitet
Nein ich wollte das aktuelle makefile komplett durch dieses ersetzen.
habe folgendes noch geändert:
Code:
.asm.o:
$(NASM) $(ASFLAGS) $<
Code:
.asm.o:
$(NASM) $(ASFLAGS) $<
Code:
.asm.o:
$(NASM) $(ASFLAGS) $<
Da stand vorher s.o.:
Zitat:
G:\OSDev\Test\49>make
nasmw -f bin file_data.asm -o file_data.dat
nasmw -O32 -f bin boot.asm -o boot.bin
make -s link
C:\DJGPP\bin/ld.exe: cannot open ckernel.o: No such file or directory (ENOENT)
make.exe[1]: *** [link] Error 1
make.exe: *** [all] Error 2
Ich sehe aber kein kernel.o im Verzeichnis.
Wenn ich das -s(ilent) weg nehme:
Zitat:
G:\OSDev\Test\49>make
nasmw -f bin file_data.asm -o file_data.dat
nasmw -O32 -f bin boot.asm -o boot.bin
make link
make.exe[1]: Entering directory `g:/OSDev/Test/49'
ld -T kernel.ld -Map kernel.map -o ckernel.bin kernel.o ckernel.o isr.o video.o
flush.o gdt.o idt.o isrs.o irq.o util.o math.o timer.o keyboard.o process.o orde
red_array.o paging.o kheap.o descriptor_tables.o task.o fs.o initrd.o syscall.o
C:\DJGPP\bin/ld.exe: cannot open ckernel.o: No such file or directory (ENOENT)
make.exe[1]: *** [link] Error 1
make.exe[1]: Leaving directory `g:/OSDev/Test/49'
make.exe: *** [all] Error 2
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 22:40:43 18.06.2009, insgesamt 1-mal bearbeitet
Das macht hier keinen Sinn weil gelten würde : "alte GDT == neue GDT". Ausserdem müssten dann die Segmentregister erst wieder mit den Deskriptoren der "neuen" GDT initialisiert werden bevor sie "Wirkung" zeigen.
Der Zweck ist wie gesagt Problemen vorzubeugen, wenn der Bootsektor überschrieben wird, weil die GDT beim LGDT nicht in die CPU geladen wird. Wenn die GDT, die sich außerhalb des Kernels befindet, überschrieben wird, weil die Speicherverwaltung den Bereich, wo der Bootsektor war, als freien Speicher empfinden, knallt es beim Nachladen der Segmentregister.
Das Laden der Segmentregister ist nicht zwingend notwendig, sofern alte und neue GDT den selben Inhalt haben. (Und wenn die Segmentregister nicht aus Versehen mit alten Selektoren geladen werden, ist es auch kein Problem eine neue GDT anzulegen.)
Aber wenn Erhard die guten 32 KByte RAM da unten verrotten lassen will, soll mir recht sein. Hat er ja schließlich für bezahlt. ^^
Erhard Henkes schrieb:
Ich sehe aber kein kernel.o im Verzeichnis.
Weil du kein Target abhängig von den $(SOURCES) gemacht hast. Make weiß nicht, dass die Objektdateien vorher gebaut werden müssen, weil du es ihm nicht gesagt hast.
kümmert sich bereits darum. Wenn das nur faul rum hängt, fliegt's raus.
.c.o sei auch obsolet, habe ich irgendwo gelesen. Dieses $@ $< finde ich auch irgendwie unlesbaren Mist.
Die sechs Assemblerzeilen schreibe ich nun lieber fix rein, nur mit Variable für Format und Flags.
TARGET des Link-Vorgangs ist für mich ckernel.bin, oder?
So wird brav kompiliert, ich kann den Compiler und die Flags zumindest variabel austauschen und verstehe das makefile noch bestens. Das doppelte Aufführen der Dateinamen muss allerdings noch weg, aber unbedingt leserlich und klar nach vollziehbar.
image:
cmd /c copy /b boot.bin + ckernel.bin MyOS
del *.coff
del *.o
del *.bin
cmd /c rename MyOS MyOS.bin
del MyOS
partcopy MyOS.bin 0 7000 -f0
Wie kann ich diesen Teil mit "compile:" allgemein ablaufen lassen analog dem Linker-Befehl? Da die o alle aus c kommen, sollte das gehen.
Sollte man die o (OBJECTS) oder die c (SOURCES, wie unten stehend) auflisten? Ich habe das mit den obj-Dateien bevorzugt, also den Linker-Vorgang in den Mittelpunkt gerückt, weil ja auch obj-Dateien von NASM zu verarbeiten sind.
Bei makefiles stehe ich immer grundsätzlich auf dem Schlauch, weil jedes Vorbild anders aussieht und die Tutorials absolut nix taugen, weil diese keine klare Linie vorgeben. Vielleicht gibt es auch einfach keine.
So in etwa findet man es z.B. (Bsp. mit C++, mit diesem obsolet-Stil .cpp.o, den Linker finde ich dort z.B. nicht, Compile- und Link-Vorgang verquickt, irgendwie viel unverständliches Zeugs, die letzte Zeile habe ich ja oben gerade gekillt, weil sie noch nicht gearbeitet hat, lag da nur so rum, ich habe sie aber verstanden trotz wildcards):
Am schlimmsten finde ich so etwas "SOURCES:.cpp=.o" mit diesem Doppelpunkt.
Vielleicht kann mir da jemand auf die Sprünge helfen. Ich finde es gut, wenn Compile- und Link-Vorgang getrennt dargestellt werden, weil dies bei einem OS wichtig ist.
So etwas wie
$(CC) $(CFLAGS) -c $(SOURCES) -o $(OBJECTS)
wäre gut verständlich. Dann müsste man aber die c-Dateien noch allgemein als Sources angeben. Habe dies probiert, hat aber nicht geklappt. Vielleicht kann jemand meine Idee doch noch zum Laufen bringen:
Aber wenn Erhard die guten 32 KByte RAM da unten verrotten lassen will, soll mir recht sein. Hat er ja schließlich für bezahlt. ^^
Ja, aber man muss ja auch andere denken. Der Speicher bis 0x00008000 sollte nach dem Boot-Vorgang schon wieder genutzt werden können, aber irgendwo muss das GDT ja dann liegen, z.B. nach dem Kernel-Ende:
Das gute PrettyOS hat das ja alles schon im C-Kernel eingebaut, wenn ich das richtig sehe, und das wird in main() nach Bildschirmlöschen und Begrüßen umgehend installiert:
; flush.asm -- contains global descriptor table and interrupt descriptor table setup code
GLOBAL _gdt_flush ; Allows the C code to call gdt_flush().
_gdt_flush:
mov eax, [esp+4] ; Get the pointer to the GDT, passed as a parameter.
lgdt [eax] ; Load the new GDT register
mov ax, 0x10 ; 0x10 is the offset in the GDT to our data segment
mov ds, ax ; Load all data segment selectors
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
jmp 0x08:.flush ; 0x08 is the offset to our code segment: Far jump!
.flush:
ret
GLOBAL _idt_flush ; Allows the C code to call idt_flush().
_idt_flush:
mov eax, [esp+4] ; Get the pointer to the IDT, passed as a parameter.
lidt [eax] ; Load the IDT register
ret
GLOBAL _tss_flush ; Allows our C code to call tss_flush().
_tss_flush:
mov ax, 0x2B ; Load the index of our TSS structure - The index is
; 0x28, as it is the 5th selector and each is 8 bytes
; long, but we set the bottom two bits (making 0x2B)
; so that it has an RPL of 3, not zero.
ltr ax ; Load 0x2B into the task state register.
ret
; flush.asm -- contains global descriptor table and interrupt descriptor table setup code
GLOBAL _gdt_flush ; Allows the C code to call gdt_flush().
_gdt_flush:
mov eax, [esp+4] ; Get the pointer to the GDT, passed as a parameter.
lgdt [eax] ; Load the new GDT register
mov ax, 0x10 ; 0x10 is the offset in the GDT to our data segment
mov ds, ax ; Load all data segment selectors
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
jmp 0x08:.flush ; 0x08 is the offset to our code segment: Far jump!
.flush:
ret
GLOBAL _idt_flush ; Allows the C code to call idt_flush().
_idt_flush:
mov eax, [esp+4] ; Get the pointer to the IDT, passed as a parameter.
lidt [eax] ; Load the IDT register
ret
GLOBAL _tss_flush ; Allows our C code to call tss_flush().
_tss_flush:
mov ax, 0x2B ; Load the index of our TSS structure - The index is
; 0x28, as it is the 5th selector and each is 8 bytes
; long, but we set the bottom two bits (making 0x2B)
; so that it has an RPL of 3, not zero.
ltr ax ; Load 0x2B into the task state register.
ret
; flush.asm -- contains global descriptor table and interrupt descriptor table setup code
GLOBAL _gdt_flush ; Allows the C code to call gdt_flush().
_gdt_flush:
mov eax, [esp+4] ; Get the pointer to the GDT, passed as a parameter.
lgdt [eax] ; Load the new GDT register
mov ax, 0x10 ; 0x10 is the offset in the GDT to our data segment
mov ds, ax ; Load all data segment selectors
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
jmp 0x08:.flush ; 0x08 is the offset to our code segment: Far jump!
.flush:
ret
GLOBAL _idt_flush ; Allows the C code to call idt_flush().
_idt_flush:
mov eax, [esp+4] ; Get the pointer to the IDT, passed as a parameter.
lidt [eax] ; Load the IDT register
ret
GLOBAL _tss_flush ; Allows our C code to call tss_flush().
_tss_flush:
mov ax, 0x2B ; Load the index of our TSS structure - The index is
; 0x28, as it is the 5th selector and each is 8 bytes
; long, but we set the bottom two bits (making 0x2B)
; so that it has an RPL of 3, not zero.
ltr ax ; Load 0x2B into the task state register.
ret
Dieses gdt_flush wird mittels gdt_install in main() aufgerufen:
image:
cmd /c copy /b boot.bin + ckernel.bin MyOS
del *.o
del *.bin
cmd /c rename MyOS MyOS.bin
floppyimage:
partcopy MyOS.bin 0 7000 -f0
# $< Erste Abhängigkeit
# $@ Name des Targets
# $+ Liste aller Abhängigkeiten
Trotz Verwendung von $<, $+ und $@ finde ich diesen ziel- bzw. abhängigkeitsbezogenen Stil ebenfalls gut nachvollziehbar.
Ich bin immer wieder verblüfft über die vielfältigen Möglichkeiten, die diese Makefile-Syntax bietet.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 09:39:06 20.06.2009, insgesamt 4-mal bearbeitet
Von den vielfältigen Möglichkeiten abgesehen, die man in 20 Jahren nicht mal genutzt hat - und die noch vielfältigeren, die man noch gar nicht kennt.
Und bei den ganzen Tools, die in einem Compilerpaket immer dabei sind, fehlt mir immer noch eines, was komplexe zeilenübergreifende Ersetzungen mit Berücksichtigung von Klammerebenen erlaubt. Reguläre Ausdrücke sind viel zu schwach. Aber das wäre ein anderes Projekt
Da das Verständnis für das Thema (Virtual) Filesystem/Files (Filesystem-Header, File-Header) wichtig ist, habe ich am Beispiel der RAM Disk manuell (mit dem Hex-Editor, damit man mitdenken und abzählen muss, was man macht) ein viertes File (nämlich genau unser winziges 29-Byte-TEST-Programm) in das Filesystem "eingeklinkt". Die Daten werden via Filesystem (Zahl 4 im Filesystem-Header zeigt 4 Files an, File-Header zeigt Name, Offset und Länge an, allerdings nicht Filetyp oder anderes, alos noch sehr primitiv, aber es geht ja noch ums Prinzip) gefunden, in ein lokales Array auf dem Stack gelesen, zur Sicherung an eine neue Stelle in ein globales Array (address_TEST in der Sektion bss; da überlege ich noch, was eigentlich der beste Platz ist) transferiert, im Prozess 'test' als Funktion aufgerufen und ausgeführt.
Damit wurde nun genau genommen ein (zugegebenermaßen kleines) Programm "von Disk geladen" und in Multitasking "ausgeführt". Das Grundprinzip funktioniert aber auch im User-Bereich mit Datenbereich, Stack, User-Lib etc. gleich.
Ich verzichte bei Systemaufrufen auf einen Task-Wechsel und verwende den fault_handler (exceptions) und syscall_handler zum Aufruf der gewünschten Kernel-Funktion. Die Assemblerroutine kümmert sich um den korrekten Kontextwechsel von Ring 3 nach Ring 0 und zurück.
http://www.henkessoft.de/OS_Dev/Bilder/syscall.PNG
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 03:05:18 24.06.2009, insgesamt 1-mal bearbeitet
Ein ganz wichtiger Punkt ist nun der Einsatz eines Cross-Compilers/-Linkers, der vor allem auch das ELF-Format erlaubt, das man für andere Hobby-OS und später für User-Programme benötigt. Man benötigt allerdings auch ein brauchbares make-Tool, das alle Anforderungen erfüllt, findet man nun alles hier, natürlich alles einfach und übersichtlich nachzuvollziehen:
http://www.henkessoft.de/OS_Dev/OS_Dev2.htm#mozTocId42018
Wichtig ist, dass man nun aus Assembler-Quellcode leicht elf-object- und mittels des linkers der cross-compiler-tools daraus elf-executable-Dateien herstellen kann. Es ist nun die nächste Aufgabe diese elf-Programme seitens Pretty-OS im "user space" zum Laufen zu bringen. Dann kann man zur Zeit mittels incbin und Einbinden in die RAM Disk Programme im VFS "loadable" zur Verfügung stellen.
Dort sollte z.B. später die Programme
init,
vielleicht noch einge Treiber
und eine shell
stehen. Das wäre dann eine interessante Aufgabe für das Assembler-Forum, solche kleinen Programme in Assembler zu erstellen.
User-Land macht Fortschritte. User-Programme werden momentan noch via incbin in die RAM Disk geladen, dort mit VFS gefunden und in den User-Page-Bereich geschoben. Dort greifen sie via syscalls (int 0x7F) auf die in syscall.h/.c freigegebenen Kernel-Funktionen zu.
Nun gäbe es z.B. folgende Aufgaben:
- elf-exec-Parser
- syscalls systematisch aufbauen
- user-Programme schreiben
- user-lib entwickeln
- user-Programme init und shell schreiben
- Treiber
Hat jemand gute Ideen, was man auf dem jetzigen Stand alles so machen könnte mit kleinen Assembler-Programmen (passt zum Unter-Forum) und dazu passenden Syscalls?
Eine Frage ist auch, wie man das Arrangement der Syscalls sinnvoll aufbaut. Vielleicht hat hier jemand eine klare Meinung?
Auf jeden Fall kann man Teil 2 des Tutorials so langsam beenden, denn wesentliche Elemente eines durch einen eigenen Bootloader hochgefahrenen OS auf dem 80x86 wurden bisher konkret gezeigt.
Nun stellt sich die Frage, was man in Teil 3 sinnvoll angehen könnte:
Es kommt bei der Weiterentwicklung von PrettyOS z.B. darauf an, welchen Kernel-Typ man anstrebt. Bei einem monolithischer Kernel umfassen die Syscalls z.B. die POSIX-Funktionen, Win32API bzw. irgendeine eigene API. Dies verlagert die komplette Funktionalität eines Betriebssystems in den Kernel. Beim Mikrokernel-Typ sind die Syscalls vor allem IPC, Speichermanagement und etwas Prozessmanagement. Die IPC stellt die Kommunikation zwischen den Prozessen sicher, um die jeweils benötigtge Funktionalität zu triggern. Der Kernel brennt dann inhaltlich eher auf Sparflamme.
Darüber hinaus könnte man verschiedene Treiber anbieten. Bisher haben wir die Hardware des PC geschont, so dass Ladevorgänge via Emulator oder in einem Rutsch beim Booten von Floppy Disk abliefen.
Falls jemand konkret Lust hat, mich bei (Re-)Design des Kernels, Definition der syscalls und Aufbau der User-Applikations-Landschaft (NASM-Assembler oder nach Aufbau einer API-Lib für die syscalls auch in C) zu unterstützen, da wäre ich nicht abgeneigt. Kein Zeitdruck. Ich denke nicht, dass man das konkrete Development im Forum machen kann. Mein persönliches Ziel (gute Co-Autoren werden akzeptiert): Buch "Betriebssystementwicklung - leicht gemacht"
Wichtiger Meilenstein:
User-Programme in C mit userlib.h/.c anstelle Assembler-User-Programme. Echt gutes Gefühl, wenn so etwas endlich läuft.
Hier sollte sogar C++ möglich sein.
Mit was für einem Dateisystem arbeitest du? bzw. hast du schon eins?
Bei User-Programmen wird mit dem ELF-executable-Format gearbeitet. In der RAM disk wird ein von James Molloy selbst gestricktes Format eingesetzt (Quellcode für Erzeuger-Programm liegt bei). Bezüglich der "Datenträger" weiß ich noch nicht wie ich vorgehen soll. Diskettenlaufwerke sind sicher nicht mehr der Hit und an vielen PCs nicht mehr vorhanden. Alle Programme werden bisher via "incbin" innerhalb initrd.img in den Kernel transportiert und dort über das VFS und selbst gestrickte Dateisystem gefunden und "geladen".
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Ah ok.
Das hilft mir doch schonmal weiter.
Da ich nicht denke, das Diskettenlaufwerke untergehen sollten und da mein OS die Massen nicht interessieren wird, habe ich ein ext2-System auf der Floppy mit dem OS.
Ich hab gehofft du benutzt das auch und wüsstest einen Treiber dafür.
Aber scheint ja nicht der Fall zu sein.
Da ich nicht denke, das Diskettenlaufwerke untergehen sollten
Da stehst Du wohl nicht völlig alleine da mit dieser Meinung, aber aufhalten kann den Niedergang niemand mehr. Das Ende der drehenden Scheiben ist nahe.
Zitat:
ext2-System auf der Floppy mit dem OS
Da ich MS Windows als Host-System mit Cross-Compiler/-Linker für die OS-Entwicklung verwende, wollte ich mich - wenn überhaupt - eher auf ELF und FAT konzentrieren.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Der Bootloader und zugehörige Aufgaben vor dem Start der C-Datei ist bisher noch ein gehöriger Schwachpunkt. Die Ratschläge, endlich GRUB einzusetzen, verstummen nicht.
Musste allerdings noch die bss section nullen, das hat noch gefehlt.
Ich könnte echt einen BIOS / Bootloader / ... -Spezialisten brauchen.
Instabiler Code, der auf mehreren PCs getestet wurde und in einigen Fällen zu "Datenverlusten" in der RAM Disk führte, was dann teilweise #PF oder fehlendes User-Programm zur Folge hatte.
http://www.henkessoft.de/OS_Dev/Downloads/20090720_83.zip
(Fehler in fs.h/fs.c muss noch exakt lokalisiert und behoben/abgefangen werden)
Da das Problem eindeutig in dem verwendeten Code von James Molloy für VFS/RAM Disk liegt, wurde das in nachfoglendem Code einfach umgangen:
Hier ist ein stabiler Code, der VFS umgeht, solange der Fehler nicht exakt lokalisiert und behoben wurde: http://www.henkessoft.de/OS_Dev/Downloads/85.zip
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 10:45:10 23.07.2009, insgesamt 8-mal bearbeitet
Ich möchte auf eine hervorragende, bisher 20-teilige Serie (BrokenThorn Entertainment, "Mike") aufmerksam machen: http://www.brokenthorn.com/Resources/OSDev1.html
(didaktisch wirklich gut, da von Grund auf mit eigenem Bootloader und FAT12 System gearbeitet wird; leider noch einige kleine Fehler enthalten, Author wurde von mir über diese informiert)
Zitat:
Ich hab gehofft du benutzt das auch und wüsstest einen Treiber dafür.
Vielleicht sind die ersten Teile des obigen Tutorials für Dich das richtige, verwendet allerdings FAT12 anstelle ext2.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 11:43:05 24.07.2009, insgesamt 2-mal bearbeitet
Das Thema Analyse des physikalischen Speichers wurde nun auch erfolgreich umgesetzt: http://www.henkessoft.de/OS_Dev/OS_Dev3.htm#mozTocId584885
Allerdings haben wir mit int 15h eax = 820h die 80er verlassen und sind auf neuere Zeiten (spätestens ab 2002, funktioniert aber vielfach bereits bei früheren PCs) umgestiegen.
Der Bootloader ist nun allerdings voll gestopft. Platz für FAT12 ist dort nun nicht. Wir verwenden ja noch "raw format". Die Frage ist, ob man sich überhaupt noch Gedanken über Floppy Disk machen soll.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 11:40:35 24.07.2009, insgesamt 1-mal bearbeitet
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?
Für meine Studienarbeit habe ich einen Echtzeit-fähigen Mikrokernel entwickelt mit einem EDF Scheduler. Während der Diplomarbeit erweitere ich den um energieeffiziente Schedulingalgorithmen. Das ganze läuft auf einem gumstix connex400 mit einem Intel PXA255 (ARMvt5). Code darf ich noch nicht veröffentlichen
_________________ "Computers are like Old Testament gods; lots of rules and no mercy" by Joseph Campbell
@supertux: Klingt echt toll! Davon musst Du hier berichten, sobald Du dies darfst. Aber vielleicht kannst Du mit Deinen Erkenntnissen hier einige wichtige Weichenstellungen kommentieren und beeinflussen. PrettyOS soll ja vor allem ein Experimentier-OS werden und Einsteiger in diese Materie ansprechen und ermuntern.
@supertux: Klingt echt toll! Davon musst Du hier berichten, sobald Du dies darfst. Aber vielleicht kannst Du mit Deinen Erkenntnissen hier einige wichtige Weichenstellungen kommentieren und beeinflussen. PrettyOS soll ja vor allem ein Experimentier-OS werden und Einsteiger in diese Materie ansprechen und ermuntern.
das hörst sich sehr interessant an, hab ein bisschem im Code nachgeschaut. Leider kenne ich mich mit x86 überhaupt nicht aus, also verstehe ich den Code und viele (für x86) Notwendige Sache gar nicht.
Ich muss meine Ausarbeitung in ca. 2 1/2 Wochen abgeben, dann Vortrag halten und so. Also erst danach werde ich wahrscheinlich meine Sachen (plus Sources) veröffentlichen können.
_________________ "Computers are like Old Testament gods; lots of rules and no mercy" by Joseph Campbell
Zuletzt bearbeitet von supertux am 14:36:57 25.07.2009, insgesamt 1-mal bearbeitet
Ich habe PrettyOS nun auch unabhängiger und stabiler gemacht. Da hatten sich nach dem Umstieg von DJGPP nach Cross-Tools doch einige unbehobene Schwachpunkte angesammelt. Nun kann man Kernel und User-Programme mit CFLAGS= -Werror -Wall -O -ffreestanding -fleading-underscore -nostdlib -nostdinc -fno-builtin kompilieren.
Physikalischer Speicher wird nun auch automatisch bestimmt und für das Paging (Anzahl der Frames = Phys. Memory / Pagesize) übernommen.
Hier habe ich wieder initrd und VFS verwendet, um die shell zu finden/laden:
http://www.henkessoft.de/OS_Dev/Downloads/90.zip
(dabei gibt es aber auf bestimmten PC noch Probleme, die ich bisher nicht lokalisieren und beheben kann):
Die Shell wurde als ELF-Programm via incbin (data.asm) über die Paarung RAM Disk / VFS lokalisiert und gestartet. Der visuelle Eindruck beginnt sich zu entwickeln.
Ich stelle mir für die Zukunft die Fragen:
- wie sollen die syscalls aussehen?
- was gehört in die user-lib?
- Sollte man Standards einsetzen? (POSIX, standardisierte C-userlib)
- Sollte man letztendlich doch GRUB einsetzen oder stur den eigenen Bootloader ausbauen? (niemand hetzt mich)
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 10:57:28 26.07.2009, insgesamt 2-mal bearbeitet
Wenn du mich fragst, ich würde auf jeden Fall einmal das Thema GRUB anschneiden.
Es soll immerhin ein OS- und kein Bootloader-Tutorial bleiben / werden.
Als erstes vorweg: ich beobachte die Entwicklung des Tutorials jetzt schon einige Zeit und bin echt begeistert! Mach auf jeden Fall weiter.
Zum Thema GRUB: Ich fände es schade um die viele Arbeit, die jetzt schon in den eigenen Bootloader investiert wurde. Außerdem find ich es gut, dass man so von Anfang an gut erklärt bekommt, wie so ein Betriebssystem von Grund auf aufgebaut ist. Wenn man den Anfang jetzt GRUB machen lässt, werden wieder Fragen wie "Wie läuft das denn eigentlich mit GRUB ab?" aufgeworfen. Es ist zwar viel Arbeit, doch ist der Lerneffekt zumindest für mich größer, wenn mit dem jetzigen Bootloader weitergearbeitet wird.
Übrigens habe ich festgestellt, dass der Bootloader sich im Bochs-Emulator bei folgenden IPS folgendermaßen verhält (Näherungswerte):
400.000 -> Bootloader wird garnicht erst geladen
400.050 -> Bootloader startet verzögert (ca. 7 Sekunden)
400.100 -> Bootloader startet verzögert (ca. 5 Sekunden)
400.500 -> Bootloader startet verzögert (ca. 1 Sekunde)
402.000 -> Bootloader startet vollkommen normal
Getestet wurde übrigens mit jeweils 8 MB RAM (das Niedrigste, ohne eine Fehlermeldung von alloc_frame() zu bekommen, dass es keine freien Frames mehr gebe, um anschließend (außer bei 7MB RAM) im "Page Fault >>> Exception. System Halted! <<<" zu enden )
Woran liegt das genau, warum sind so kleine Werte hierbei so entscheidend?
ich beobachte die Entwicklung des Tutorials jetzt schon einige Zeit und bin echt begeistert! Mach auf jeden Fall weiter.
Ich plane im Hintergrund bereits ein neues völlig eigenes OS, muss aber erst noch mehr Erfahrung sammeln. Bei OSDEV zählt Erfahrung mehr als jede noch so tolle Theorie. Jeder baut hier auf den anderen auf. MSDOS auf CP/M, Linux auf Minix, ...
Zitat:
Zum Thema GRUB: Ich fände es schade um die viele Arbeit, die jetzt schon in den eigenen Bootloader investiert wurde. Außerdem find ich es gut, dass man so von Anfang an gut erklärt bekommt, wie so ein Betriebssystem von Grund auf aufgebaut ist.
Sehe ich genau so! Ich war nur deutlich verunsichert, weil ich bei seltsamen Fehlern jedes Mal mühsam belegen muss, das es nicht am Bootloader hängt. Seit ich diesen Schwachpunkt in initrd (buffer overflow durch strcpy; behoben durch Einbau von strncpy in Version 0.1.0090, die läuft schon sehr stabil, aber ich weiß noch nicht, warum manche PC da Blödsinn machen) behoben habe, geht es mir besser. Der Bootloader ist schon echt klasse. Jetzt ist ja auch die Speichererkennung mit dabei. Man könnte noch ein FAT12 einbauen, dann könnte man den kernel als kernel.sys von Floppy laden. Aber das macht die Sache auch nicht besser.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
400.000 -> Bootloader wird garnicht erst geladen
400.050 -> Bootloader startet verzögert (ca. 7 Sekunden)
400.100 -> Bootloader startet verzögert (ca. 5 Sekunden)
400.500 -> Bootloader startet verzögert (ca. 1 Sekunde)
402.000 -> Bootloader startet vollkommen normal
Getestet wurde übrigens mit jeweils 8 MB RAM (das Niedrigste, ohne eine Fehlermeldung von alloc_frame() zu bekommen, dass es keine freien Frames mehr gebe, um anschließend (außer bei 7MB RAM) im "Page Fault >>> Exception. System Halted! <<<" zu enden )
Interessante Untersuchung, mach weiter so!
Der minimalistische Denkansatz gefällt mir!
Ram Disk at: 4008100Ch
<DIR> dev
35 file1
35 file2
35 file3
7719 dummy
1693 shell
$> hi <--
I am PrettyOS. Always at your service!
$> ? <--
Implemented Instructions: help ? hi
$> OK <--
Sorry, I do not know this command.
$>
Ich sehe aus praktischer Sicht vier Komponenten eines OS:
1) PC/BIOS
2) Bootloader
3) Kernel
4) Userspace
Durch Einsatz von GRUB schaltet man Fehler aus 1) und 2) aus. Dafür hat man GRUB am Hals.
Da wir ausgehend von MS Windows entwickeln, werden wir auf GRUB verzichten. Dieses Tutorial macht mir Mut: http://www.brokenthorn.com/Resources/OSDev1.html (zwar einige Fehler enthalten, aber bezüglich des didaktischen Ansatzes richtig gut). Allerdings überfrachtet es den Leser im Sinne eines Nachschlagwerkes mit Theorie-Details. Die Praxis geht dort fast unter.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 22:45:05 27.07.2009, insgesamt 8-mal bearbeitet
Ram Disk at: 4008100Ch
<DIR> dev
35 file1
35 file2
35 file3
7719 dummy
1693 shell
$> hi <--
I am PrettyOS. Always at your service!
$>
7MB:
Zitat:
loc_frame: no free frames!!!message from alloc_frame: no free frames!!!message f
rom alloc_frame: no free frames!!!message from alloc_frame: no free frames!!!mes
sage from alloc_frame: no free frames!!!message from alloc_frame: no free frames
!!!message from alloc_frame: no free frames!!!message from alloc_frame: no free
frames!!!message from alloc_frame: no free frames!!!message from alloc_frame: no
free frames!!!message from alloc_frame: no free frames!!!message from alloc_fra
me: no free frames!!!message from alloc_frame: no free frames!!!message from all
oc_frame: no free frames!!!message from alloc_frame: no free frames!!!message fr
om alloc_frame: no free frames!!!message from alloc_frame: no free frames!!!mess
age from alloc_frame: no free frames!!!message from alloc_frame: no free frames!
!!message from alloc_frame: no free frames!!!message from alloc_frame: no free f
rames!!!message from alloc_frame: no free frames!!!message from alloc_frame: no
free frames!!!message from alloc_frame: no free frames!!!message from alloc_fram
e: no free frames!!!message from alloc_frame: no free frames!!!Ram Disk at: 4008
100Ch
<DIR> dev
35 file1
35 file2
35 file3
7719 dummy
1693 shell
$> hi <--
I am PrettyOS. Always at your service!
$>
Stimmt! Allerdings ohne #PF, zumindest bei Version 0.1.0090
8 MB Minimum finde ich gar nicht übel.
PS: Ich sehe gerade: da gehört noch ein Space hinter "no free frames!!!"
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 23:16:04 27.07.2009, insgesamt 2-mal bearbeitet
Dieses Tutorial macht mir Mut: http://www.brokenthorn.com/Resources/OSDev1.html (zwar einige Fehler enthalten, aber bezüglich des didaktischen Ansatzes richtig gut). Allerdings überfrachtet es den Leser im Sinne eines Nachschlagwerkes mit Theorie-Details. Die Praxis geht dort fast unter.
Der Code davon gefällt mir aber persönlich ganz gut. Flags als Veroderung von Defines, Code aufgeteilt in Teilbereiche (wie Hal, Lib etc.) und die Trennung von Bootloader- und Kernelcode wären etwas was man sich abgucken könnte.
zu http://www.brokenthorn.com/Resources/OSDev20.html :
Der Aufbau gegliedert in boot.bin, kernel.sys und kernel.exe unter Verwendung von FAT12 und VC2008 Express ist vorbildhaft, da hast Du Recht. Echt klasse gemacht.
Ob VC++ 2005 od. 2008 unbedingt sein muss, da kann man sich streiten, aber professionell ist es auf jeden Fall. Man lernt halt wenig über makefile und linker script, was ich für einen OSDever für wichtig halte.
In boot.asm fehlen drei Doppelpunkte bei den Labels, aber auch nach Ergänzung (nasm motzt herum, warum der Autor dies nicht liest?) verschwindet der "Reset" beim Booten nicht. Liegt der Fehler bereits hier?
kernel.sys klappt gut, oder liegt hier im Code das Problem? In zip-Datei mitgeliefertes kernel.sys funktioniert auch nicht.
Kernel.exe: VC++ 2008 Express (bei der Demo sind noch die 2005er Projektdateien dabei, kann man aber updaten) gibt einige Warnungen aus, kompiliert/linkt aber alles und schreibt kernel.exe auf floppy disk.
Hast Du da praktische Tipps, wie man es zum Laufen bekommt, oder muss ich den Autor ansprechen? Auch mit Bochs 2.4.1 gibt es einen Reset (wie bei real PC).
Bei solchen Zeilen:
Assembler Code:
movax, 0x0000 ; set the stack
Assembler Code:
movax, 0x0000 ; set the stack
Assembler Code:
movax, 0x0000 ; set the stack
gibt es normalerweise Kritik (siehe Anmerkungen von NobuoT weiter vorne). Das müsste wirklich nicht sein.
Mein Fazit:
Tutorial: sehr umfassend, viele Rechtschreibfehler, didaktischer Stil gut
Grundlegender Code-Aufbau: Klasse! Davon kann man lernen.
Tools: Mit freien Windows-Tools (nasm, partcopy, VC++ 2005/08 Express) umsetzbar, kein GRUB, sondern eigener Bootloader mit FAT12-Erkennung!
Praktische Umsetzung/Fehlerfreiheit: negativ (bootet, resettet aber, Warnungen von nasm und VC++ 2008, offenbar schaut sich der Autor die Meldungen seiner Tools nicht an, ist mir aber auch schon passiert, typische Verdrängung, Hauptsache vorwärts)
Das ist wirklich schade, denn ansonsten ist das Tutorial hervorragend. Vielleicht liegt das Problem auch in Kernel.exe? Kannst Du Dich näher auslassen über deine Erfahrungen? Welche Demo-Version geht bei Dir?
Zitat:
Flags als Veroderung von Defines
Was gefällt Dir da besonders gut?
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 04:14:32 29.07.2009, insgesamt 5-mal bearbeitet
Ich habe noch nicht versucht den Code zu kompilieren und zu starten. Werd ich nacher mal ausprobieren. Habe den Code nur durchgesehen um ein paar Anregungen zu bekommen, da C nicht gerade meine Hauptsprache ist, und da sind mir die Sachen aufgefallen. Visual Studio ist auch nicht unbedingt meine Wahl für Betriebsystementwicklung, zumal da auch C++ im Spiel ist. Allerdings sieht das mit dem inline Assembler etwas eleganter aus als beim gcc.
Das mit den Defines ist mir hauptsächlich beim IDT und GDT aufgefallen:
Ist besser zu erkennen was gesetzt ist, als wenn da einfach als Flag 0x9A steht. Es ist natürlich im Tutorial erklärt, aber wenn man später auf den Code zurück kommt muss man es wieder nachlesen.
Edit: Habs jetzt ausprobiert (Demo15.zip aus Tutorial 20) und es läuft in Virtualbox und QEmu ohne Probleme. Wichtig war vor allem als Debug zu kompilieren, da Release anscheinend nicht richtig konfiguriert ist. Als beim Release Build 740 Fehler kamen wollte ich schon aufgeben. Ansonsten habe ich nichts umgestellt. Allerdings nutze ich Visual Studio Professional und kein Express, was noch einen Unterschied machen könnte.
Zuletzt bearbeitet von Tobiking2 am 11:53:28 29.07.2009, insgesamt 1-mal bearbeitet
Habs jetzt ausprobiert (Demo15.zip aus Tutorial 20) und es läuft in Virtualbox und QEmu ohne Probleme. Wichtig war vor allem als Debug zu kompilieren, da Release anscheinend nicht richtig konfiguriert ist.
Mit VC++ 2008 Express im Debug-Modus kompiliert, Release geht überhaupt nicht wegen massenweise Fehler. Auf der Diskette befindet sich hinterher kernel.sys und kernel.exe. habe auch mal die hal.dll dazu, bringt aber nichts, besteht ja auch keine Abhängigkeit der kernel.exe.
Keine Ahnung, wie Du das schaffst. Läuft nicht mit Virtual PC 2007 (bootet zwar, hinterher aber nur blauer Bildschirm mit blinkendem Cursor), überhaupt nicht mit Sun xVM VirtualBox und nicht mit Bochs 2.4.1. Dieser Zustand ist gelinde gesagt nicht gerade berauschend.
4 mb könnten aber knapp werden wenn das OS 8 mb braucht um zu booten
Erhard Henkes schrieb:
ARM ist für einige interessant. Vielleicht kannst Du auch einen passenden Emulator verlinken.
QEmu kann so einiges emulieren (http://www.qemu.org/status.html), darunter auch ARM und MIPS. Wobei es für den Embedded Bereich ja oft auch spezielle Development Kits mit allem drum und dran gibt.
Wobei es für den Embedded Bereich ja oft auch spezielle Development Kits mit allem drum und dran gibt.
sogar kostenlos. aber sein OS ständig im simulator laufen zu lassen, ist sehr unbefriedigend (wie alle software, die nur im computer läuft und nicht mit der realen welt interagiert, sondern nur mit einem benutzer, der vor der kiste sitzt).
Ist einiges an Luft drinnen, die man ablassen kann, wenn notwendig. 8 MB ist doch heute kein Thema mehr in einem echten PC.
ARM ist eine andere Welt. Die spielt hier aber keine Geige.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Trennung von Bootloader- und Kernelcode wären etwas was man sich abgucken könnte
@Tobiking2:
Ich habe für die Freunde des altehrwürdigen Floppy Disk Formates einen ersten Schuss mit den etwas abgeänderten Startdateien von brokenthorn http://www.brokenthorn.com/Resources/OSDev1.html (schon ziemlich viel Ballast abgeworfen) durchgeführt:
Als Tools: partcopy, nasm(w), cross-compiler
Die Startadresse von ckernel.sys habe ich nach 0x40000 verlegt. Klappt gut.
Läuft im Gegensatz zu brokenthorn in -Werror -Wall Qualität mit den Cross-Tools. Den Kernel habe ich als Binary gelinkt, damit man das Parsen der ELF-Datei einspart.
Lässt sich mittels Bochs und auf Real PC booten.
Da kann man nun im nächsten Schritt boot.asm / boot2.asm mit unseren bisherigen Routinen, z.B. für Memory-Bestimmung und Sektoren-Lesen bestücken, noch mehr Ballast abwerfen und einige Adressen noch abändern. Sobald alles robust steht, werde ich das (mit Hinweis auf brokenthorn u.a. als Vorlage) ins PrettyOS-Tutorial einbauen. Gutes Exempel für Second-Stage-Bootloader und Lesen von FAT12-Format.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 04:45:03 01.08.2009, insgesamt 6-mal bearbeitet
Die Diskette mit Fat12 zu formatieren und nur noch den Bootsektor mit partcopy zu beschreiben find ich auf jeden Fall eine gute Änderung. Was nun natürlich fehlt ist die Möglichkeit Userprogramme von der Diskette zu laden statt aus der initrd.
Mich hat nur etwas gewundert, dass du den Bootloader Code von Brokenthorn übernommen und deine Sachen hinzugefügt hast, statt deinen eigenen Code umzubauen und zu erweitern. Solange du damit zufrieden ist ist es ok, aber ich schreib normalerweise lieber Funktionen sinngemäß nach, als irgendetwas erstmal 1:1 zu übernehmen und dann anzupassen.
Mich hat nur etwas gewundert, dass du den Bootloader Code von Brokenthorn übernommen und deine Sachen hinzugefügt hast, statt deinen eigenen Code umzubauen und zu erweitern. Solange du damit zufrieden bist, ist es ok, aber ich schreib normalerweise lieber Funktionen sinngemäß nach, als irgendetwas erstmal 1:1 zu übernehmen und dann anzupassen.
Das war nur ein schneller Versuch, ob das Zusammenschmieden so klappt, wie ich mir das vorgestellt habe, und das hat es. Ich werde meinen Code um die notwendigen Teile erweitern, weil mir der bisherige Aufbau des Bootloaders besser gefällt als der Code von brokenthorn.
Die Erweiterung um einen second stage bootloader ist auf jeden Fall vorteilhaft, weil man Platz gewinnt. Das Laden von Diskette und später anderen Datenträgern ist ebenso ein Ziel.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
@ Tobiking2: Mich würde interessieren, wie Du die Demo15 von Tut20 bei brokenthorn zum Laufen bekommen hast. Ich verwende - wie bereits erwähnt - VC++ 2008 EE.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
@ Tobiking2: Mich würde interessieren, wie Du die Demo15 von Tut20 bei brokenthorn zum Laufen bekommen hast. Ich verwende - wie bereits erwähnt - VC++ 2008 EE.
Ich hab gerade nochmal frisch entpackt um zu prüfen das ich nicht irgendwo etwas verstellt habe und da ist mir aufgefallen, dass ich das Projekt nicht bereinigt habe und so die schon kompilierten libs benutzt habe. Nach einer kompletten Bereinigung läuft es bei mir auch nicht mehr.
Ich habe danach dann gezielt nur einzelnen Projekte bereinigt und es läuft solange ich nicht das Keyboard Projekt bereinige. Nachdem ich bei einem Diff zu Demo14 keinen unterschied gesehen habe, habe ich es dort ausprobiert und auch da tritt der Fehler auf sobald ich Keyboard selber compiliere. Demo12 ist die höchste die ich ohne Keyboard gefunden habe, da diese aber andere Fehler hat, hab ich da nicht weiter probiert.
Die brokenthorn downloads scheinen ein ziemlicher Software-Müllhaufen zu sein, allerdings mit einigen Edelsteinen darin.
Ich bin auch etwas verblüfft, da ich die gestern schnell zusammen geschusterte Version 94 zum Laufen bringen wollte, diese aber plötzlich im Kernel nur ein gelbes S auf den Screen brachte (wegen dieses Problems hatte ich extra den Kernel nach 0x40000 geschoben, wegen interner Kernel-Schiebereien im Brokenthorn-Code). Ich habe sogar meinen Upload getestet, ging auch nicht mehr. Gestern abend lief der gleiche Code mit Bochs und Floppy, bin etwas verwirrt. Läuft meine Version 94 bei Dir?
http://www.henkessoft.de/OS_Dev/Downloads/94.zip
Das Speichermanagement ist etwas seltsam, muss mal seine Einstiegstuts lesen, in denen er diesen Aufbau beschreibt.
Die GRUB-Verfechter würden sich wieder schief lachen über diese Krämpfe.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 18:57:31 31.07.2009, insgesamt 2-mal bearbeitet
Ich hab unter Windows keine ordentliche gcc Toolchain installiert um richtig zu testen. Wenn ich aber unter Linux den Kernel compilier krieg ich folgendes vom Linker:
Macht die Zeile: jmp dword 0x08:0x300060 ; offset 0x60 in kernel.elf
in boot2.asm sinn wenn der Kernel in 0x40000 liegt und kein ELF-Format mehr ist?
Nein, natürlich nicht. Völliger Blödsinn.
Da ist beim Hochladen Stage 2 durcheinander geraten.
Da muss alles genau passen. Auch die Namen stehen mehrfach im Code.
Kleiner Fehler bei Änderungen: alles kaputt.
Irgendwo habe ich mal einen Spruch gehört, Assembler Funktionen seien verdammt schnell, man müsse aber beim Programmieren der Assembler Funktionen verdammt langsam denken...
Klappt das bei Dir mit der Version 94? Dank FAT 12 kann man jetzt aus mehreren Versionen alles schön vermischen, spätestens auf der Floppy.
Wer keine Floppy hat, gibts natürlich auch virtuell (VFD).
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 22:53:51 31.07.2009, insgesamt 2-mal bearbeitet
Klappt das bei Dir mit der Version 94? Dank FAT 12 kann man jetzt aus mehreren Versionen alles schön vermischen, spätestens auf der Floppy.
Ja es klappt. Man muss aber natürlich drauf achten, dass Stage 1 und 2 zusammen passen. Wenn man ein altes Stage 1 oder 2 mit anderen Adressen benutzt kann das schnell schief gehen.
Erhard Henkes schrieb:
Wer keine Floppy hat, gibts natürlich auch virtuell (VFD).
Na, wenn das soweit klappt (bei Vers. 94 ist sogar initrd-Filesystem dabei, das bisher nicht überall lief), ist es wohl an der Zeit, sich nun den Themen Device-Treiber "Floppy Disk" sowie dem File-Format "FAT 12" zu widmen und First und Second Stage Bootloader auszubauen.
So sieht momentan nach einigen Speicheranpassungen der First Stage Bootloader aus:
;##############################################################################
; "FAT 12" file system is build up by four areas at Floppy Disk:
; - Boot Sector (also called BIOS Parameter Block, BPB)
; - File Allocation Table, FAT
; - Root Directory
; - Subdirectories and Files
; boot sector info block
; begins 3 bytes from start. A far jump needs 3 bytes.
OperatingSystemName db "PrettyOS' ; 8 byte
BytesPerSec dw 512 ; used in bootloader code
SecPerClus db 1 ; used in bootloader code
ReservedSec dw 1 ; used in bootloader code
NumFATs db 2 ; used in bootloader code
RootEntries dw 224 ; used in bootloader code
TotSec dw 2880
MediaType db 0xF0
FATSize dw 9 ; used in bootloader code
SecPerTrack dw 18 ; used in bootloader code
NumHeads dw 2 ; used in bootloader code
HiddenSec dd 0
TotSec32 dd 0
DriveNum db 0 ; used in bootloader code
Reserved db 0
BootSig db 0x29
VolumeSerialNum dd 0xD00FC0DE
VolumeLabel db "PrettyOS ' ; 11 byte
FileSys db "FAT12 ' ; 8 byte
;##############################################################################
;******************************************************************************
; bootloader entry point
;******************************************************************************
entry_point:
xorax, ax; set registers
movds, ax moves, ax movfs, ax movgs, ax
movax, 0x9000 ; set the stack
movss, ax xorsp, sp
mov [bootdevice], dl; store boot device
movsi, msgLoading
call print_string
Load_Root_Directory_Table:
; compute size of root directory and store in "cx"
xorcx, cx xordx, dx movax, 0x20 ; 32 byte directory entry
mul WORD [RootEntries] ; total size of directory
div WORD [BytesPerSec] ; sectors used by directory
xchgax, cx
; compute location of root directory and store in "ax"
moval, BYTE [NumFATs] ; number of FATs
mul WORD [FATSize] ; sectors used by FATs
addax, WORD [ReservedSec] ; adjust for bootsector
mov WORD [datasector], ax; base of root directory
add WORD [datasector], cx
; read root directory into memory (7E00h)
movbx, 0x7E00 ; copy root dir above bootcode
call ReadSectors
;******************************************************************************
; Find stage2 bootloader
;******************************************************************************
; browse root directory for binary image
movcx, WORD [RootEntries] ; load loop counter
movdi, 0x7E00 ; locate first root entry
.LOOP:
pushcx movcx, 0xB ; name has 11 characters
movsi, ImageName ; look for this image name
pushdi repcmpsb; test for entry match
popdi je Load_FAT
popcx adddi, 0x20 ; queue next directory entry
loop .LOOP
jmp FAILURE
;******************************************************************************
; Load File Allocation Table (FAT)
;******************************************************************************
Load_FAT:
; save starting cluster of boot image
movdx, WORD [di + 0x001A]
mov WORD [cluster], dx; file's first cluster
; compute size of FAT and store in "cx"
xorax, ax moval, BYTE [NumFATs] ; number of FATs
mul WORD [FATSize] ; sectors used by FATs
movcx, ax
; compute location of FAT and store in "ax"
movax, WORD [ReservedSec] ; adjust for bootsector
; read image file into memory (0500h)
xorax, ax moves, ax; destination for image
movbx, 0x0500 ; destination for image
pushbx
;******************************************************************************
; Load stage2 bootloader
;******************************************************************************
Load_Image:
movax, WORD [cluster] ; cluster to read
popbx; buffer to read into
call Convert_Cluster_to_LBA ; convert cluster to LBA
xorcx, cx movcl, BYTE [SecPerClus] ; sectors to read
call ReadSectors
pushbx
; compute next cluster
movax, WORD [cluster] ; identify current cluster
movcx, ax; copy current cluster
movdx, ax; copy current cluster
shrdx, 1 ; divide by two
addcx, dx; sum for (3/2)
movbx, 0x7E00 ; location of FAT in memory
addbx, cx; index into FAT
movdx, WORD [bx] ; read two bytes from FAT
testax, 1
jnz .ODD_CLUSTER
.EVEN_CLUSTER:
anddx, 0000111111111111b ; take low twelve bits
jmp .DONE
.ODD_CLUSTER:
shrdx, 4 ; take high twelve bits
.DONE:
mov WORD [cluster], dx; store new cluster
cmpdx, 0x0FF0 ; test for end of file
jb Load_Image
DONE:
movsi, msgCRLF
call print_string
movdl, [bootdevice]
push WORD 0x0000
push WORD 0x0500
retf
FAILURE:
movsi, msgFailure
call print_string
movah, 0x00
int 0x16 ; wait for keypress
int 0x19 ; warm boot reset
;******************************************************************************
; Convert CHS to LBA
; LBA = (cluster - 2) * sectors per cluster
;******************************************************************************
Convert_Cluster_to_LBA:
subax, 2 ; zero base cluster number
xorcx, cx movcl, BYTE [SecPerClus] ; convert byte to word
mulcx addax, WORD [datasector] ; base data sector
ret
;******************************************************************************
; Convert LBA to CHS
; AX LBA Address to convert
;
; absolute sector = (logical sector / sectors per track) + 1
; absolute head = (logical sector / sectors per track) MOD number of heads
; absolute track = logical sector / (sectors per track * number of heads)
;
;******************************************************************************
Convert_LBA_to_CHS :
xordx, dx; prepare dx:ax for operation
div WORD [SecPerTrack] ; calculate
incdl; adjust for sector 0
mov BYTE [Sector], dl xordx, dx; prepare dx:ax for operation
div WORD [NumHeads] ; calculate
mov BYTE [Head], dl mov BYTE [Cylinder], al ret
;******************************************************************************
; Reads sectors
; CX Number of sectors to read
; AX Starting sector
; ES:BX Buffer to read to
;******************************************************************************
ReadSectors:
.NEXTSECTOR:
movdi, 5 ; five retries for error
.LOOP:
pushax pushbx pushcx call Convert_LBA_to_CHS ; convert starting sector from LBA to CHS
movah, 2 ; INT 0x13, AH=2 --> read in CHS mode
moval, 1 ; read one sector
movch, BYTE [Cylinder] ; track/cylinder
movcl, BYTE [Sector] ; sector
movdh, BYTE [Head] ; head
movdl, BYTE [DriveNum] ; drive
int 0x13
jnc .SUCCESS ; check read error
xorax, ax; INT 0x13, AH=0 --> reset floppy/hard disk
int 0x13
decdi; decrement error counter
popcx popbx popax jnz .LOOP ; read again
int 0x18
.SUCCESS:
movsi, msgProgress
call print_string
popcx popbx popax addbx, WORD [BytesPerSec] ; queue next buffer
incax; queue next sector
loop .NEXTSECTOR ; read next sector
ret
;******************************************************************************
; Print String
; DS:SI null-terminated string
;******************************************************************************
print_string:
movah, 0x0E ; BIOS function 0x0E: teletype
.loop:
lodsb; grab a byte from SI
testal, al; NUL?
jz .done ; if the result is zero: get out
int 0x10 ; else: print out the character
jmp .loop
.done:
ret
;******************************************************************************
; Parameters
;******************************************************************************
Sector db 0
Head db 0
Cylinder db 0
bootdevice db 0
datasector dw 0
cluster dw 0
ImageName db "BOOT2 SYS'
msgCRLF db 0x0D, 0x0A, 0
msgProgress db "*', 0
msgLoading db "Loading Second Stage Bootloader', 0x0D, 0x0A, 0
msgFailure db 0x0D, 0x0A, "BOOT2.SYS MISSING', 0x0D, 0x0A, 0
TIMES 510-($-$$) hlt; fill bytes until boot signature
db 0x55 ; boot signature
db 0xAA ; boot signature
;##############################################################################
; "FAT 12" file system is build up by four areas at Floppy Disk:
; - Boot Sector (also called BIOS Parameter Block, BPB)
; - File Allocation Table, FAT
; - Root Directory
; - Subdirectories and Files
; boot sector info block
; begins 3 bytes from start. A far jump needs 3 bytes.
OperatingSystemName db "PrettyOS' ; 8 byte
BytesPerSec dw 512 ; used in bootloader code
SecPerClus db 1 ; used in bootloader code
ReservedSec dw 1 ; used in bootloader code
NumFATs db 2 ; used in bootloader code
RootEntries dw 224 ; used in bootloader code
TotSec dw 2880
MediaType db 0xF0
FATSize dw 9 ; used in bootloader code
SecPerTrack dw 18 ; used in bootloader code
NumHeads dw 2 ; used in bootloader code
HiddenSec dd 0
TotSec32 dd 0
DriveNum db 0 ; used in bootloader code
Reserved db 0
BootSig db 0x29
VolumeSerialNum dd 0xD00FC0DE
VolumeLabel db "PrettyOS ' ; 11 byte
FileSys db "FAT12 ' ; 8 byte
;##############################################################################
;******************************************************************************
; bootloader entry point
;******************************************************************************
entry_point:
xorax, ax; set registers
movds, ax moves, ax movfs, ax movgs, ax
movax, 0x9000 ; set the stack
movss, ax xorsp, sp
mov [bootdevice], dl; store boot device
movsi, msgLoading
call print_string
Load_Root_Directory_Table:
; compute size of root directory and store in "cx"
xorcx, cx xordx, dx movax, 0x20 ; 32 byte directory entry
mul WORD [RootEntries] ; total size of directory
div WORD [BytesPerSec] ; sectors used by directory
xchgax, cx
; compute location of root directory and store in "ax"
moval, BYTE [NumFATs] ; number of FATs
mul WORD [FATSize] ; sectors used by FATs
addax, WORD [ReservedSec] ; adjust for bootsector
mov WORD [datasector], ax; base of root directory
add WORD [datasector], cx
; read root directory into memory (7E00h)
movbx, 0x7E00 ; copy root dir above bootcode
call ReadSectors
;******************************************************************************
; Find stage2 bootloader
;******************************************************************************
; browse root directory for binary image
movcx, WORD [RootEntries] ; load loop counter
movdi, 0x7E00 ; locate first root entry
.LOOP:
pushcx movcx, 0xB ; name has 11 characters
movsi, ImageName ; look for this image name
pushdi repcmpsb; test for entry match
popdi je Load_FAT
popcx adddi, 0x20 ; queue next directory entry
loop .LOOP
jmp FAILURE
;******************************************************************************
; Load File Allocation Table (FAT)
;******************************************************************************
Load_FAT:
; save starting cluster of boot image
movdx, WORD [di + 0x001A]
mov WORD [cluster], dx; file's first cluster
; compute size of FAT and store in "cx"
xorax, ax moval, BYTE [NumFATs] ; number of FATs
mul WORD [FATSize] ; sectors used by FATs
movcx, ax
; compute location of FAT and store in "ax"
movax, WORD [ReservedSec] ; adjust for bootsector
; read image file into memory (0500h)
xorax, ax moves, ax; destination for image
movbx, 0x0500 ; destination for image
pushbx
;******************************************************************************
; Load stage2 bootloader
;******************************************************************************
Load_Image:
movax, WORD [cluster] ; cluster to read
popbx; buffer to read into
call Convert_Cluster_to_LBA ; convert cluster to LBA
xorcx, cx movcl, BYTE [SecPerClus] ; sectors to read
call ReadSectors
pushbx
; compute next cluster
movax, WORD [cluster] ; identify current cluster
movcx, ax; copy current cluster
movdx, ax; copy current cluster
shrdx, 1 ; divide by two
addcx, dx; sum for (3/2)
movbx, 0x7E00 ; location of FAT in memory
addbx, cx; index into FAT
movdx, WORD [bx] ; read two bytes from FAT
testax, 1
jnz .ODD_CLUSTER
.EVEN_CLUSTER:
anddx, 0000111111111111b ; take low twelve bits
jmp .DONE
.ODD_CLUSTER:
shrdx, 4 ; take high twelve bits
.DONE:
mov WORD [cluster], dx; store new cluster
cmpdx, 0x0FF0 ; test for end of file
jb Load_Image
DONE:
movsi, msgCRLF
call print_string
movdl, [bootdevice]
push WORD 0x0000
push WORD 0x0500
retf
FAILURE:
movsi, msgFailure
call print_string
movah, 0x00
int 0x16 ; wait for keypress
int 0x19 ; warm boot reset
;******************************************************************************
; Convert CHS to LBA
; LBA = (cluster - 2) * sectors per cluster
;******************************************************************************
Convert_Cluster_to_LBA:
subax, 2 ; zero base cluster number
xorcx, cx movcl, BYTE [SecPerClus] ; convert byte to word
mulcx addax, WORD [datasector] ; base data sector
ret
;******************************************************************************
; Convert LBA to CHS
; AX LBA Address to convert
;
; absolute sector = (logical sector / sectors per track) + 1
; absolute head = (logical sector / sectors per track) MOD number of heads
; absolute track = logical sector / (sectors per track * number of heads)
;
;******************************************************************************
Convert_LBA_to_CHS :
xordx, dx; prepare dx:ax for operation
div WORD [SecPerTrack] ; calculate
incdl; adjust for sector 0
mov BYTE [Sector], dl xordx, dx; prepare dx:ax for operation
div WORD [NumHeads] ; calculate
mov BYTE [Head], dl mov BYTE [Cylinder], al ret
;******************************************************************************
; Reads sectors
; CX Number of sectors to read
; AX Starting sector
; ES:BX Buffer to read to
;******************************************************************************
ReadSectors:
.NEXTSECTOR:
movdi, 5 ; five retries for error
.LOOP:
pushax pushbx pushcx call Convert_LBA_to_CHS ; convert starting sector from LBA to CHS
movah, 2 ; INT 0x13, AH=2 --> read in CHS mode
moval, 1 ; read one sector
movch, BYTE [Cylinder] ; track/cylinder
movcl, BYTE [Sector] ; sector
movdh, BYTE [Head] ; head
movdl, BYTE [DriveNum] ; drive
int 0x13
jnc .SUCCESS ; check read error
xorax, ax; INT 0x13, AH=0 --> reset floppy/hard disk
int 0x13
decdi; decrement error counter
popcx popbx popax jnz .LOOP ; read again
int 0x18
.SUCCESS:
movsi, msgProgress
call print_string
popcx popbx popax addbx, WORD [BytesPerSec] ; queue next buffer
incax; queue next sector
loop .NEXTSECTOR ; read next sector
ret
;******************************************************************************
; Print String
; DS:SI null-terminated string
;******************************************************************************
print_string:
movah, 0x0E ; BIOS function 0x0E: teletype
.loop:
lodsb; grab a byte from SI
testal, al; NUL?
jz .done ; if the result is zero: get out
int 0x10 ; else: print out the character
jmp .loop
.done:
ret
;******************************************************************************
; Parameters
;******************************************************************************
Sector db 0
Head db 0
Cylinder db 0
bootdevice db 0
datasector dw 0
cluster dw 0
ImageName db "BOOT2 SYS'
msgCRLF db 0x0D, 0x0A, 0
msgProgress db "*', 0
msgLoading db "Loading Second Stage Bootloader', 0x0D, 0x0A, 0
msgFailure db 0x0D, 0x0A, "BOOT2.SYS MISSING', 0x0D, 0x0A, 0
TIMES 510-($-$$) hlt; fill bytes until boot signature
db 0x55 ; boot signature
db 0xAA ; boot signature
;##############################################################################
; "FAT 12" file system is build up by four areas at Floppy Disk:
; - Boot Sector (also called BIOS Parameter Block, BPB)
; - File Allocation Table, FAT
; - Root Directory
; - Subdirectories and Files
; boot sector info block
; begins 3 bytes from start. A far jump needs 3 bytes.
OperatingSystemName db "PrettyOS' ; 8 byte
BytesPerSec dw 512 ; used in bootloader code
SecPerClus db 1 ; used in bootloader code
ReservedSec dw 1 ; used in bootloader code
NumFATs db 2 ; used in bootloader code
RootEntries dw 224 ; used in bootloader code
TotSec dw 2880
MediaType db 0xF0
FATSize dw 9 ; used in bootloader code
SecPerTrack dw 18 ; used in bootloader code
NumHeads dw 2 ; used in bootloader code
HiddenSec dd 0
TotSec32 dd 0
DriveNum db 0 ; used in bootloader code
Reserved db 0
BootSig db 0x29
VolumeSerialNum dd 0xD00FC0DE
VolumeLabel db "PrettyOS ' ; 11 byte
FileSys db "FAT12 ' ; 8 byte
;##############################################################################
;******************************************************************************
; bootloader entry point
;******************************************************************************
entry_point:
xorax, ax; set registers
movds, ax moves, ax movfs, ax movgs, ax
movax, 0x9000 ; set the stack
movss, ax xorsp, sp
mov [bootdevice], dl; store boot device
movsi, msgLoading
call print_string
Load_Root_Directory_Table:
; compute size of root directory and store in "cx"
xorcx, cx xordx, dx movax, 0x20 ; 32 byte directory entry
mul WORD [RootEntries] ; total size of directory
div WORD [BytesPerSec] ; sectors used by directory
xchgax, cx
; compute location of root directory and store in "ax"
moval, BYTE [NumFATs] ; number of FATs
mul WORD [FATSize] ; sectors used by FATs
addax, WORD [ReservedSec] ; adjust for bootsector
mov WORD [datasector], ax; base of root directory
add WORD [datasector], cx
; read root directory into memory (7E00h)
movbx, 0x7E00 ; copy root dir above bootcode
call ReadSectors
;******************************************************************************
; Find stage2 bootloader
;******************************************************************************
; browse root directory for binary image
movcx, WORD [RootEntries] ; load loop counter
movdi, 0x7E00 ; locate first root entry
.LOOP:
pushcx movcx, 0xB ; name has 11 characters
movsi, ImageName ; look for this image name
pushdi repcmpsb; test for entry match
popdi je Load_FAT
popcx adddi, 0x20 ; queue next directory entry
loop .LOOP
jmp FAILURE
;******************************************************************************
; Load File Allocation Table (FAT)
;******************************************************************************
Load_FAT:
; save starting cluster of boot image
movdx, WORD [di + 0x001A]
mov WORD [cluster], dx; file's first cluster
; compute size of FAT and store in "cx"
xorax, ax moval, BYTE [NumFATs] ; number of FATs
mul WORD [FATSize] ; sectors used by FATs
movcx, ax
; compute location of FAT and store in "ax"
movax, WORD [ReservedSec] ; adjust for bootsector
; read image file into memory (0500h)
xorax, ax moves, ax; destination for image
movbx, 0x0500 ; destination for image
pushbx
;******************************************************************************
; Load stage2 bootloader
;******************************************************************************
Load_Image:
movax, WORD [cluster] ; cluster to read
popbx; buffer to read into
call Convert_Cluster_to_LBA ; convert cluster to LBA
xorcx, cx movcl, BYTE [SecPerClus] ; sectors to read
call ReadSectors
pushbx
; compute next cluster
movax, WORD [cluster] ; identify current cluster
movcx, ax; copy current cluster
movdx, ax; copy current cluster
shrdx, 1 ; divide by two
addcx, dx; sum for (3/2)
movbx, 0x7E00 ; location of FAT in memory
addbx, cx; index into FAT
movdx, WORD [bx] ; read two bytes from FAT
testax, 1
jnz .ODD_CLUSTER
.EVEN_CLUSTER:
anddx, 0000111111111111b ; take low twelve bits
jmp .DONE
.ODD_CLUSTER:
shrdx, 4 ; take high twelve bits
.DONE:
mov WORD [cluster], dx; store new cluster
cmpdx, 0x0FF0 ; test for end of file
jb Load_Image
DONE:
movsi, msgCRLF
call print_string
movdl, [bootdevice]
push WORD 0x0000
push WORD 0x0500
retf
FAILURE:
movsi, msgFailure
call print_string
movah, 0x00
int 0x16 ; wait for keypress
int 0x19 ; warm boot reset
;******************************************************************************
; Convert CHS to LBA
; LBA = (cluster - 2) * sectors per cluster
;******************************************************************************
Convert_Cluster_to_LBA:
subax, 2 ; zero base cluster number
xorcx, cx movcl, BYTE [SecPerClus] ; convert byte to word
mulcx addax, WORD [datasector] ; base data sector
ret
;******************************************************************************
; Convert LBA to CHS
; AX LBA Address to convert
;
; absolute sector = (logical sector / sectors per track) + 1
; absolute head = (logical sector / sectors per track) MOD number of heads
; absolute track = logical sector / (sectors per track * number of heads)
;
;******************************************************************************
Convert_LBA_to_CHS :
xordx, dx; prepare dx:ax for operation
div WORD [SecPerTrack] ; calculate
incdl; adjust for sector 0
mov BYTE [Sector], dl xordx, dx; prepare dx:ax for operation
div WORD [NumHeads] ; calculate
mov BYTE [Head], dl mov BYTE [Cylinder], al ret
;******************************************************************************
; Reads sectors
; CX Number of sectors to read
; AX Starting sector
; ES:BX Buffer to read to
;******************************************************************************
ReadSectors:
.NEXTSECTOR:
movdi, 5 ; five retries for error
.LOOP:
pushax pushbx pushcx call Convert_LBA_to_CHS ; convert starting sector from LBA to CHS
movah, 2 ; INT 0x13, AH=2 --> read in CHS mode
moval, 1 ; read one sector
movch, BYTE [Cylinder] ; track/cylinder
movcl, BYTE [Sector] ; sector
movdh, BYTE [Head] ; head
movdl, BYTE [DriveNum] ; drive
int 0x13
jnc .SUCCESS ; check read error
xorax, ax; INT 0x13, AH=0 --> reset floppy/hard disk
int 0x13
decdi; decrement error counter
popcx popbx popax jnz .LOOP ; read again
int 0x18
.SUCCESS:
movsi, msgProgress
call print_string
popcx popbx popax addbx, WORD [BytesPerSec] ; queue next buffer
incax; queue next sector
loop .NEXTSECTOR ; read next sector
ret
;******************************************************************************
; Print String
; DS:SI null-terminated string
;******************************************************************************
print_string:
movah, 0x0E ; BIOS function 0x0E: teletype
.loop:
lodsb; grab a byte from SI
testal, al; NUL?
jz .done ; if the result is zero: get out
int 0x10 ; else: print out the character
jmp .loop
.done:
ret
;******************************************************************************
; Parameters
;******************************************************************************
Sector db 0
Head db 0
Cylinder db 0
bootdevice db 0
datasector dw 0
cluster dw 0
ImageName db "BOOT2 SYS'
msgCRLF db 0x0D, 0x0A, 0
msgProgress db "*', 0
msgLoading db "Loading Second Stage Bootloader', 0x0D, 0x0A, 0
msgFailure db 0x0D, 0x0A, "BOOT2.SYS MISSING', 0x0D, 0x0A, 0
TIMES 510-($-$$) hlt; fill bytes until boot signature
db 0x55 ; boot signature
db 0xAA ; boot signature
Any comments?
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 09:03:12 01.08.2009, insgesamt 5-mal bearbeitet
;******************************************************************************
; boot2.asm
; Stage2 Bootloader
;******************************************************************************
[Bits 16]
org 0x500
jmp entry_point ; go to entry point
;*******************************************************
; Includes and Defines
;*******************************************************
%include "gdt.inc' ; GDT definition
%include "A20.inc' ; A20 gate enabling
%include "Fat12.inc' ; FAT12 driver
%include "GetMemoryMap.inc' ; INT 0x15, eax = 0xE820
%define IMAGE_PMODE_BASE 0x40000 ; where the kernel is to be loaded to in protected mode
%define IMAGE_RMODE_BASE 0x3000 ; where the kernel is to be loaded to in real mode
ImageName db "CKERNEL SYS'
ImageSize dw 0
;*******************************************************
; Data Section
;*******************************************************
msgLoading db 0x0D, 0x0A, "Jumping to OS Kernel...', 0
msgFailure db 0x0D, 0x0A, "Missing CKERNEL.SYS', 0x0D, 0x0A, 0x0A, 0
;*******************************************************
; Switch from Real Mode (RM) to Protected Mode (PM)
;*******************************************************
EnterProtectedMode:
movsi, msgLoading
call print_string
; switch off floppy disk motor
movdx,0x3F2
moval,0x0C
outdx,al
; switch to PM
cli; clear interrupts
moveax, cr0; set bit 0 in cr0--enter pmode
oreax, 1
movcr0, eax jmp DWORD CODE_DESC:ProtectedMode ; far jump to fix CS. Remember that the code selector is 0x8!
[Bits 32]
ProtectedMode:
movax, DATA_DESC ; set data segments to data selector (0x10)
movds, ax movss, ax moves, ax movesp, 0x9000
CopyImage:
moveax, DWORD [ImageSize]
movzxebx, WORD [BytesPerSec]
mulebx movebx, 4
divebx cld movesi, IMAGE_RMODE_BASE
movedi, IMAGE_PMODE_BASE
movecx, eax repmovsd; copy image to its protected mode address
;*******************************************************
; calls, e.g. print_string
;*******************************************************
[BITS 16]
print_string:
lodsb; grab a byte from SI
oral, al; logical or AL by itself
jz .done ; if the result is zero, get out
movah, 0x0E
int 0x10 ; otherwise, print out the character!
jmp print_string
.done:
ret
;******************************************************************************
; boot2.asm
; Stage2 Bootloader
;******************************************************************************
[Bits 16]
org 0x500
jmp entry_point ; go to entry point
;*******************************************************
; Includes and Defines
;*******************************************************
%include "gdt.inc' ; GDT definition
%include "A20.inc' ; A20 gate enabling
%include "Fat12.inc' ; FAT12 driver
%include "GetMemoryMap.inc' ; INT 0x15, eax = 0xE820
%define IMAGE_PMODE_BASE 0x40000 ; where the kernel is to be loaded to in protected mode
%define IMAGE_RMODE_BASE 0x3000 ; where the kernel is to be loaded to in real mode
ImageName db "CKERNEL SYS'
ImageSize dw 0
;*******************************************************
; Data Section
;*******************************************************
msgLoading db 0x0D, 0x0A, "Jumping to OS Kernel...', 0
msgFailure db 0x0D, 0x0A, "Missing CKERNEL.SYS', 0x0D, 0x0A, 0x0A, 0
;*******************************************************
; Switch from Real Mode (RM) to Protected Mode (PM)
;*******************************************************
EnterProtectedMode:
movsi, msgLoading
call print_string
; switch off floppy disk motor
movdx,0x3F2
moval,0x0C
outdx,al
; switch to PM
cli; clear interrupts
moveax, cr0; set bit 0 in cr0--enter pmode
oreax, 1
movcr0, eax jmp DWORD CODE_DESC:ProtectedMode ; far jump to fix CS. Remember that the code selector is 0x8!
[Bits 32]
ProtectedMode:
movax, DATA_DESC ; set data segments to data selector (0x10)
movds, ax movss, ax moves, ax movesp, 0x9000
CopyImage:
moveax, DWORD [ImageSize]
movzxebx, WORD [BytesPerSec]
mulebx movebx, 4
divebx cld movesi, IMAGE_RMODE_BASE
movedi, IMAGE_PMODE_BASE
movecx, eax repmovsd; copy image to its protected mode address
;*******************************************************
; calls, e.g. print_string
;*******************************************************
[BITS 16]
print_string:
lodsb; grab a byte from SI
oral, al; logical or AL by itself
jz .done ; if the result is zero, get out
movah, 0x0E
int 0x10 ; otherwise, print out the character!
jmp print_string
.done:
ret
;******************************************************************************
; boot2.asm
; Stage2 Bootloader
;******************************************************************************
[Bits 16]
org 0x500
jmp entry_point ; go to entry point
;*******************************************************
; Includes and Defines
;*******************************************************
%include "gdt.inc' ; GDT definition
%include "A20.inc' ; A20 gate enabling
%include "Fat12.inc' ; FAT12 driver
%include "GetMemoryMap.inc' ; INT 0x15, eax = 0xE820
%define IMAGE_PMODE_BASE 0x40000 ; where the kernel is to be loaded to in protected mode
%define IMAGE_RMODE_BASE 0x3000 ; where the kernel is to be loaded to in real mode
ImageName db "CKERNEL SYS'
ImageSize dw 0
;*******************************************************
; Data Section
;*******************************************************
msgLoading db 0x0D, 0x0A, "Jumping to OS Kernel...', 0
msgFailure db 0x0D, 0x0A, "Missing CKERNEL.SYS', 0x0D, 0x0A, 0x0A, 0
;*******************************************************
; Switch from Real Mode (RM) to Protected Mode (PM)
;*******************************************************
EnterProtectedMode:
movsi, msgLoading
call print_string
; switch off floppy disk motor
movdx,0x3F2
moval,0x0C
outdx,al
; switch to PM
cli; clear interrupts
moveax, cr0; set bit 0 in cr0--enter pmode
oreax, 1
movcr0, eax jmp DWORD CODE_DESC:ProtectedMode ; far jump to fix CS. Remember that the code selector is 0x8!
[Bits 32]
ProtectedMode:
movax, DATA_DESC ; set data segments to data selector (0x10)
movds, ax movss, ax moves, ax movesp, 0x9000
CopyImage:
moveax, DWORD [ImageSize]
movzxebx, WORD [BytesPerSec]
mulebx movebx, 4
divebx cld movesi, IMAGE_RMODE_BASE
movedi, IMAGE_PMODE_BASE
movecx, eax repmovsd; copy image to its protected mode address
;*******************************************************
; calls, e.g. print_string
;*******************************************************
[BITS 16]
print_string:
lodsb; grab a byte from SI
oral, al; logical or AL by itself
jz .done ; if the result is zero, get out
movah, 0x0E
int 0x10 ; otherwise, print out the character!
jmp print_string
.done:
ret
Test-Download: http://www.henkessoft.de/OS_Dev/Downloads/96.zip
EDIT: includes wurden überarbeitet. Gemeinsames BPB für Stage1 und Stage2 ausgegliedert. Floppy-Motor wurde nun auch ausgeschaltet.
Das ist m.E. eine brauchbare Basis für die weitere Entwicklung.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 20:30:08 02.08.2009, insgesamt 3-mal bearbeitet
Ich sagte doch, dass Du etwas Geduld mitbringen musst. Jedes OS wandert im gewissen Sinne durch eine autistische Phase. Bei PrettyOS wurde das durch Verweigern der "Gehhilfe" GRUB noch vertieft. Der Kontakt zur Außenwelt gehört bei vielen Entwürfen im Sinne eines Mikrokernels nicht wirklich zum Systemkern, sondern wird an den User-Space delegiert. Nun wird sich PrettyOS allerdings verstärkt öffnen, sowohl gegenüber dem User als auch dem Computerumfeld.
Damit ist allerdings auch die wirklich spannende Phase vorbei.
Nun verstehe ich Autisten.
Danke für den interessanten Link!
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 11:43:29 02.08.2009, insgesamt 1-mal bearbeitet
Besonders interessant finde ich die Analyse der Datenstrukturen auf Diskette mit dem Hex-Editor: http://www.henkessoft.de/OS_Dev/Bilder/FAT12_HexEditor.PNG
Man erkennt sehr schön den Bootsektor, FAT1, FAT2, die Root-Directory und dahinter die Daten. Eine Analyse der Clusterketten in der FAT zeigt die Linked List (folgt noch). Damit wird das Ganze sehr klar und konkret.
Na ja, ich überlasse die Formatierung noch MS Windows. Der Format-Befehl von MS Windows 98, ME und XP wird eine Diskette immer mit FAT12 und nur alle anderen Laufwerke mit FAT32 bzw. NTFS formatieren. Der Format-Befehl von MS DOS und MS Windows 95A, der nur FAT12 und FAT16 kennt, formatiert eine Diskette mit FAT12 und alle anderen Laufwerke mit FAT16. Daher ist FAT12 das zu erwartende Format, in der unsere Daten mit einem copy BOOT2.SYS A:\BOOT2.SYS bzw. copy CKERNEL.SYS A:\CKERNEL.SYS auf Diskette übertragen und dort abgelegt werden. Mir gefällt das Umgehen mit halben Bytes auch nicht, wäre bei 16 Bit einfacher.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Ich habe gerade mal etwas in dem Tutorial gelesen über das OS und hab mir mal aus spass direkt das Programm Bochs runtergeladen ( hab zwar noch n Disketten Laufwerk aber die Diskette sind nicht mehr so gut ^^ ) funktionierte auch sofort alles mit dem Kernel.bin am Anfang.
Aber jetzt würde mich mal interessieren wie viele Leute an dem Tutorial mit arbeiten, das ist ja wahnsinn wie viel da steht da kann man eine menge lernen.
Ich habe gerade mal etwas in dem Tutorial gelesen über das OS und hab mir mal aus spass direkt das Programm Bochs runtergeladen ( hab zwar noch n Disketten Laufwerk aber die Diskette sind nicht mehr so gut ^^ ) funktionierte auch sofort alles mit dem Kernel.bin am Anfang.
Aber jetzt würde mich mal interessieren wie viele Leute an dem Tutorial mit arbeiten, das ist ja wahnsinn wie viel da steht da kann man eine menge lernen.
naja das sind doch alles basics, die lernst du in jedem informatik erstsemester :-)
Die Theorie wird schon in den ersten beiden Semestern behandelt. Dabei wird nur nicht auf technische Besonderheiten einer bestimmten Architektur eingegangen. Aufbau/Funktionsweise einer CPU, Assembler (nicht unbedingt x86), Speicher-/Ressourcenmanagement, Scheduling, sind alles Themen die dran kommen.
Zuletzt bearbeitet von Tobiking2 am 19:52:40 04.08.2009, insgesamt 2-mal bearbeitet
Aber jetzt würde mich mal interessieren wie viele Leute an dem Tutorial mit arbeiten
Ich arbeite bisher alleine an diesem Tutorial, seit etwa Mitte März diesen Jahres. Schön wäre es, wenn sich so eine Art OS Community bilden würde, habe diesbezüglich aber keine klaren Vorstellungen.
Zitat:
naja das sind doch alles basics, die lernst du in jedem informatik erstsemester :-)
Man hört zumindest davon. Ob man es rein theoretisch versteht, ist allerdings eine andere Sache. Aus den flüchtigen Universitäts-Skripten, die man so im Internet findet, könnte ich allerdings kein OS zimmern. Selbst mit dem Tanenbaum in der Hand ist dies nicht möglich. Man benötigt "echte" Vorbilder. Linux hatte Minix, DOS hatte CP/M als Vorbild, so sieht das aus.
Mich freut es, wenn ich mitbekomme, dass junge Leute mit etwa 13 - 15 Jahren diesbezüglich konkret los legen und nicht nur zu Konsumenten von fertigen OS, Programmen und Spielen werden. Ich denke dies ist die Hauptzielgruppe. Daher schreibe ich das Tutorial auch in Deutsch und nicht in Englisch.
Zitat:
Die Theorie wird schon in den ersten beiden Semestern behandelt. Dabei wird nur nicht auf technische Besonderheiten einer bestimmten Architektur eingegangen.
Die technischen Besonderheiten, gerade bei 80x86, sind aber genau der Clou! Grau, grau ist alle Theorie.
Ich weiß, dass das Thema hier zu genüge durchgekaut wurde. Aber gerade ich als jemand, der in deiner Zielgruppe liegt, frage ich mich doch, ob es denn gerade so sinnvoll ist, ständig vom eigentlichen Kern abzulenken, nur weil der liebe x86er unbedingt auf seine Extrawurst besteht.
Das Anschalten des A20-Gates - beispielsweise - könnte sich, ob der nicht unbedingt offensichtlichen Notwendigkeit dieses Gefrickels, für so manchen zum Stolperstein entwickeln. In dieselbe Nische schlägt da auch das gesamte Umschalten zwischen PM / RM usw. usf.
Trotzdem muss ich dir ein dickes Lob aussprechen.
Dein Tutorial ist eine unglaublich willkommene Abwechslung zu den teilweise sehr schwer auffindbaren und verstreuten Informationen zum Thema im Netz.
Weiter so
Danke für das positive Feedback! Es ist eine abwechslungsreiche und interessante Materie, aber auch verflixt viel Arbeit, die Zusammenhänge verständlich und dennoch auf das Wesentliche konzentriert darzustellen. Für konkrete konstruktive Anregungen bin ich dankbar.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Für konkrete konstruktive Anregungen bin ich dankbar.
In Sachen fachlicher Korrektheit kann dir die "Forenprominenz" ja bestens zur Seite stehen. Was das Hauptproblem der Didaktik angeht, so kann ich eigentlich keine wirklichen Kritikpunkte finden. Allgemein würde ich dir - zumindest was meinen Standpunkt angeht - dazu raten, nicht mit Theorie und Hintergrundinformation zu geizen. Absätze überspringen und im Notfall nachlesen kann man jederzeit, aber nichts ärgert mich mehr, als wenn ein Detail ungeklärt bleibt oder nicht erwähnt wird.
Dabei wird nur nicht auf technische Besonderheiten einer bestimmten Architektur eingegangen.
ach das wär zu schön. schätzungsweise 90% der dozenten, die diese dinge lehren, haben ihr skript, was sie ca. 1983 kompiliert haben, worin sie die studenten mit gelaber über uralten x86 kram wie real mode, protected mode, xms, x86 isa und co nerven :-/ die meisten dozenten sind leider vor 20 jahren in der zeit stehen geblieben, als sie selbst das letzte mal irgendwas programmiert haben :-/
die meisten dozenten sind leider vor 20 jahren in der zeit stehen geblieben
So etwas ist eine Schande für unser Ausbildungssystem!
Für was zahle ich denn Unmengen von Steuern? Diesen Damen und Herren müsst ihr mal richtig Bescheid stoßen.
Die Abwehr gegen den "x86 Kram" finde ich prinzipiell unberechtigt, denn diese CPUs beherrschen heute immer noch den PC-Markt, nun in 64-Bit-Qualität, was die ganze Sache noch mehr verkompliziert. Ich fände eine vergleichende konkrete Darstellung der Pros und Cons sinnvoll mit Blick auf Windows, Linux, Symbian OS und einem Roboter-Betriebssystem.
Mit "abstrakter" Software kann man zunächst wenig bewegen. Zum Schluss müssen immer auch die "Basics" bedient werden, und diese sind zumeist komplex, weil diese wirtschaftlich und nicht didaktisch getrieben sind, eben Bit-Gefrickel.
Mir geht es bei meinem Tutorial darum, dass man alles praktisch durchführen und analysieren kann, was zu einem grundlegenden OS dazu gehört, vom Bootloader angefangen bis hin zum Dateisystem. Dinge wie kompliziertes Scheduling oder Deadlock-Verhindern kann man im Tanenbaum sehr gut nachlesen und bei einem einfacheren System leicht nachrüsten.
Die größten Probleme bereitet in der Regel der Umgang mit Assemblercode oder das Präparieren von Stacks, soweit ich das bisher sehen kann. Treiber- oder User-Space-Programmierung ist eher Fleissarbeit.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 23:56:04 04.08.2009, insgesamt 4-mal bearbeitet
Die technischen Besonderheiten, gerade bei 80x86, sind aber genau der Clou! Grau, grau ist alle Theorie.
Man braucht aber zum verstehen von Paging nicht zu wissen, welches Bit in welchem Register gesetzt werden muss, um es beim x86 zu aktivieren. Es ist natürlich in einem Tutorial wie deinem wichtig zu erwähnen, aber wer selber ein Betriebsystem schreibt und nicht auf ein Tutorial aufbaut, kann sich das auch aus dem Intel Manual oder aus einem der recht guten Wikis zu dem Thema raussuchen. Das sehe ich noch nicht als spannend an. Zu dem Wechsel in den PM dürften sich auch schon genug negativ geäußert haben.
Ich find dann erst wieder interessant, wie die verschiedenen Techniken dann in der Praxis wirklich genutzt werden. Das steht dann aber auch oft im Tanenbaum. Ich denke zudem schon das man mit soliden Assembler/C Kenntnissen, dem Tanenbaum und dem Intel Manual ein Betriebsystem schreiben könnte. Es kann ja nicht jeder irgendwo abgucken, irgendwer muss angefangen haben.
Die Zielgruppe 13-15 find ich etwas niedrig. Den meisten dürfte es selbst mit dem Tutorial an Vorwissen fehlen und es endet mit stumpfen copy and paste.
Zitat:
Daher schreibe ich das Tutorial auch in Deutsch und nicht in Englisch.
Und wieso kommentierst du den Code (vor allem die Codebeispiele) auf Englisch und nicht auf Deutsch?
Zitat:
ach das wär zu schön. schätzungsweise 90% der dozenten, die diese dinge lehren, haben ihr skript, was sie ca. 1983 kompiliert haben, worin sie die studenten mit gelaber über uralten x86 kram wie real mode, protected mode, xms, x86 isa und co nerven :-/ die meisten dozenten sind leider vor 20 jahren in der zeit stehen geblieben, als sie selbst das letzte mal irgendwas programmiert haben :-/
Wir haben teilweise Glück gehabt. Die Hardware Grundlagen kamen von einem Dozenten der am Lehrstuhl Eingebettete Systeme tätig ist. Das ist warscheinlich der Grund dafür gewesen, dass wir jedes mal MIPS als Referenzarchitektur genommen haben wenn etwas konkret behandelt wurde.
Die Betriebsystem Vorlesung war dafür dann das Tanenbaum Buch in Kurzform. Sogar die Folien waren original von Tanenbaum auf Englisch und zwischendurch mal ein Deutscher Satz als Zusatz drunter gesetzt.
Zitat:
So etwas ist eine Schande für unser Ausbildungssystem!
Für was zahle ich denn Unmengen von Steuern? Diesen Damen und Herren müsst ihr mal richtig Bescheid stoßen.
Ich zahl sogar noch 500 Euro pro Semester an Studiengebühren...
Das ist erstmal nicht schlimm. Man will sehen, dass etwas geht, bevor man sich tiefer beschäftigt. RM->PM ist blöd, dass BIOS weg ist. Gibt es da keinen Weg, das doch noch zu nutzen? Einschalten A20 kopiert man einfach, basta. Interessant ist Einsatz von FAT12, hoffentlich bald USB, wäre toll.
Tutorial ist bisher glaube ich bestes, was es gibt in deutsch, oder?
Tobiking2: ja dan hattest du wohl Glück. Ich verstehe auch nicht, warum nicht mehr Dozenten einfach auf die wirklich gute Literatur wie Tanenbaum, Silberschatz und Co. zurückgreifen, die es zu diesem Thema ja zur Genüge gibt. Viele Dozenten haben stattdessen ihr didaktisch absolut miserabel gestaltetes Skript, was zusätzlich halt veraltet ist. Schade, daß die Dozenten so ziemlich in jeder Hinsicht tun und lassen können, was sie wollen. Da sollte es mal mehr Vorgaben geben oder so :o
Die Abwehr gegen den "x86 Kram" finde ich prinzipiell unberechtigt, denn diese CPUs beherrschen heute immer noch den PC-Markt, nun in 64-Bit-Qualität, was die ganze Sache noch mehr verkompliziert. Ich fände eine vergleichende konkrete Darstellung der Pros und Cons sinnvoll mit Blick auf Windows, Linux, Symbian OS und einem Roboter-Betriebssystem.
der 'x86-kram' stellt aber eine äusserst miserabel zusammengeflickte architektur dar (im vergleich z.b. zu ARM-systemen). das design der ersten X86-CPUs war nun mal nicht darauf ausgelegt, immer neue derivate (bis in die heutige zeit) hervorzubringen. dementsprechenden haben wir heute übelsten pfusch in unserern PCs.
Erhard Henkes schrieb:
Mit "abstrakter" Software kann man zunächst wenig bewegen. Zum Schluss müssen immer auch die "Basics" bedient werden, und diese sind zumeist komplex, weil diese wirtschaftlich und nicht didaktisch getrieben sind, eben Bit-Gefrickel.
deshalb sollte man auch besser 'hardware abstraction layer' einsetzen, im einfachsten fall I/O-bitgefummel über makros machen und für komplexere komponenten (z.b. die RTC, RS232, speichermedien usw.) treiber mit 'nem einheitlichen interface schreiben. sowas wie scheduling-algorithmus, exception und interrupt-handling z.b. kann man in portablem C coden, wobei nur sehr wenig plattformabhängiger assembler-code verwendet werden muss (den man hinter funktionen verstecken kann, z.b. switch_context()). wenn ich z.b. so'n tutorial wie deins (bis jetzt) machen würde, dann würde ich es nicht 'betriebssystementwicklung' sondern eher 'programmierung unter nackten x86 systemen' o.ä. nennen.
der 'x86-kram' stellt aber eine äusserst miserabel zusammengeflickte architektur dar (im vergleich z.b. zu ARM-systemen). das design der ersten X86-CPUs war nun mal nicht darauf ausgelegt, immer neue derivate (bis in die heutige zeit) hervorzubringen. dementsprechenden haben wir heute übelsten pfusch in unserern PCs.
Du kennst die ARM-Systeme in 20 Jahren noch nicht, kann genau so enden wie bei x86. Nennt sich Abwärtskompatibilität.
Zitat:
programmierung unter nackten x86 systemen
Schöner Titel.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 17:31:46 05.08.2009, insgesamt 1-mal bearbeitet
Du kennst die ARM-Systeme in 20 Jahren noch nicht, kann genau so enden wie bei x86. Nennt sich Abwärtskompatibilität.
naja, wenn die ARMs nicht mehr mit zusätzlichen befehlen und macrocells erweitert werden können (z.b. die normalen register R0...R15 auf 64 bits aufgebläht werden o.ä. x86-krankheiten), dann ist's wohl auch damit vorbei. *heul*
Ich habe den gut strukturierten FAT12-Floppy-Treiber von www.brokenthorn.com ,Tut20, Demo15 an PrettyOS angeflanscht, um zu sehen, ob unser OS möglichst leicht neue fremde Teile aufnehmen kann. Dies hat erstaunlich gut geklappt: http://www.henkessoft.de/OS_Dev/Downloads/98.zip
In dieser Version 98 wird der (Boot-)Sektor 0 (bei null beginnende LBA-Zählweise) von Floppy Disk testweise mittels DMA gelesen und auf dem Bildschirm ausgegeben.
Du kennst die ARM-Systeme in 20 Jahren noch nicht, kann genau so enden wie bei x86. Nennt sich Abwärtskompatibilität.
naja, wenn die ARMs nicht mehr mit zusätzlichen befehlen und macrocells erweitert werden können (z.b. die normalen register R0...R15 auf 64 bits aufgebläht werden o.ä. x86-krankheiten), dann ist's wohl auch damit vorbei. *heul*
Tja, keine Architektur lebt ewig. Dann lieber einmal komplett from scratch ueberholen.
Richtig schlimm wird es doch erst, wenn tausend neue obskure Register, krumme OpCodes und irgendwelche (Kompatibilitaets)Modi eingefuehrt werden.
Ich bin noch nicht lange dabei beim OSDEV (genau genommen ernsthaft seit dem 13.03.2009, siehe Beginn dieses Threads), mache es auch nur aus grundlegendem Interesse, spielerischer Neugier und basierend auf dem Wunsch, die Dinge möglichst knapp, bildhaft und vor allem praktisch umsetzbar zu erklären. Denn ich denke, dass die Einstiegshürde relativ hoch ist. Da gehören beim x86 das A20-Gate und der Umstieg von RM nach PM dazu. Mir persönlich ist z.B. das Multitasking schwer gefallen, bis ich eine brauchbare Vorgehensweise bei tyndur fand. Auch das Paging bei x86 hat mich oft durcheinander gebracht. Das Wichtigste beim OSDEV ist, dass man "dran" bleibt. Das ist kein Thema für ein Wochenende, sondern ein mehrjähriges Projekt, also nur etwas für Leute, die sich in etwas "hinein bohren" können und wollen und dann auch nicht los lassen, wenn es zäh und holprig wird.
Wenn man aber über diese Einstiegshürde hinweg ist, dann ist es wie beim Tanzen: Man hat Lust dem eigenen Repertoire ab und zu eine neue Figurabfolge hinzu zu fügen.
Hier z.B. das Lesen von Sektoren von Floppy Disk im Kernel:
http://www.henkessoft.de/OS_Dev/OS_Dev3.htm#mozTocId967150
Diesbezüglich ist mir wichtig, dass ich solche externen Module möglichst einfach an PrettyOS anflanschen kann. Bezüglich IRQ-Interface ist unser Aufbau hier einfacher und damit eleganter, so dass man einiges einfach weglassen konnte.
Ich kann nur hoffen, dass ich mit meinem Tutorial im deutschen Sprachraum etwas dazu beitrage, dass Einsteiger in diese absolut empfehlenswerte Materie schnell praktische Erfolgserlebnisse haben. Das würde mich freuen.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 10:50:19 06.08.2009, insgesamt 5-mal bearbeitet
Mal eine ganz andere Diskussion: Das Blöde und vielleicht zur Zeit dennoch Gute an Computern ist, dass man sie nicht individualisieren kann. Was müsste passieren, um PrettyOS ein lernfähiges oder zumindest adaptives Interface zum User zu verpassen? Ich denke mal, das gehört in den Bereich Shell / User-Space. Gibt es da schon Beispiele?
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Als Gegenleistung bekommt man in der Regel nur den Genuss der intellektuellen Herausforderung, denn gegen MS Windows, Linux oder andere etablierte OS anzutreten, hat sicher keine Einzelperson vor. Wozu sollte das auch gut sein?
MS Windows ist ein hoch-professionelles OS mit Millionen von Code-Zeilen und Heerscharen von Fulltime-Planern, -Codern, -Testern. Linux, das ich bewundere, aber ungern selbst verwende, schafft es kaum, mit diesem Giganten aus USA mitzuhalten.
Nachdem nun die Grundlagen auf Basis x86 dargestellt wurden, würde es mich reizen, auf der User-Ebene etwas Neues zu schaffen, das in Richtung KI geht. Alleine kann ich allerdings nur Impulse setzen.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Das sagt leider rein gar nichts über die Code Qualität des Linux Kernels und die nicht gerade vertrauenserweckenden Praktiken des Maintainerteams (Kindergarten!) aus. Ich bin zwar auch ein Fan von Linux, aber ob der Kernel so gut und objektorientiert ist, wie der von Wind00f, da habe ich meine Zweifel.
Zur Zeit freue ich mich aber noch über jeden kleinen Schritt, z.B. dass es mir gelungen ist, die Floppy-Sektor-Read-Funktion mit einigen Änderungen auch zu einer zusätzlichen Sektor-Write-Funktion umzuwandeln (alles via DMA). Nun habe ich endlich einen zweiten Ausgabekanal neben dem Bildschirm, allerdings noch rein binär.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Das sagt leider rein gar nichts über die Code Qualität des Linux Kernels und die nicht gerade vertrauenserweckenden Praktiken des Maintainerteams (Kindergarten!) aus.
[Ironie on:]
klar - der durchschnittliche Ottonormal-User ist ja in der Wahl seines OS auch kompetenter als der Architekt von mio. $ teurer Hardware in der Spitzenforschung.
aber ob der Kernel so gut und objektorientiert ist, wie der von Wind00f, da habe ich meine Zweifel.
Ersteres ist hoffentlich ein (schlechter) Witz, und Letzteres ist absolut nicht abzusehen, es sei denn, du verdienst deinen Lebensunterhalt bei jenem mittelständigschem Unternehmen aus Redwood...
Ach kommt, bitte kein Linux vs. Windows Geflame hier. Das Tutorial mal "Linux-kompatibel" zu machen hätte zwar was für sich, aber wenn der Autor nun einmal Windows einsetzt , dann ist das halt so.
Edit:
Sehr richtig. Deshalb werde ich auch alles Weitere in Richtung dieser "Diskussion" loeschen.
Zuletzt bearbeitet von Nobuo T am 11:24:15 07.08.2009, insgesamt 1-mal bearbeitet
Ich habe momentan das Problem, das zwei Sektoren anstelle von einem Sektor geschrieben wird.
teste die read/write funktionen separat. wird wirklich nur ein sektor (512 bytes) beschrieben und gelesen (benutze ein externes programm, z.b. 'nen disk-editor wie z.b. 'winhex' zur prüfung)?
falls nicht, schau im user manual des DMA-controllers nach. vielleicht transfererierst du 16-bit words anstelle von bytes oder sowas.
DMA -> floppy sollte track-weise (nicht sektor-weise, ist mir inzwischen klar) erfolgen. Das klappt aber leider noch nicht, obwohl alle Einstellungen nun - soweit ich das recherchieren kann - in Ordnung sein sollten.
Klappt aber nicht lückenlos. Es werden weniger als 18 Sektoren geschrieben. Die 17 hatte ich schon gewählt, weil in einem Fall waren es immer eins zu wenig. Seltsame Geschichte.
int j;
for(j=0; j<7; ++j)
flpydsk_read_data(); // read status info
flpydsk_check_int(&st0,&cyl); // let FDC know we handled interrupt
}
// write a sector int flpydsk_write_sector(int sectorLBA)
{
if (_CurrentDrive >= 4) return -1;
// convert LBA sector to CHS int head=0, track=0, sector=1;
flpydsk_lba_to_chs(sectorLBA, &head, &track, §or);
// turn motor on and seek to track
flpydsk_control_motor(TRUE);
if(flpydsk_seek (track, head)) return -2;
// write sector and turn motor off
flpydsk_write_sector_imp(head, track, sector);
flpydsk_control_motor(FALSE);
return 0;
}
int j;
for(j=0; j<7; ++j)
flpydsk_read_data(); // read status info
flpydsk_check_int(&st0,&cyl); // let FDC know we handled interrupt
}
// write a sector int flpydsk_write_sector(int sectorLBA)
{
if (_CurrentDrive >= 4) return -1;
// convert LBA sector to CHS int head=0, track=0, sector=1;
flpydsk_lba_to_chs(sectorLBA, &head, &track, §or);
// turn motor on and seek to track
flpydsk_control_motor(TRUE);
if(flpydsk_seek (track, head)) return -2;
// write sector and turn motor off
flpydsk_write_sector_imp(head, track, sector);
flpydsk_control_motor(FALSE);
return 0;
}
int j;
for(j=0; j<7; ++j)
flpydsk_read_data(); // read status info
flpydsk_check_int(&st0,&cyl); // let FDC know we handled interrupt
}
// write a sector int flpydsk_write_sector(int sectorLBA)
{
if (_CurrentDrive >= 4) return -1;
// convert LBA sector to CHS int head=0, track=0, sector=1;
flpydsk_lba_to_chs(sectorLBA, &head, &track, §or);
// turn motor on and seek to track
flpydsk_control_motor(TRUE);
if(flpydsk_seek (track, head)) return -2;
// write sector and turn motor off
flpydsk_write_sector_imp(head, track, sector);
flpydsk_control_motor(FALSE);
return 0;
}
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 00:52:26 09.08.2009, insgesamt 2-mal bearbeitet
Ja, genau da liegt der Hase im Pfeffer. Diese Zeile ist nicht zielführend. Wurde von mir durch folgendes ersetzt:
C/C++ Code:
flpydsk_send_command( 18 );
C/C++ Code:
flpydsk_send_command( 18 );
C/C++ Code:
flpydsk_send_command( 18 );
Nun werden 18 Sektoren geschrieben (fkt. auch mit anderen Zahlen), allerdings muss man beim Schreiben genau an einem Track-Anfang (gemäß LBA) beginnen: 0, 18, 36, ...
Ansonsten wird nämlich auch noch dort jeweils geschrieben. Merkwürdiger Effekt.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Wenn jemand Lust hat, mich zu unterstützen, würde mich freuen. Es ist jetzt viel Detail-Arbeit angesagt. Ich würde gerne mit engagierten Leuten ohne Zeitdruck eine eigene OS-Community aufbauen:
http://www.c-plusplus.de/forum/viewtopic-var-t-is-247814.html
Ich denke, das Thema OSDEV im leicht fortgeschrittenen Stadium passt nicht mehr richtig in dieses Forum. Die Diskussionen werden unspezifisch. Die anstehenden Aufgaben erfordern Detailwissen über PrettyOS, C, Assembler, Erfahrung mit der Toolchain, konstruktive Kreativität, analytisches Vorgehen, Tiefgang und generell Lust am Thema OS-Development (viel Arbeit, wenig Belohnung).
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 11:10:39 16.08.2009, insgesamt 3-mal bearbeitet
@Erhard: Erstmal ein für die großartigen Tutorials, sehr interessant und informativ.
Nun habe ich aber folgendes Problem:
Ich versuche zur Zeit einige der Beispiele aus deinem Tutorial auf einem Ubuntu-System über Bochs zum Laufen zu bekommen. Allerdings hagelt es bei mir immer wieder "Boot failed: could not read the boot disk" beim Laden meiner selbstgenerierten MyOS.bin-Datei.
Die nasm-Befehle sind ja praktisch die gleichen wie unter Windows, allerdings verwende ich statt dem copy /b-Befehl ein "cat boot.bin kernel.bin>MyOS.bin". Der Pfad zur OS.bin-Datei ist in der Konfigurationsdatei auch korrekt gesetzt. Die Quelldateien entsprechen 1:1 denen des ersten Beispiels im Protected-Mode-Abschnitt.
Benutze ich das bereits vorgefertigte Sourcepaket(inkl. der MyOS.bin), so gibt es in Bochs keinerlei Probleme beim Booten. Könnte es sein, dass hier der cat-Aufruf das Problem verursacht? Denn beim generieren des allerersten Beispiels, das jediglich aus der Datei kernel.bin bestand, gab es auch noch keinerlei Probleme.
Ich benutze auch die ganze Zeit Ubuntu und habe keinerlei Probleme gehabt. Allerdings nehme ich QEmu, da der QEmu-Launcher meiner Meinung nach etwas komfortabler ist wenn man öfters mal etwas an den Einstellungen ändert.
Ich hab das mit cat in zwei Befehlen gemacht "cat boot.bin > MyOS" und "cat ckernel.bin >> MyOS" aber sollte auf das gleiche hinauslaufen.
Kleines Problem: aktueller 1st stage BL startet bei einigen PC nicht...
ein Blick in den Quellcode von grub, z.B. aus boot.S:
Zitat:
/*
* This is a workaround for buggy BIOSes which don't pass boot
* drive correctly. If GRUB is installed into a HDD, check if
* DL is masked correctly. If not, assume that the BIOS passed
* a bogus value and set DL to 0x80, since this is the only
* possible boot drive. If GRUB is installed into a floppy,
* this does nothing (only jump).
*/
oder
Zitat:
/*
* Determine the hard disk geometry from the BIOS!
* We do this first, so that LS-120 IDE floppies work correctly.
*/
lässt erahnen, warum es immer mindestens einen problematischen Rechner geben wird...
Der Start hat geklappt. Wollte mich bei allen bedanken, die mich auf diesem Weg bisher unterstützten. Nun ist m.E. der richtige Zeitpunkt, um noch dazu zu stoßen, egal ob auf Basis Windows oder Linux.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
ich habe ja kein eingebautes Disketten-Laufwerk und habe gerade folgendes ausprobiert:
- ein Floppy Driver USB genommen, angeschlossen - scheint noch zu funktionieren
- damit eine Diskette formatiert - ja, ich habe bei mir noch eine Diskette gefunden:
Zitat:
mkfs.msdos -F 12 -I /dev/sdc
- Dateien boot2.sys und ckernel.sys (diese habe ich ja am Montag kompilieren können) ins Wurzelverzeichnis der Diskette kopiert und so sieht es aus:
Zitat:
drwxrwxrwx 2 alex root 16384 1. Jan 1970 .
drwxr-xr-x 3 root root 4096 7. Okt 19:04 ..
-rwxr-xr-x 1 alex root 912 7. Okt 18:54 boot2.sys
-rwxr-xr-x 1 alex root 34036 7. Okt 18:54 ckernel.sys
- auf einen USB Stick habe ich mit dd die boot.bin kopiert
- System neugestartet und vom USB Stick gestartet, mein BIOS kann es, wie es aussieht
Nun kommt folgende Ausgabe:
Zitat:
Loading Second Stage Bootloader
**************
BOOT2.SYS MISSING
Heisst das, mein BIOS hat vom USB Stick gestartet und hat erkannt, dass es einen Disketten-USB-Adapter mit einer Diskette drin gibt - nur findet die BOOT2.SYS nicht?
;******************************************************************************
; Load stage2 bootloader
;******************************************************************************
Load_Image:
movax, WORD [cluster] ; cluster to read
popbx; buffer to read into
call Convert_Cluster_to_LBA ; convert cluster to LBA
xorcx, cx movcl, BYTE [SecPerClus] ; sectors to read
call ReadSectors
pushbx
; compute next cluster
movax, WORD [cluster] ; identify current cluster
movcx, ax; copy current cluster
movdx, ax; copy current cluster
shrdx, 1 ; divide by two
addcx, dx; sum for (3/2)
movbx, 0x7E00 ; location of FAT in memory
addbx, cx; index into FAT
movdx, WORD [bx] ; read two bytes from FAT
testax, 1
jnz .ODD_CLUSTER
.EVEN_CLUSTER:
anddx, 0000111111111111b ; take low twelve bits
jmp .DONE
.ODD_CLUSTER:
shrdx, 4 ; take high twelve bits
.DONE:
mov WORD [cluster], dx; store new cluster
cmpdx, 0x0FF0 ; test for EOF
jb Load_Image
DONE:
movsi, msgCRLF
call print_string
movdl, [bootdevice]
push WORD 0x0000
push WORD 0x0500
retf
FAILURE:
movsi, msgFailure
call print_string
movah, 0x00
int 0x16 ; wait for keypress
int 0x19 ; warm boot reset
;
;
;
msgFailure db 0x0D, 0x0A, "BOOT2.SYS MISSING', 0x0D, 0x0A, 0
;******************************************************************************
; Load stage2 bootloader
;******************************************************************************
Load_Image:
movax, WORD [cluster] ; cluster to read
popbx; buffer to read into
call Convert_Cluster_to_LBA ; convert cluster to LBA
xorcx, cx movcl, BYTE [SecPerClus] ; sectors to read
call ReadSectors
pushbx
; compute next cluster
movax, WORD [cluster] ; identify current cluster
movcx, ax; copy current cluster
movdx, ax; copy current cluster
shrdx, 1 ; divide by two
addcx, dx; sum for (3/2)
movbx, 0x7E00 ; location of FAT in memory
addbx, cx; index into FAT
movdx, WORD [bx] ; read two bytes from FAT
testax, 1
jnz .ODD_CLUSTER
.EVEN_CLUSTER:
anddx, 0000111111111111b ; take low twelve bits
jmp .DONE
.ODD_CLUSTER:
shrdx, 4 ; take high twelve bits
.DONE:
mov WORD [cluster], dx; store new cluster
cmpdx, 0x0FF0 ; test for EOF
jb Load_Image
DONE:
movsi, msgCRLF
call print_string
movdl, [bootdevice]
push WORD 0x0000
push WORD 0x0500
retf
FAILURE:
movsi, msgFailure
call print_string
movah, 0x00
int 0x16 ; wait for keypress
int 0x19 ; warm boot reset
;
;
;
msgFailure db 0x0D, 0x0A, "BOOT2.SYS MISSING', 0x0D, 0x0A, 0
;******************************************************************************
; Load stage2 bootloader
;******************************************************************************
Load_Image:
movax, WORD [cluster] ; cluster to read
popbx; buffer to read into
call Convert_Cluster_to_LBA ; convert cluster to LBA
xorcx, cx movcl, BYTE [SecPerClus] ; sectors to read
call ReadSectors
pushbx
; compute next cluster
movax, WORD [cluster] ; identify current cluster
movcx, ax; copy current cluster
movdx, ax; copy current cluster
shrdx, 1 ; divide by two
addcx, dx; sum for (3/2)
movbx, 0x7E00 ; location of FAT in memory
addbx, cx; index into FAT
movdx, WORD [bx] ; read two bytes from FAT
testax, 1
jnz .ODD_CLUSTER
.EVEN_CLUSTER:
anddx, 0000111111111111b ; take low twelve bits
jmp .DONE
.ODD_CLUSTER:
shrdx, 4 ; take high twelve bits
.DONE:
mov WORD [cluster], dx; store new cluster
cmpdx, 0x0FF0 ; test for EOF
jb Load_Image
DONE:
movsi, msgCRLF
call print_string
movdl, [bootdevice]
push WORD 0x0000
push WORD 0x0500
retf
FAILURE:
movsi, msgFailure
call print_string
movah, 0x00
int 0x16 ; wait for keypress
int 0x19 ; warm boot reset
;
;
;
msgFailure db 0x0D, 0x0A, "BOOT2.SYS MISSING', 0x0D, 0x0A, 0
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 19:38:50 07.10.2009, insgesamt 1-mal bearbeitet
Loading Second Stage Bootloader
**************
BOOT2.SYS MISSING
Heisst das, mein BIOS hat vom USB Stick gestartet und hat erkannt, dass es einen Disketten-USB-Adapter mit einer Diskette drin gibt - nur findet die BOOT2.SYS nicht?
Laut "boot.asm" (hab leider nur die "104"-Version, könnte also falsch sein meine Vermutung) heißt es eher, daß die "boot.bin" die "BOOT2.SYS" auf dem USB-Stick gesucht hat. Starte mal "vollständig" vom USB-Diskettenlaufwerk.
Vielen Dank! Stell da mal eine Seite rein auf der die Versionen schön sauber aufgelistet sind (zusammen mit den Chat-Protokollen).
abc.w schrieb:
Ausprobiert, geht nicht. Keine Ausgaben, BIOS geht dann weiter und bootet von der Festplatte...
Probier mal folgendes:
Assembler Code:
; boot.asm (version 106)
ReadSectors:
(...)
; mov dl, BYTE [DriveNum] ; ändern in:
movdl, BYTE [bootdevice] ; Da unklar ist welche "Nummer" das Bootdevice hat,
(...) ; wird "bootdevice" schon die richtige Nummer sein.
Assembler Code:
; boot.asm (version 106)
ReadSectors:
(...)
; mov dl, BYTE [DriveNum] ; ändern in:
movdl, BYTE [bootdevice] ; Da unklar ist welche "Nummer" das Bootdevice hat,
(...) ; wird "bootdevice" schon die richtige Nummer sein.
Assembler Code:
; boot.asm (version 106)
ReadSectors:
(...)
; mov dl, BYTE [DriveNum] ; ändern in:
movdl, BYTE [bootdevice] ; Da unklar ist welche "Nummer" das Bootdevice hat,
(...) ; wird "bootdevice" schon die richtige Nummer sein.
Setzt aber voraus, daß das BIOS "CHS-Zugriffe" auf ein USB-Gerät unterstützt (emuliert).
Die Version 106 startet auch "vollständig" von einem USB-Stick. Allerdings unter folgender Voraussetzung:
FORMAT Volume [/FS:Dateisystem] [/V:Bezeichnung] [/Q] [/A:Größe] [/C] [/X]
FORMAT Volume [/V:Bezeichnung] [/Q] [/F:Größe]
FORMAT Volume [/V:Bezeichnung] [/Q] [/T:Spuren /N:Sektoren]
FORMAT Volume [/V:Bezeichnung] [/Q]
FORMAT Volume [/Q]
Volume Gibt den Laufwerkbuchstaben (gefolgt von einem Doppel-
punkt), den Bereitstellungspunkt oder den Volume-
namen an.
/FS:Dateisystem Gibt den Typ des Dateisystems an (FAT, FAT32, oder NTFS).
/V:Bezeichnung Gibt die zuzuweisende Volumebezeichnung an.
/Q Führt eine Formatierung mit Schnellformatierung durch.
/C Nur NTFS: Auf dem neuen Volume erstellte Dateien werden
standardmäßig komprimiert.
/X Erzwingt das Aufheben der Bereitstellung des Volumes,
falls erforderlich. Alle zu dem Volume geöffneten Bezüge
werden ungültig.
/A:Größe Überschreibt die Standardgröße der Zuordnungseinheiten.
Standardeinstellungen werden für den Gebrauch empfohlen.
NTFS: 512, 1024, 2048, 4096, 8192, 16 KB, 32 KB, 64 KB.
FAT: 512, 1024, 2048, 4096, 8192, 16 KB, 32 KB, 64 KB
(128 KB, 256 KB für Sektorengröße > 512 Bytes).
FAT32: 512, 1024, 2048, 4096, 8192, 16 KB, 32 KB, 64 KB
(128 KB, 256 KB für Sektorengröße > 512 Bytes).
Bedenken Sie, dass für die Dateisysteme FAT und FAT32
folgende Einschränkungen bezüglich der Anzahl der Cluster
auf einem Volume gelten:
FAT: Anzahl der Cluster <= 65526
FAT32: 65526 < Anzahl der Cluster < 4177918
FORMAT wird sofort abgebrochen, wenn die oben genannten
Bedingungen mit der angegebenen Clustergröße nicht
eingehalten werden können.
NTFS-Komprimierung wird nicht für Zuordnungseinheitsgrößen
über 4096 unterstützt.
/F:Größe Gibt die Größe der zu formatierenden Diskette an (1.44).
/T:Spuren Formatiert jede Seite mit der angegebenen Anzahl an Spuren.
/N:Sektoren Formatiert jede Spur mit der angegebenen Anzahl an Sektoren.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 19:37:22 09.10.2009, insgesamt 1-mal bearbeitet
... befinden sich in unserem Forum (bitte Badestrand anmailen)
Hier ist noch die Version 105 (106 war eine experimentelle Variante wegen des Floppy Treibers, um zu sehen, was bei echten PCs geht und was nicht ): http://www.henkessoft.de/OS_Dev/Downloads/105.zip
So sieht das bei mir mit Bochs (gebootet von echter Floppy) aus:
$> hi <--
I am PrettyOS. Always at your service!
$>
Die obere Auflistung betrifft die Root Directory der Floppy Disk, während nachstehend die RAM Disk Struktur (vom User-Bereich mit incbin in den Kernel eingeschleuste Programme, z.B. shell) dargestellt wird. Details findet man hier: http://www.henkessoft.de/OS_Dev/OS_Dev3.htm#mozTocId610721
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 13:42:51 10.10.2009, insgesamt 7-mal bearbeitet
Die obere Auflistung betrifft die Root Directory der Floppy Disk, ...
Und genau das ist ein großes Problem. Die boot.asm ist teilweise "hardcoded" und deshalb nur für eine Diskette geeignet. Wenn ein USB-Stick formatiert ist, hat er einen MBR und eine Partition. "Angesprochen" wird er vom BIOS (falls das BIOS das unterstützt) via CHS (wobei gilt: CHS = xxx 255 63 (xxx = je nach Größe)).
Normalerweise gehört die "boot.bin" in den ersten logischen Sektor der Partition (sprich ganz an den Anfang der Partition). Aber dann stimmt die gesamte "CHS-Zugriffslogik" nicht mehr. Prinzipell aber kein Problem das anzupassen.
Allerdings das unangenehmste an der boot.bin ist das Dateisystem:
Wenn man auf der Diskette eine Datei löscht und wieder raufkopiert, dann stimmt die Struktur des Root-Directory nicht mehr. Dummerweise werden gelöschte Dateien nicht gelöscht, sondern nur als gelöscht markiert. Was heißt, daß dann das Root-Directory einen Eintrag zuviel hat und geparst werden muß.
Entweder programmiert jemand einen 510 Byte großen FAT12-Parser (und für den USB-Stick am besten gleich einen FAT32-Parser) oder beim Bootloader muß auf die Vorzüge eines FS verzichtet werden.
Spätestens bei Bootmedien mit Partitionen wird aus Stage1 wohl sämtlicher Filesystem Code verschwinden müssen. Die Partitionstabelle liegt nämlich auch im MBR. Es sind also letztendlich nur noch 440 Byte nutzbar für Stage1. Die jetzige Stage1 ist mit 480 Byte also schon zu groß. Die Stage1 von Grub ist auch schon 400 Byte groß ohne jeglichen Filesystem Code.
Allerdings nutzt Grub einen anderen Trick aus. Der MBR besteht zwar nur aus den ersten Sektor des Bootmediums, aber der Rest des ersten Kopfes ist unbenutzt. Grub legt Stage2 also in diesen ungenutzen Platz auf dem ersten Kopf. Die Installationsroutine von Grub überprüft allerdings die Größe und kann die Stelle von Stage2 nachträglich in der compilierten Stage1 patchen.
Wenn Stage 1 die CHS der eigenen Partition selbst ermittelt (z.B. durch Auslesen der Partitionstabelle des MBR), dann müssen "Seltsamkeiten" wie bei GRUB erst nicht in Erwägung gezogen werden.
In der eigenen Partition ist Platz genug für "seltsames" (jedenfalls solange wie PrettyOS noch kein FS hat).
Dort sitzt aber normal schon ein Dateisystem, das seine eigenen Vorstellungen davon hat, wie der Platz genutzt wird. Ich denke, man wird eher nicht drum herumkommen, in stage1 ein paar Sektornummern von stage2 reinzupatchen.
Ich hab grad mal meine Grub Installationen geprüft. Auf der Diskette (ext2) und der Festplatte (ext3) liegt Stage2 im Sektor 1, also direkt hinter dem MBR.
Was würde man denn machen wenn das Dateisystem dann doch mal in Sektor 1 beginnt? Stage2 in die Zuordnungstabelle eintragen und hoffen das das Dateisystem nicht auf die Idee kommt die Datei zu verschieben (Stichwort defragmentieren bei ntfs)?
Du müsstest mal schauen, was GRUB auf einer Diskette macht, dort hat man diesen Platz ja nicht, wenn ich mich grad nicht täusche. Aber stimmt, mit Dateisystem und Defragmentierung hast du wohl verloren.
Du müsstest mal schauen, was GRUB auf einer Diskette macht, dort hat man diesen Platz ja nicht, wenn ich mich grad nicht täusche. Aber stimmt, mit Dateisystem und Defragmentierung hast du wohl verloren.
Stimmt, bei der Diskette ist es doch nicht Sektor 1. Ich habe mir mit Hexdump nur 1 byte anzeigen lassen...
Also bei der Diskette liegt Stage2 mitten auf der Diskette und ist im Dateisystem eingetragen. Also bräuchte man um sicher zu gehen wirklich einen Installer der mit dem Dateisystem auf dem Bootmedium klarkommen muss.
; boot.asm (version 106)
ReadSectors:
(...)
; mov dl, BYTE [DriveNum] ; ändern in:
movdl, BYTE [bootdevice] ; Da unklar ist welche "Nummer" das Bootdevice hat,
(...) ; wird "bootdevice" schon die richtige Nummer sein.
Assembler Code:
; boot.asm (version 106)
ReadSectors:
(...)
; mov dl, BYTE [DriveNum] ; ändern in:
movdl, BYTE [bootdevice] ; Da unklar ist welche "Nummer" das Bootdevice hat,
(...) ; wird "bootdevice" schon die richtige Nummer sein.
Assembler Code:
; boot.asm (version 106)
ReadSectors:
(...)
; mov dl, BYTE [DriveNum] ; ändern in:
movdl, BYTE [bootdevice] ; Da unklar ist welche "Nummer" das Bootdevice hat,
(...) ; wird "bootdevice" schon die richtige Nummer sein.
Setzt aber voraus, daß das BIOS "CHS-Zugriffe" auf ein USB-Gerät unterstützt (emuliert).
Die Version 106 startet auch "vollständig" von einem USB-Stick.
Hab's noch nicht ausprobiert, werde es noch nachholen.
Ich beobachte jetzt folgendes Verhalten: Mein BIOS scheint keine Probleme zu haben vom USB Stick zu starten. Ich habe auf dem Stick meinen Dummy-Bootloader kopiert, der mit int 13 einen Sektor lesen soll. Der Dummy-Bootloader scheint vom USB-Diskettenlaufwerk zu lesen (erkennbar an den Geräuschen vom USB-Diskettenlaufwerk ).
Hier ist mein Dummy-Bootloader:
;******************************************************************************
; Print String
; DS:SI null-terminated string
;******************************************************************************
print_string:
.loop:
movah, 0x0E ; BIOS function 0x0E: teletype
lodsb; grab a byte from SI
testal, al; NUL?
jz .done ; if the result is zero: get out
int 0x10 ; else: print out the character
jmp .loop
.done:
ret
szStartMessage db "Started from USB stick', 0x0D, 0x0A, 0
szDriveStatusError db "Drive status error', 0x0D, 0x0A, 0
szReadSectorDone db "Reading sector done', 0x0D, 0x0A, 0
TIMES 510-($-$$) hlt; fill bytes until boot signature
db 0x55 ; boot signature
db 0xAA ; boot signature
;******************************************************************************
; Print String
; DS:SI null-terminated string
;******************************************************************************
print_string:
.loop:
movah, 0x0E ; BIOS function 0x0E: teletype
lodsb; grab a byte from SI
testal, al; NUL?
jz .done ; if the result is zero: get out
int 0x10 ; else: print out the character
jmp .loop
.done:
ret
szStartMessage db "Started from USB stick', 0x0D, 0x0A, 0
szDriveStatusError db "Drive status error', 0x0D, 0x0A, 0
szReadSectorDone db "Reading sector done', 0x0D, 0x0A, 0
TIMES 510-($-$$) hlt; fill bytes until boot signature
db 0x55 ; boot signature
db 0xAA ; boot signature
;******************************************************************************
; Print String
; DS:SI null-terminated string
;******************************************************************************
print_string:
.loop:
movah, 0x0E ; BIOS function 0x0E: teletype
lodsb; grab a byte from SI
testal, al; NUL?
jz .done ; if the result is zero: get out
int 0x10 ; else: print out the character
jmp .loop
.done:
ret
szStartMessage db "Started from USB stick', 0x0D, 0x0A, 0
szDriveStatusError db "Drive status error', 0x0D, 0x0A, 0
szReadSectorDone db "Reading sector done', 0x0D, 0x0A, 0
TIMES 510-($-$$) hlt; fill bytes until boot signature
db 0x55 ; boot signature
db 0xAA ; boot signature
Hab noch ein wenig rumexperimentiert - mein BIOS weigert sich, vom USB-Diskettenlaufwerk zu starten. Geht einfach weiter und bootet von der Festplatte.
Nach dem Booten vom USB Stick ist bei mir übrigens DX = 0x0080. D.h. USB Stick = "1st hard disk"
mein BIOS weigert sich, vom USB-Diskettenlaufwerk zu starten
Hast Du nicht schon den BL Stage 1 davon gestartet, oder war das vom USB-Stick?
Er konnte doch nur den BL Stage 2 nicht nachladen, oder verwechsle ich da etwas?
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 01:22:23 11.10.2009, insgesamt 1-mal bearbeitet
mein BIOS weigert sich, vom USB-Diskettenlaufwerk zu starten
Hast Du nicht schon den BL Stage 1 davon gestartet, oder war das vom USB-Stick?
Er konnte doch nur den BL Stage 2 nicht nachladen, oder verwechsle ich da etwas?
Stage 1 Bootloader habe ich bis jetzt immer nur vom USB-Stick starten können. Und dieser Stage 1 Bootloader kann irgendwie auf den USB-Diskettenlaufwerk zugreifen (der gleichzeitig am anderen USB Port angeschlossen ist), findet aber den Stage 2 Bootloader nicht.
Das habe ich auch alles mit meinem Dummy-Bootloader nachvollzogen. Und wie ich vorhin gepostet habe, USB-Sticks werden von meinem BIOS als "1st hard disk" abgebildet (int 13h mit DL = 0x80) und USB-Diskettenlaufwerke werden als "1st floppy disk" abgebildet (int 13h mit DL = 0x00). Und Booten vom USB-Diskettenlaufwerk geht interessanterweise nicht.
Du hast nicht zufällig einen GRUB auf der Platte? Ansonsten könntest du es mal mit "chainloader (fd0)+1" versuchen (also GRUB von der Platte booten und den wiederum den Bootloader von der Floppy laden lassen).
Stage 1 Bootloader habe ich bis jetzt immer nur vom USB-Stick starten können. Und dieser Stage 1 Bootloader kann irgendwie auf den USB-Diskettenlaufwerk zugreifen (der gleichzeitig am anderen USB Port angeschlossen ist), findet aber den Stage 2 Bootloader nicht. Das habe ich auch alles mit meinem Dummy-Bootloader nachvollzogen. Und wie ich vorhin gepostet habe, USB-Sticks werden von meinem BIOS als "1st hard disk" abgebildet (int 13h mit DL = 0x80) und USB-Diskettenlaufwerke werden als "1st floppy disk" abgebildet (int 13h mit DL = 0x00). Und Booten vom USB-Diskettenlaufwerk geht interessanterweise nicht.
Danke für die Zusammenfassung.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Das habe ich auch alles mit meinem Dummy-Bootloader nachvollzogen.
Der Stack sollte noch initialisiert werden.
abc.w schrieb:
Und Booten vom USB-Diskettenlaufwerk geht interessanterweise nicht.
Lade das Floppy-Image mal irgendwo hoch. Ich will es mir mal genauer ansehen.
abc.w schrieb:
Und wie ich vorhin gepostet habe, USB-Sticks werden von meinem BIOS als "1st hard disk" abgebildet (int 13h mit DL = 0x80) und USB-Diskettenlaufwerke werden als "1st floppy disk" abgebildet (int 13h mit DL = 0x00).
Das ist auch ok so. Das BIOS ist der Ansicht, daß es sich bei dem USB-Stick um eine Festplatte handelt. So soll das auch sein. Nun kannst du via CHS, INT13 und DL = 0x80 beliebig auf den USB-Stick zugreifen. Aber Vorsicht bei DL=0x81! Das wird wahrscheinlich die "echte" Festplatte sein.
Aber wie schon gesagt, die "boot.bin" ist "hardcoded" und nur für eine FAT12-formatierte Diskette geeignet! (FAT12 nur dehalb, weil man da (halbwegs) sicher sein kann, daß sich Dateien im Root-Verzeichnis an einer ganz bestimmten CHS-Position befinden).
Wenn du ganz vom USB-Stick starten willst, dann sind Anpassungen in der CHS-"Leselogik" erforderlich.
Die allgemeine CHS-Konfiguration für einen USB-Stick ist wie folgt:
CHS = xxx 255 63, wobei xxx je nach Größe des Sticks differiert.
Du hast nicht zufällig einen GRUB auf der Platte? Ansonsten könntest du es mal mit "chainloader (fd0)+1" versuchen (also GRUB von der Platte booten und den wiederum den Bootloader von der Floppy laden lassen).
Ja, ich habe GRUB. Habe probeweise in der menu.lst eingetragen:
Zitat:
title PrettyOS
chainloader (fd0)+1
Nach dem Neustart ausgewählt und, unglaublich, aber wahr, mein Dummy-Bootloader wurde von der Diskette geladen und ausgeführt
Werde es noch mal mit dem Stage1 BL von Erhard ausprobieren.
Ich habe mein Laptop in letzter Zeit zig mal neugestartet
Vielleicht kann Bochs oder andere Emu-Software helfen, die HW zu schonen. Ich habe manchmal auch das Gefühl, dass ich mein Floppy-LW schon geschrottet habe, wenn es das Schreiben verweigert und nur noch rumstottert.
Zitat:
Werde es noch mal mit dem Stage1 BL von Erhard ausprobieren.
Sollte eigentlich gehen.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 15:20:46 11.10.2009, insgesamt 1-mal bearbeitet
-F 12 -v -I /dev/sdc
mkfs.msdos 3.0.2 (28 Feb 2009)
/dev/sdc has 1 head and 3 sectors per track,
logical sector size is 512,
using 0xf8 media descriptor, with 2880 sectors;
file system has 2 12-bit FATs and 4 sectors per cluster.
FAT size is 3 sectors, and provides 710 clusters.
Root directory contains 512 slots.
Volume ID is 8d70e057, no volume label.
mount /dev/sdc /mnt -v
mount: Es wurde kein Dateisystemtyp für /dev/sdc angegeben
Werde den Typ vfat versuchen
/dev/sdc on /mnt type vfat (rw)
cp /home/abc.w/PrettyOS_106/_stage2_bootloader/BOOT2.SYS /mnt -v
»/home/abc.w/PrettyOS_106/_stage2_bootloader/BOOT2.SYS« ->
»/mnt/BOOT2.SYS«
cp /home/abc.w/PrettyOS_106/kernel/ckernel.sys /mnt -v
»/home/abc.w/PrettyOS_106/kernel/ckernel.sys« -> »/mnt/ckernel.sys«
umount /mnt -v
/dev/sdc ausgehängt
dd if=/home/abc.w/PrettyOS_106/_stage1_bootloader/boot.bin of=/dev/sdc bs=512 count=1
1+0 Datensätze ein
1+0 Datensätze aus
512 Bytes (512 B) kopiert, 0,44804 s, 1,1 kB/s
eject /dev/sdc
Mit GRUB (wie vorhin gepostet) vom USB-Diskettenlaufwerk gestartet. Bekomme die Ausgabe:
Zitat:
Loading Second Stage Bootloader
**************
BOOT2.SYS MISSING
mkfs.msdos -F 12 -v -I /dev/sdc
mkfs.msdos 3.0.2 (28 Feb 2009)
/dev/sdc has 1 head and 3 sectors per track,
logical sector size is 512,
using 0xf8 media descriptor, with 2880 sectors;
file system has 2 12-bit FATs and 4 sectors per cluster.
FAT size is 3 sectors, and provides 710 clusters.
Root directory contains 512 slots.
Volume ID is f6235fac, no volume label.
dd if=/home/abc.w/PrettyOS_106/_stage1_bootloader/boot.bin of=/dev/sdc bs=512 count=1
1+0 DatensÀtze ein
1+0 DatensÀtze aus
512 Bytes (512 B) kopiert, 0,029063 s, 17,6 kB/s
mount /dev/sdc /mnt -v
mount: Es wurde kein Dateisystemtyp fÃŒr /dev/sdc angegeben
Werde den Typ vfat versuchen
/dev/sdc on /mnt type vfat (rw)
cp /home/abc.w/PrettyOS_106/_stage2_bootloader/BOOT2.SYS /mnt -v
»/home/abc.w/PrettyOS_106/_stage2_bootloader/BOOT2.SYS« -> »/mnt/BOOT2.SYS«
cp /home/abc.w/PrettyOS_106/kernel/ckernel.sys /mnt -v
»/home/abc.w/PrettyOS_106/kernel/ckernel.sys« -> »/mnt/ckernel.sys«
ls -l /mnt/BOOT2.SYS
-rwxr-xr-x 1 root root 912 11. Okt 15:53 /mnt/BOOT2.SYS
ls -l /mnt/ckernel.sys
-rwxr-xr-x 1 root root 34036 11. Okt 15:53 /mnt/ckernel.sys
umount /mnt -v
/dev/sdc ausgehängt
eject /dev/sdc
mkfs.msdos -F 12 -v -I /dev/sdc
mkfs.msdos 3.0.2 (28 Feb 2009)
/dev/sdc has 1 head and 3 sectors per track,
logical sector size is 512,
using 0xf8 media descriptor, with 2880 sectors;
file system has 2 12-bit FATs and 4 sectors per cluster.
FAT size is 3 sectors, and provides 710 clusters.
Root directory contains 512 slots.
Volume ID is f6235fac, no volume label.
dd if=/home/abc.w/PrettyOS_106/_stage1_bootloader/boot.bin of=/dev/sdc bs=512 count=1
1+0 DatensÀtze ein
1+0 DatensÀtze aus
512 Bytes (512 B) kopiert, 0,029063 s, 17,6 kB/s
mount /dev/sdc /mnt -v
mount: Es wurde kein Dateisystemtyp fÃŒr /dev/sdc angegeben
Werde den Typ vfat versuchen
/dev/sdc on /mnt type vfat (rw)
cp /home/abc.w/PrettyOS_106/_stage2_bootloader/BOOT2.SYS /mnt -v
»/home/abc.w/PrettyOS_106/_stage2_bootloader/BOOT2.SYS« -> »/mnt/BOOT2.SYS«
cp /home/abc.w/PrettyOS_106/kernel/ckernel.sys /mnt -v
»/home/abc.w/PrettyOS_106/kernel/ckernel.sys« -> »/mnt/ckernel.sys«
ls -l /mnt/BOOT2.SYS
-rwxr-xr-x 1 root root 912 11. Okt 15:53 /mnt/BOOT2.SYS
ls -l /mnt/ckernel.sys
-rwxr-xr-x 1 root root 34036 11. Okt 15:53 /mnt/ckernel.sys
umount /mnt -v
/dev/sdc ausgehängt
eject /dev/sdc
mkfs.msdos -F 12 -v -I /dev/sdc
mkfs.msdos 3.0.2 (28 Feb 2009)
/dev/sdc has 1 head and 3 sectors per track,
logical sector size is 512,
using 0xf8 media descriptor, with 2880 sectors;
file system has 2 12-bit FATs and 4 sectors per cluster.
FAT size is 3 sectors, and provides 710 clusters.
Root directory contains 512 slots.
Volume ID is f6235fac, no volume label.
dd if=/home/abc.w/PrettyOS_106/_stage1_bootloader/boot.bin of=/dev/sdc bs=512 count=1
1+0 DatensÀtze ein
1+0 DatensÀtze aus
512 Bytes (512 B) kopiert, 0,029063 s, 17,6 kB/s
mount /dev/sdc /mnt -v
mount: Es wurde kein Dateisystemtyp fÃŒr /dev/sdc angegeben
Werde den Typ vfat versuchen
/dev/sdc on /mnt type vfat (rw)
cp /home/abc.w/PrettyOS_106/_stage2_bootloader/BOOT2.SYS /mnt -v
»/home/abc.w/PrettyOS_106/_stage2_bootloader/BOOT2.SYS« -> »/mnt/BOOT2.SYS«
cp /home/abc.w/PrettyOS_106/kernel/ckernel.sys /mnt -v
»/home/abc.w/PrettyOS_106/kernel/ckernel.sys« -> »/mnt/ckernel.sys«
ls -l /mnt/BOOT2.SYS
-rwxr-xr-x 1 root root 912 11. Okt 15:53 /mnt/BOOT2.SYS
ls -l /mnt/ckernel.sys
-rwxr-xr-x 1 root root 34036 11. Okt 15:53 /mnt/ckernel.sys
umount /mnt -v
/dev/sdc ausgehängt
eject /dev/sdc
Und das ist die Ausgabe von PrettyOS:
Code:
PrettyOS [Version 0.1.0106] (C) 2009 henkessoft.de
Usable RAM: 1047608 KB
RAM Disk at: 4008100Ch
DEBUG ckernel.c: after flpydsk_set_working_drive(0)
Code:
PrettyOS [Version 0.1.0106] (C) 2009 henkessoft.de
Usable RAM: 1047608 KB
RAM Disk at: 4008100Ch
DEBUG ckernel.c: after flpydsk_set_working_drive(0)
Code:
PrettyOS [Version 0.1.0106] (C) 2009 henkessoft.de
Usable RAM: 1047608 KB
RAM Disk at: 4008100Ch
DEBUG ckernel.c: after flpydsk_set_working_drive(0)
Mit dem Hexeditor
http://wintotal.de/softw/index.php?id=2286
konnte ich unter Datei/Datenträger/Datenträger öffnen den USB-Stick öffnen, ebenso
die Datei bootrecord.bin.
Dann einfach per Copy and Paste den Bootrecord Salat in den USB-Stick Salat reinkopiert,
den USB Stick salat gespeichert.
Der USB-Stick ließ sich anschließend in der Tat booten, was mir die folgenden
Zeilen am Monitor nach einem Neustart bestätigten:
HELLO World!
This is no OS. Press any key to reboot.
Das könnte für den einen oder anderen hilfreich sein, der sonst nicht weiß, wie er
seinen Bootrecord am USB-Stick testen kann.
Wenn nicht, einfach diesen Beitrag löschen.
Daher hatte ich die Version 106 geschrieben mit DEBUG-Meldungen:
C/C++ Code:
1 2 3 4 5 6 7 8 9 10
1 2 3 4 5 6 7 8 9 10
// floppy disk with DMA Buffer
flpydsk_set_working_drive(0); // set drive 0 as current drive
printformat("DEBUG ckernel.c: after flpydsk_set_working_drive(0)\n");
sti();
flpydsk_install(6); // floppy disk uses IRQ 6
printformat("DEBUG ckernel.c: after flpydsk_install(6)\n");
k_memset((void*)DMA_BUFFER, 0x0, 0x2400);
printformat("DEBUG ckernel.c: after k_memset\n");
tasking_install(); // ends with sti()
C/C++ Code:
1 2 3 4 5 6 7 8 9 10
// floppy disk with DMA Buffer
flpydsk_set_working_drive(0); // set drive 0 as current drive
printformat("DEBUG ckernel.c: after flpydsk_set_working_drive(0)\n");
sti();
flpydsk_install(6); // floppy disk uses IRQ 6
printformat("DEBUG ckernel.c: after flpydsk_install(6)\n");
k_memset((void*)DMA_BUFFER, 0x0, 0x2400);
printformat("DEBUG ckernel.c: after k_memset\n");
tasking_install(); // ends with sti()
C/C++ Code:
1 2 3 4 5 6 7 8 9 10
// floppy disk with DMA Buffer
flpydsk_set_working_drive(0); // set drive 0 as current drive
printformat("DEBUG ckernel.c: after flpydsk_set_working_drive(0)\n");
sti();
flpydsk_install(6); // floppy disk uses IRQ 6
printformat("DEBUG ckernel.c: after flpydsk_install(6)\n");
k_memset((void*)DMA_BUFFER, 0x0, 0x2400);
printformat("DEBUG ckernel.c: after k_memset\n");
tasking_install(); // ends with sti()
Die Zeile "DEBUG ckernel.c: after flpydsk_set_working_drive(0)" taucht bei euch noch auf, d.h. es klemmt bei:
Dort müsste man nun weiter debuggen, wo es in der Funktion blockt. Es ist sicher ein Unterschied zwischen einer USB-Floppy und einer Floppy. Das heißt das Thema Booten und Floppy-/...-/Treiber hängt stark zusammen.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 20:04:04 11.10.2009, insgesamt 1-mal bearbeitet
@abc.w: Könntest Du das makefile bitte auch auf Windows übertragen?
Ich weiß leider nicht, was wären unter Windows Alternativen für dd, mount, umount und eject...
Linux -> Windows: mkfs.msdos -> format dd -> rawwrite ?
mount -> ?
cp -> copy ls -> dir umount -> ?
eject -> ?
Erhard Henkes schrieb:
Zitat:
mount: Es wurde kein Dateisystemtyp für /dev/sdc angegeben
Werde den Typ vfat versuchen
Kann man da FAT12 eingeben für die Floppy, damit die RootDir gelesen werden kann?
Die Diskette habe ich mit mkfs.msdos FAT12 formatiert (Parameter -F 12). mount benutzt vfat, weil ich es in der Konfiguration des Linux Kernels aktiviert habe und dort unter Hilfe steht:
Zitat:
CONFIG_VFAT_FS:
This option provides support for normal Windows file systems with
long filenames. That includes non-compressed FAT-based file systems
used by Windows 95, Windows 98, Windows NT 4.0, and the Unix
programs from the mtools package.
The VFAT support enlarges your kernel by about 10 KB and it only
works if you said Y to the "DOS FAT fs support" above...
Ich weiß leider nicht, was wären unter Windows Alternativen für dd, mount, umount und eject...
Linux -> Windows:
mkfs.msdos -> format
dd -> rawwrite ?
mount -> ?
cp -> copy
ls -> dir
umount -> ?
eject -> ?
Anstelle rawwrite nehme ich lieber das primitive partcopy, weil ich rawwrite nicht aus dem makefile sauber ansteuern konnte. mount/unmount keine Ahnung. Format macht man vorher.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
// send CHECK_INT/SENSE INTERRUPT command to all drives int i;
for(i=0; i<4; ++i)
flpydsk_check_int(&st0,&cyl);
flpydsk_write_ccr(0); // transfer speed 500kb/s
flpydsk_drive_data(3,16,240,TRUE); // pass mechanical drive info: steprate=3ms, load time=16ms, unload time=240ms
flpydsk_calibrate(_CurrentDrive); // calibrate the disk
}
Ja, da ist bereits ein flpydsk_drive_data(...) drinnen, aber auch ein flpydsk_wait_irq(). Ganz schön verschachtelt der ganze Kram. Könntest Du bitte weiter suchen?
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Hast Du einen konkreten Verbesserungsvorschlag? Mit einem klassisch angebundenen Floppy-LW läuft es (fast überall, einige störrische PCs gibt es leider immer).
Auf jeden Fall erkennt man hier, dass Floppy-Treiber und Bootloader momentan bei PrettyOS von einer gemeinsamen Gruppe bearbeitet werden müssen. Wir sind ja schon mitten drin.
Asche auf mein Haupt! Das hätte ich lieber mal von Anfang an sagen sollen: Stage 1 in seiner jetzigen Form funktioniert nur mit Floppy-Laufwerken. Das er (scheinbar) auch mit USB-Floppys oder USB-Sticks funktioniert, hat einen ganz trivialen Grund: Das BIOS emuliert! Aber spätestens nach Stage 2 ist der Prozessor im PM. Dann ist Feierabend mit dem BIOS und PrettyOS läuft nur noch solange nach Plan weiter, bis es zum ersten Zugriff im PM auf das Laufwerk kommt. Effektiv testen (was heißt über Stage 2 hinaus) kann man PrettyOS z.Zt. nur mit einem physikalisch vorhandenen Floppy-Laufwerk. (Oder indem der Quellcode gelesen wird). Aber technisch gesehen ist es nach wie vor möglich, PrettyOS von einem USB-Floppy oder einem "pen drive" zu starten. Die Frage ist eigentlich nur, ob man im PM auf das "Bootgerät" zugreifen will oder muß (oder wird). Deshalb gibt es in dem Sinne leider auch keine Verbesserungsvorschläge.
@ +gjm+: Du meinst der Floppy-Treiber kann nichts ausrichten, weil er nicht mehr an den Floppy-Controller heran kommt? Ich kann es nicht testen, weil ich kein USB-Floppy-LW besitze.
Auf jeden Fall haben wir einen Fehler im Floppy-Treiber gefunden. Wenn man bei 500K Transfer Rate die 240 ms Head Unload Time haben will, muss man 0xF eingeben und nicht 240. Analog muss man auch noch die Head Load Time Eingabe überprüfen. Da macht das offensichtlich aber nur Faktor 2 aus. http://www.isdaman.com/alsos/hardware/fdc/floppy.htm
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 23:40:36 11.10.2009, insgesamt 2-mal bearbeitet
Auf den FDC zugreifen, bringt offensichtlich nichts, wenn das Laufwerk ein USB-Gerät ist. In dem Fall bräuchtest du USB-Treiber. Das BIOS bietet dir halt den passenden Treiber über int 13h an, deswegen funktioniert das, solange du das BIOS benutzt.
Das wirft nun für die OS-Community Grundsatzfragen für das Design auf, denn die meisten User haben heutzutage kein klassisch angeschlossenes Floppy-LW mehr, lediglich die USB-Variante.
@ taljeth: hast Du einen Link auf ein Beispiel, wo jemand mit USB-Treiber auf eine Floppy zugreift? Auf der anderen Seite wäre dann der USB-Stick die bessere Variante. Also entweder klassische Floppy oder USB-Stick?
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 00:03:05 12.10.2009, insgesamt 2-mal bearbeitet
Mal nicht so schnell jetzt. Im Prinzip darf in der ckernel.c nur nicht "direkt" (was heißt per Name) auf "irgendwas" in der flpydsk.c zugegriffen werden. Das ist eigentlich schon alles was vielleicht geändert werden sollte. Irgendwann kommt eh noch eine pendrv.c hinzu.
Technischer Hinweis: Die USB-Diskettenlaufwerke ... sind bootfähig. Voraussetzung ist jedoch ein erweiterter USB-Support des Bios. Sollte Ihr Computer nicht von dem USB-Floppy starten, drücken Sie während des Startvorgangs die Taste "F2" oder "Entf" um in das Bios-Menü zu gelangen. Suchen Sie den Eintrag "USB Legacy Support" und aktivieren Sie die Funktion ("Enabled"). Zum Abschluss müssen Sie über den Menüpunkt "beenden und speichern" (save and exit) das Bios wieder verlassen, damit die Änderungen gespeichert werden. Einige ältere Computer, besonders Fertigrechner aus diversen Supermärkten, unterstützden diese erweiterte USB-Funktion nicht.
Zitat:
Im Prinzip darf in der ckernel.c nur nicht "direkt" (was heißt per Name) auf "irgendwas" in der flpydsk.c zugegriffen werden.
Richtig, da muss eine Abstraktionsebene dazwischen.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 00:26:17 12.10.2009, insgesamt 2-mal bearbeitet
Ehrlich gesagt habe ich die USB-Floppy eigentlich für einen Hoax gehalten. Wenn PrettyOS USB im PM unterstützt, dann werden gleichzeitig eine ganze Reihe von Geräten unterstützt. Darauf sollte PrettyOS durch "Abstraktion" langsam vorbeitet werden. Aber bevor die Geräteflut hereinbricht.
Dass dieser Thread mal die 50000er Lese-Marke knackt, hätte ich mir am 13.03.2009 nicht träumen lassen.
Zitat:
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?
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
kein problem, ist aber tatsächlich viel holz. wäre ein fulltime job für 1..2 monate (lernaufwand für PCI bussystem und hardware interrupt-handling auf pc-kisten nicht eingerechnet). aber nicht so, wie dieser trottel 'XanClic' erzählt. der scheint das ganze nur sabotieren zu wollen.
kein problem, ist aber tatsächlich viel holz. wäre ein fulltime job für 1..2 monate (lernaufwand für PCI bussystem und hardware interrupt-handling auf pc-kisten nicht eingerechnet). aber nicht so, wie dieser trottel 'XanClic' erzählt. der scheint das ganze nur sabotieren zu wollen.
Bei allem gebotenen Respekt, aber der "Trottel" XanClic hat vermutlich etwas mehr OS-Dev-Erfahrung als du (und auch als Erhard). Davon abgesehen scheinst du ja nicht vollkommen anderer Meinung sein, denn wenn du zwei Monate Vollzeit mal auf ein Freizeitprojekt umrechnest, bist du locker mit einem Jahr oder mehr dabei. Ein Jahr wohlgemerkt, in dem du ansonsten nichts anderes weiterentwickeln würdest.
Bei allem gebotenen Respekt, aber der "Trottel" XanClic hat vermutlich etwas mehr OS-Dev-Erfahrung als du (und auch als Erhard).
dass er sich besser mit der PC-plattform auskennt, zweifle ich garnicht an. aber dass er meine fragen nicht beantworten konnte und sein allgemein arrogantes gehabe, lassen darauf schliessen, dass er nur ein oberflächlicher frickler ist, dessen kleine erfolge ihn viel schweiss und gehirnschmalz gekostet haben. darauf kann er sich gern ausruhen und es als 'os-dev-erfahrung' verkaufen, aber als berater zum thema USB halte ich ihn für ungeeignet.
btw, ich hab' bisher nur usb-devices mit intelligenten controllern (vinculum) angesteuert, die den ganzen USB-protokollstack schon mitbringen. aber seit der diskussion von gestern, beginnt mich das thema zu interessieren (z.b. selbst mal 'nen USB-host für primitivere controller zu implementieren). der link von +gjm+ sieht auch sehr vielversprechend aus.
@erhard: falls ich in der richtung was machen sollte, kannste das gern in dein OS integrieren. ich werde vermutlich mein selbstgemachtes 'spezial-windows-ddk' dafür verwenden. das ist eine entwicklungsumgebung zur ansteuerung von hardware, die am PC angeschlossen wird (am PCI- bzw. PCMCIA-bus), die unter win-2k läuft. damit habe ich schon ein paar treiber geschrieben, die dann auf diverse mikrocontroller portiert wurden.
zweifle ich garnicht an. aber dass er meine fragen nicht beantworten konnte und sein allgemein arrogantes gehabe, lassen darauf schliessen, dass er nur ein oberflächlicher frickler ist, dessen kleine erfolge ihn viel schweiss und gehirnschmalz gekostet haben. darauf kann er sich gern ausruhen und es als 'os-dev-erfahrung' verkaufen, aber als berater zum thema USB halte ich ihn für ungeeignet.
Ich habe den Log gelesen (komme aus #lost, Erhard dürfte mich kennen ) und habe lediglich daraus schließen können, dass du nicht in der Lage bist, einen Text in einem Zusammenhang zu lesen und gegebene Ratschläge anzunehmen, fricky.
XanClic programmiert seit 3 Jahren Betriebssysteme und hat so einiges auf dem Kasten. Und natürlich hat er wie auch ich und wie auch taljeth und wie eigentlich jeder OS-Dever viel Gehirnschmalz und Fleiß investiert, um auf den heutigen Wissensstand zu kommen. Und natürlich kommt mit dem Lernprozess und den damit verbundenen Tiefschlägen und Fehler die man macht die Erfahrung. Er hat sehr wohl Erfahrung, ja sogar mehr als ich. Und "kleine Erfolge" ist ja wohl subjektiv, für mich war es damals ein großes Erfolg, "Hallo Welt" auf dem Bildschirm ohne Windows/Linux auszugeben.
Wieso hälst du ihn als Berater für das Thema USB ungeeignet? Weil er mehr Ahnung hat als du und du dich in deiner Kompetenz eingeschränkt siehst? Und außerdem hat er es mit Sicherheit nicht nötig, das Projekt zu sabotieren. Wieso auch, wenn er sich stundenlang mit einem so uneinsichtigen Menschen wie dir freiwillig unterhält?
Ps: Bevor du über Menschen urteilst, die du nicht kennst, lerne doch bitte Rechtschreibung sowie allgemeine Form und Schrift. Danke.
Zuletzt bearbeitet von DerHartmut am 12:36:56 13.10.2009, insgesamt 2-mal bearbeitet
XanClic programmiert seit 3 Jahren Betriebssysteme und hat so einiges auf dem Kasten.
mag ja sein, aber wenn er noch nicht mal 2 einfache fragen beantworten kann, dann hilft das keinen weiter, auch wenn er 30 jahre an seinem hobby-OS rumgebastelt hat.
DerHartmut schrieb:
Wieso hälst du ihn als Berater für das Thema USB ungeeignet? Weil er mehr Ahnung hat als du und du dich in deiner Kompetenz eingeschränkt siehst?
meine kompetenz bezüglich pc-hardware ist nahe bei 0, jedes kind, das schon mal 'nen pc zusammengeschustert hat, kennt sich in der hinsicht besser aus. XanClic ist als berater ungeeignet, weil er sein mühsam erworbenes (halb?)wissen nicht preisgeben kann oder will. wenn ich mich 'nen halben tag hinsetze, google, USB-specs, codebeispiele usw. durchgehe, bringt das sicherlich mehr.
mag ja sein, aber wenn er noch nicht mal 2 einfache fragen beantworten kann
Soweit ich das sehe, hab ich sie beantwortet. Du kannst sie mir hier gern nochmal genau stellen, damit ich das weiß.
Ich weiß zum Beispiel noch, dass du wissen wolltest, welche USB-Chipsätze es gebe. Ich hab dir gesagt, es gibt vier Host-Controller-Arten. OHC, UHC, EHC und xHC. Und für die brauchst du jeweils einen Treiber.
fricky schrieb:
XanClic ist als berater ungeeignet
Hab ich mich jemals als Berater bezeichnet? Ich bin gar keiner. Und ich konnte noch nie gut erklären.
fricky schrieb:
wenn ich mich 'nen halben tag hinsetze, google, USB-specs, codebeispiele usw. durchgehe, bringt das sicherlich mehr.
Dann tu das doch.
Das hab ich schließlich auch gemacht.
Ich weiß zum Beispiel noch, dass du wissen wolltest, welche USB-Chipsätze es gebe. Ich hab dir gesagt, es gibt vier Host-Controller-Arten. OHC, UHC, ...
was ist mit den usb-controllern in PCs? gibt es da einen standardtypen?
Genau das hat er gefragt. Und unter "Controller" verstehe ich bei USB nun einmal "Host Controller". Entschuldigung, dass ich das falsch wiedergegeben habe.
EDIT: Bevor ich hier beschuldigt werde, irgendwas aus dem Zusammenhang zu reißen. Mein Log:
Zitat:
[Mo Okt 12 2009] [22:54:01] <fricky> was ist mit den usb-controllern in PCs? gibt es da einen standardtypen?
[...]
[Mo Okt 12 2009] [22:54:21] <XanClic> fricky: Es gibt OHCI, UHCI, EHCI und seit 3.0 auch xHCI
[...]
[Mo Okt 12 2009] [22:54:49] <fricky> XanClic: heist so der chip(satz)?
[Mo Okt 12 2009] [22:54:55] <XanClic> fricky: Hm?
[Mo Okt 12 2009] [22:55:04] <XanClic> Wieviel weißt du über USB? ^^
[Mo Okt 12 2009] [22:55:07] <fricky> OHCI, UHCI usw?
[Mo Okt 12 2009] [22:55:17] <XanClic> Das nennt sich Host Controller
[Mo Okt 12 2009] [22:55:23] <XanClic> Und danach hast du gefragt
[Mo Okt 12 2009] [22:55:36] <XanClic> Strenggenommen müsste man die Is weglassen, also OHC, UHC, EHC und xHC
[Mo Okt 12 2009] [22:55:52] <fricky> XanClic: welche hardware ist dafür nötig. gibt es einen standard-chip im pc dafür?
[Mo Okt 12 2009] [22:56:04] <fricky> oder viele verschiedene?
[Mo Okt 12 2009] [22:56:07] <XanClic> Es gibt diese.
[Mo Okt 12 2009] [22:56:13] <XanClic> Für USB-1.x-Geräte gibt es OHC und UHC
[Mo Okt 12 2009] [22:56:16] <XanClic> Für 2.0 EHC
[Mo Okt 12 2009] [22:56:19] <XanClic> Und für 3.0 xHC
Hinweis: Die [...]-Sachen waren ein Gespräch mit ehenkes und haben mit diesem Gesprächsfaden nichts zu tun.
Zuletzt bearbeitet von XanClic am 17:02:46 13.10.2009, insgesamt 1-mal bearbeitet
Mo Okt 12 2009] [22:54:01] <fricky> was ist mit den usb-controllern in PCs? gibt es da einen standardtypen?
[...]
[Mo Okt 12 2009] [22:54:21] <XanClic> fricky: Es gibt OHCI, UHCI, EHCI und seit 3.0 auch xHCI
[...]
[Mo Okt 12 2009] [22:55:52] <fricky> XanClic: welche hardware ist dafür nötig. gibt es einen standard-chip im pc dafür?
[Mo Okt 12 2009] [22:56:04] <fricky> oder viele verschiedene?
[Mo Okt 12 2009] [22:56:07] <XanClic> Es gibt diese.
Ich weiß zum Beispiel noch, dass du wissen wolltest, welche USB-Chipsätze es gebe. Ich hab dir gesagt, es gibt vier Host-Controller-Arten. OHC, UHC, EHC und xHC.
und hätte ich dich gefragt: 'was ist ein auto?', dann hätteste du geantwortet: 'es gibt grüne, gelbe und blaue', nicht wahr?
DerHartmut schrieb:
Aber lass uns das doch einfach in #PrettyOS klären, der Anlaufstelle für einen unterhaltsamen und konstruktiven Abend
machen wir. ich schaue da bestimmt bald mal wieder rein.
und hätte ich dich gefragt: 'was ist ein auto?', dann hätteste du geantwortet: 'es gibt grüne, gelbe und blaue', nicht wahr?
Nö, nur wenn du mich gefragt hättest: "Was für Autos gibt es?" Und dann hätte ich gesagt: "Es gibt PKWs mit Automatik, ohne Automatik, LKWs, ... Du musst nicht wissen, wie du jedes einzelne Auto steuerst, weil sich alle Autos eines Typs gleich steuern lassen."
+gmj+ schrieb:
@Stasi 2.0: Ein "nein" hätte gereicht.
1. Super, wie du meine regionale Herkunft aus meinen Aussagen ableitest. 100 Punkte
2. "nein" ist doch gar keine richtige Antwort? Natürlich gibt es keinen Standard-Chip, aber es gibt vier Standardtypen, die sich jeweils gleich ansteuern lassen. btw, schön, dass mir angekreidet wird, nichts zu erklären, ich dann aber dafür als "Stasi" bezeichnet werden (weil ich logge, haha, dabei macht Konversation das doch automatisch für mich), wenn ich mal eine differenzierte Antwort gebe.
Zuletzt bearbeitet von XanClic am 19:16:46 13.10.2009, insgesamt 1-mal bearbeitet
Also zunächst möchte ich allen danken, die bereit sind, unsere OS-Community in ihren Anfängen zu unterstützen und sich im IRC channel #PrettyOS einfinden. Ich gehe davon aus, dass bei allen Teilnehmern das Interesse an Vermehrung von Wissen und praktischer Erfahrung im OS-Bereich im Vordergrund steht. Bei manchen Themen führt eine emotional engagierte Diskussion auch zu einer wirklichen Klärung anstelle zu fortgesetzten, lauwarmen theoretischen Diskussionen. Wir haben gesehen, dass das Thema USB-Treiber besetzt und bearbeitet werden muss, soweit ok. Ich denke, es ist auch klar geworden, dass wir zunächst ohne GRUB auskommen wollen. Hier lassen wir nicht mit uns reden. Soweit so gut.
Der IRC ist m.E. zur Klärung detaillierter Sachfragen nicht sonderlich gut geeignet. Dafür geht dort momentan noch zuviel durcheinander. Im Forum können wir solchen Themen, gestützt durch vertiefte Recherchen und Nachdenken, hoffentlich besser auf den Grund gehen.
Ich danke ;fricky und XanClic dennoch für die engagierte Diskussion und bitte alle darum, in diesem Thread und diesem wirklich sachlichen Subforum von persönlichen Angriffen und dem Zitieren unseres Chat-Protokolls abzusehen.
Wir sollten das Sachliche wieder rasch in den Mittelpunkt der Diskussion rücken. Gemeinsames Ziel ist ein breit funktionierender USB-Treiber, um auf moderner Hardware zu bestehen.
XanClic hat seine Arbeit übrigens großzügig zur Verfügung gestellt. Das macht nicht jeder!
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 20:06:39 13.10.2009, insgesamt 1-mal bearbeitet
Ich denke, es ist auch klar geworden, dass wir zunächst ohne GRUB auskommen wollen. Hier lassen wir nicht mit uns reden. Soweit so gut.
Das ist ja auch in Ordnung. Solange ihr nicht wollt, dass wir euren Bootloader fixen, ist das euer eigenes Problem.
Zitat:
Der IRC ist m.E. zur Klärung detaillierter Sachfragen nicht sonderlich gut geeignet.
Meine Erfahrung sagt, dass man im IRC im allgemeinen schneller zu einer Lösung kommt als im Forum. Ich würde die beiden Dienste ungefähr in einem Verhältnis sehen wie das Telefon zu einem Brief. Der Brief ist verbindlicher, aber Telefon geht schneller.
Zuletzt bearbeitet von taljeth am 20:26:48 13.10.2009, insgesamt 1-mal bearbeitet
IRC ist ein Telefonat mit Mitschnitt, also nicht wirklich vergleichbar. So etwas würde niemand privat oder geschäftlich nutzen! Das Ergebnis sieht man oben.
Ich bin wirklich an einer friedlichen Koexistenz im Dienst der Sache interessiert und bitte Dich uns diesbezüglich zu unterstützen.
Zitat:
Solange ihr nicht wollt, dass wir euren Bootloader fixen, ist das euer eigenes Problem.
Wie sagte PorkChicken so nett? Assembler ist etwas für die Elite, also offensichtlich für ihn, und ich denke auch für das Assembler-Forum hier.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 20:50:09 13.10.2009, insgesamt 1-mal bearbeitet
IRC ist ein Telefonat mit Mitschnitt, also nicht wirklich vergleichbar. So etwas würde niemand privat oder geschäftlich nutzen! Das Ergebnis sieht man oben.
Verallgemeinerungen sind immer falsch.
Wenn IRC weder privat noch geschäftlich genutzt werden würde, würde es mangels Nutzern nicht existieren. Offensichtlich ist es aber doch sehr existent.
Zitat:
Wie sagte PorkChicken so nett? Assembler ist etwas für die Elite, also offensichtlich für ihn, und ich denke auch für das Assembler-Forum hier.
Man setzt sich hin, liest die Spezifikationen für UHCI und EHCI, erkennt die Geräte über PCI und nutzt sie dann gemäß Spezifikation. Ja, so einfach kann das sein.
er hat aber recht, so wie's aussieht, steht im 'OpenHCI' spec alles wichtige drin, um die meisten USB-controller zum leben zu erwecken (nur eben ohne die USB2.0 funktionalität). man muss sich nur die zeit nehmen und sich damit auseinandersetzen. dann braucht man auch nicht 3 mal neu anzufangen (wer hat das gestern noch erzählt?). *fg*
btw, das buch mit dem vielversprechenden namen: 'USB Design by Example' konnte ich leider noch nirgends finden.
ach ja, für USB 2.0/'ehci': http://developer.intel.com/technology/usb/download/ehci-r10.pdf
er hat aber recht, so wie's aussieht, steht im 'OpenHCI' spec alles wichtige drin, um die meisten USB-controller zum leben zu erwecken (nur eben ohne die USB2.0 funktionalität).
Außer Intelrechner, die benutzen nicht OHCI, sondern UHCI.
Außer Intelrechner, die benutzen nicht OHCI, sondern UHCI.
wie ich bis jetzt mitbekommen habe, ist 'uhci' die älteste variante (USB 1.0). wikipedia sagt zwar, 'intel und VIA' benutzen noch UHCI, aber meine kiste zeigt mir 2 openHCI (USB 1.1) interfaces an, obwohl der host-controller von VIA ist.
btw: wie weit wird UHCI heute noch verwendet?
für alle, die wissen wollen um was es geht, hier ein link zur erklärung von OHCI/UHCI/EHCI: http://en.wikipedia.org/wiki/Open_Host_Controller_Interface
@ abc.w und +gjm+ : Hier eine Version, die mit USB-Floppy booten und bis in die Shell laufen sollte: http://www.henkessoft.de/OS_Dev/Downloads/107.zip
Will nur mal sehen, ob da alles klappt unter Linux mit USB-Floppy o.ä. zum Booten.
Die Shell wird mittels incbin in den Kernel eingeschleust. Das ist program.elf in der user-Subdirectory.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Wir möchten PrettyOS mittelfristig mit einem USB-Treiber ausstatten, da USB das moderne Bus-System ist. Hier ein Hinweis (aus dem IRC) von XanClic, wie man hierbei vorgehen könnte:
Zitat:
So, wenn ihr also unbedingt USB haben wollt. Ihr schreibt zuerst mal einen PCI-Treiber, der die Geräte auflisten kann und die USB-Host-Controller rausfindet. Dann lest ihr euch bei Wikipedia und in der OS-Dev-Wiki über USB ein.
Dann könnt ihr euch die Spezifikationen (OHCI, UHCI von den Hardwareherstellern bzw. s. Links bei WP; USB 2.0 von usb.org, ebenso die Massenspeicherspezifikation (Bulk only)) runterladen und anschließend damit einen hübschen Treiber schreiben.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Tobiking postete im bisherigen Google Groups Forum folgenden grundlegenden Beitrag, den ich für weitere Diskussionen nun hierher transferiere:
Zitat:
Ich habe noch ein paar Punkte die meiner Meinung nach geklärt werden
sollten bevor man groß anfängt Code zu schreiben. Das betrifft den
generellen Aufbau (Buildsystem etc.) und auch den Code selber, um
nicht später alles umschreiben zu müssen sobald sich etwas ändert. Im
IRC ist meist nicht die Zeit etwas größer zu Diskutieren, daher fang
ich das mal hier an.
Zum Buildsystem:
- Wie im IRC auch kurz erwähnt denke ich sollte man den ganzen Build
Prozess auf Makefiles umstellen. Ich sehe keinen Vorteil darin für den
Bootloader .bat dateien zu nehmen. Ob es elegant ist die Makefiles
aufzuteilen oder eine große zu haben kann man dann noch überlegen
- Das Erstellen des Kernels sollte auch leicht unter Linux machbar
sein. Mit einem zweiten Makefile oder mit einem include dürfte es
eigentlich einfach machbar sein das es auf den meisten Linux systemen
problemlos funktioniert.
Zum Code:
- Verschiedene Architekturen sind ja erstmal nicht angedacht, aber man
könnte trotzdem zumindest bei den Typen etwas Abstraktion einführen.
Dann wird statt unsigned int z.B. uint32_t oder u32 verwendet. Benutzt
man irgendwann einen anderen Compiler oder eine andere Architektur (64
Bit) kann man das mit einem typedef korrigieren. Bei den meisten
Strukturen kommt es ja genau auf die Größe an. Der gcc hat dafür sogar
einen Standard Header (stdint.h), den man ohne weiters nutzen kann. Da
gibt es auch noch ein paar andere Header die man nutzen kann und vor
allem Compiler spezifische Konstanten oder Macros enthalten.
- Struktur der Header. Zurzeit gibt es soweit ich gesehen habe einen
Header, der alles andere includiert. Das könnte irgendwann sehr
unübersichtlich werden. Ansonsten überlegen wie man die Header
ordentlich benennt. Mir ist da die Idee gekommen (und habs bei tyndur
ähnlich gesehen) das man sich an den C Standard orientiert.
Letztendlich schreibt man viele Funktionen die exakt so im C Standard
sind. Also kann man die Header auch angleichen.
- Naja Codestil allgemein ist nicht Lebensnotwendig aber schon eine
tolle Sache. Zeichensatz, Einrückung, ein paar Grundlegende Sachen
sollte man schon festlegen.
Mir fällt mit der Zeit bestimmt noch einiges ein. Das ist aber erstmal
alles was mir so gerade einfällt.
Zu guter letzt habe ich einfach mal mein Bastel OS hochgeladen in dem
ein paar der Punkte beachtet habe. Ich bin noch nicht so weit gekommen
und es hat an vielen Stellen noch Probleme, aber kann vielleicht die
ein oder andere Idee geben. Zu finden ist es unter:
Dies ist ja der Hauptthread, der meinen Einstieg in das "OSDEV" begleitet. Daher möchte ich mich hier sehr herzlich bei allen bedanken, die die bisherigen Gehversuche in die Welt des Kernels, der Treiber und des User-Spaces engagiert und tatkräftig unterstützen, sei es durch kleine Hilfestellungen, Hinweise, Diskussionen, Tests, eigene Experimente, kleine Aufsätze sowie Sourcecode. Ein funktionierendes OS ist die Summe vieler kleiner Schritte und Erkenntnisse. Ich wünsche diesem Subforum und unserer kleinen Gruppe um PrettyOS weiterhin viel Spaß, interessante Diskussionen und gutes Gelingen bei allen Vorhaben.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Den Helfern, Freunden und Gönnern dieser Gruppierung um das Thema OS-Development am Beispiel PrettyOS möchte ich aufs Herzlichste danken. Euch allen und euren Familien ein frohes und besinnliches Weihnachtsfest und einen erfolgreichen Start in das Jahr 2010!
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Da es heute gelungen ist, das Zusammenspiel von PCI, EHCI und USB 2.0 fehlerfrei zu betreiben, ist es an der Zeit diesen Thread mal wieder "auszupacken". Für mich ist das der Beweis, dass man vieles (nicht alles) bewirken und erreichen kann, wenn man es wirklich will und dran bleibt.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Nachtrag: der wirkliche Durchbruch bei USB2 MSD/SCSI fand in der zeit vom 11.05. zum 13.05. statt durch einen Versuch und Hinweis von +gjm+, der diesen Thread und das Projekt von Anfang an wohlwollend verfolgt hat. Ich kann nur hoffen, dass +gjm+ uns auch weiterhin treu verbunden bleibt.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 09:58:01 30.05.2010, insgesamt 2-mal bearbeitet
Weiterer Meilenstein:
Heute konnten Files von verschiedenen usb-Sticks (512 MB FAT16, 1 GB FAT16, 4 GB FAT32, 16 GB FAT32) per FAT16/32-Filesystem aufgespürt (root dir) und von usb MSD mittels SCSI command "read(10)" in den Speicher geladen (FAT, data area) werden. Damit hat die Floppy Disk (FAT12) als zentrales Speichermedium ausgedient.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 01:53:42 23.05.2010, insgesamt 1-mal bearbeitet
Nun kann man von Floppy Disk Device und USB Mass Storage Device laden. Der Device Manager hat an Abstraktionsgrad und Mächtigkeit gewonnen.
"1:\ttt.elf" (bei vorhandener Floppy sogar mit "ttt.elf")
Der device manager ist nun in seinen Grundzügen realisiert und läuft gut.
Nun kann man von Floppy oder usb MSD mit 1:/ttt.elf (durchgezählte angeschlossene Partitionen) bzw. A:/pqeq.elf eine Datei laden und starten.
A: ist die sogenannte Port-Ansprechweise
Ports sind bei uns: FDD, RAM, usb-slots
Disks sind bei uns: FloppyDisk, RAMDisk, usb-stick
Partitionen sind z.B. FAT12/16/32-Partitionen darauf. Wir zählen die Ports mit A,B,C, ... und die Partitionen mit 1,2,3,...
Wir haben also drei Ebenen:
1) die "Slot"-Ebene, das ist die feste Hardware: RAM, FDD, EHCI-Port
2) das eingeschobene Wechselmedium: RAMDisk, FloppyDisk, usb-MassStorageDevice
3) die Partition, die wir mit Namen und Serial kennen: part->serial
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 18:59:20 12.06.2010, insgesamt 2-mal bearbeitet
... und 14 Tage später trauen wir uns, auf usb Mass Storage Devices zu schreiben! Mit einem FAT12-Stick klappt es bereits analog zur Floppy.
FAT16 geht ebenfalls korrekt. Bezüglich FAT32 gibt es noch ein Problem bei vielen bereits vorhandenen Einträgen in der root dir (gelöschte zählen mit). Hier muss im FAT-Modul nachgebessert werden. Aber wie diesen Fehler finden?
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 10:23:46 27.06.2010, insgesamt 1-mal bearbeitet
Durch Zufall ist mir der Fehler beim Code studieren aufgefallen. Es geht eben nichts über Code Review. Da hatte ich eine Zeile testweise ausgeblendet (zum Glück nicht gestrichen), die hatte nun gerade für einige Fälle gefehlt.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Ein OS (im erweiterten Sinne) besteht nicht nur aus Elementen wie Bootloader, Kernel und Treiber, sondern im Hobby-Bereich auch aus den User-Programmen, die speziell für die API maßgeschneidert wurden. Beispiele sind die Spiele TicTacToe von Mrx und ARROW ATTACK von mir. Der "homo ludens" sucht solche Anwendungen, um sich an den Möglichkeiten der IT zu erfreuen und seine Kreativität zu entfalten. Daher kann ich nur immer wieder ermuntern, unterhaltsame und neuartige Programme für PrettyOS zu schreiben. Die API der Userlib ist noch nicht stabil, aber Programmanpassungen werden sicher kein Problem darstellen, da die API bei ihrem Fortschritt eher verbesserte Möglichkeiten anbieten wird, die das Erstellen erleichtern. Also nix wie ran.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 00:30:44 04.07.2010, insgesamt 1-mal bearbeitet
Whether for university, hobby, or commercial uses, operating system development takes time. The Linux kernel took over one year of very dedicated work to get into a semblance of usefulness, and all Linus Torvalds did was mimic existing and well-documented behaviour to get an already-existing userspace to run on it. Moreover, for every project as successful as Linux, there are literally hundreds of projects that consumed a man-year or more of work without ever getting as far as hosting a functional shell.
Therefore, plan a reasonable road map of what you want to get done. Do not assume that in 3 months your OS will have a GUI and voice recognition, because operating system development does not contain any RAD tools in it at all. In fact, it is completely void of them. (void. It's a joke. Get it?)
Wir haben nun über ein Jahr Entwicklung von PrettyOS (sicher weniger als 1 Mannjahr in Summe, denn es ist ja ein Hobby-Projekt neben Arbeit, Studium, Schule). Die Shell funktioniert bestens bisher. Die Roadmap wurde eingehalten: Eigener Bootloader anstelle GRUB, Laden/Speichern USB-Stick mit FAT. Netzwerk musste da halt zurück stehen mangels Developer Ressourcen. Man kann nicht alles gleichzeitg machen. EHCI, USB2 und FAT sind schwere Brocken. Nun kommen die "Manager" und die Schnittstellen. Manche machen es umgekehrt, ich möchte zuerst die Funktionalität sehen, wissen, dass es wirklich "geht".
Zitat:
Community Projects
Don't overestimate your chances of getting people interested in your project. Even the more successful projects usually consist of one, perhaps two people actually working on the code. And that is not due to a lack of need.
Brooks' Law states that the more people on a project, the longer it takes. The only way around this is to split the project into parts that you get people working on and only on that part. Good luck.
Wir sind zwei aktive Entwickler, vielleicht werden es ab und zu drei sein, die daran werkeln. Die Aufteilung hat bisher gut geklappt aufgrund der unterschiedlichen Interessengebiete. Aber eine Community von 5-10 Leuten, die daran koordiniert arbeiten, ist Illusion. Das muss man rasch einsehen. Im chat gewinnt man neue Leute, und es erhält die Lebendigkeit, leider geht auch Zeit verloren mit für die praktische Arbeit sinnlosen Debatten. Destruktive Stänkerer muss man leider immer wieder in die Schranken weisen. Ertragen muss man auch die "Idler". PrettyOS hat hier momentan einen guten Mix von Leuten, und es gibt ja auch "virtuelle Hinterzimmer" in den chat-Bezirken, wenn wichtige Dinge im kleinen Kreis zu klären sind.
Zitat:
Commercial OSDEV
There really is no such topic. OSDEV will probably never land you a job. (As shown in the "Jobs" section of the forum.)
Also, don't get your mind set that by building such a great OS that you'll be rich. If anything, history has shown us that the best operating systems never receive any commercial success, while the ones that have a near total lack of design and inspiration do, because of clever business moves and being in the right place, in the right time, with the right cover-up.
Das ist ein interessanter Punkt. PrettyOS wurde nie als Karriere-Sprungbrett oder als kommerzielles Projekt geplant, sondern als didaktische Hilfe zum Einstieg in OSDev. Daher auch mein Tutorial von Anfang an, das eher ein Skript/Logbuch für mich selbst war. Ein begleitendes e-book schwebte mir vor. Allerdings braucht das länger als geplant, bis ein Niveau erreicht wird, dass man die Dinge vernünftig ordnen und weiter geben kann. Man muss hier mit sich selbst und anderen geduldig sein. Die Erfahrung, die man selbst macht beim Aufbau eines OS und einer damit zusammen hängenden Community ist umfassend und tiefgehend. Man muss dazu bereit sein. Ansonsten gibt man auf. OSDev ist streckenweise "langweilig" und gleichzeitig fordernd.
Zitat:
GUI Design
It will likely take you several years, starting from scratch, to get to the point where you actually do anything GUI-related. And, the looks of a GUI are secondary at best, as they can easily be changed retroactively; what really decides the usability of a GUI is the functionality, and that isn't expressed in mock-up graphics. If your aim is creating a better look instead of a better OS, consider implementing an X Window Manager instead of a whole OS.
GUI und Maus, übrigens auch Sound sind sicher ein interessantes Feld, aber dies hält durch hohe Komplexität eher von wesentlichen Dingen ab. Ist momentan nicht auf der konkreten Roadmap.
Zitat:
OS Emulation
My OS will be able to run programs from Windows, Mac OS, Linux, and even PDP-11 programs!!!
I'm sorry to burst your bubble, but it probably won't. Emulating even just a single system takes years of work, especially when it's proprietary, such as Windows or Mac OS (Linux is probably the easiest out of the four.). Wine despite 15 years of development and being written in userspace have still problems with many programs.
So instead of focusing on emulating other systems, focus on your own. Design it, develop it, and be friends with it.
Wir schreiben unsere eigenen Programme, vielleicht wird die eine oder andere Anwendung portiert werden, aber das ist zweitrangig.
Zitat:
Naming
Naming is usually the last problem to be solved, even while we all wish for a cool name to our cool concept. Since the "coolness" of a name is a matter of taste, we cannot help you finding it. Moreover, if you tie your project name to a certain feature, you might discover somewhere down the road that no concept is perfect and that you might want to change what you initially thought a key feature. Nothing would be more stupid not to evolve just because you 'want to stick to a name'...
A lot of good information on naming can be found in this thread. Simply put, naming your operating system <name>OS (JackOS, FredOS, etc) may seem like a good idea, until you get a second project member. A good idea (courtesy of Solar) is to choose a codename (like Longhorn, Chicago, etc) and then make up a better name closer to release time.
Die Bezeichnung "PrettyOS" habe ich bereits sehr früh festgelegt, bisher habe ich es nicht bereut. Eine Namensänderung kommt nicht in Frage.
Man kann hier aber nur zu Vorsicht raten. Da hilft nur Intuition.
Zitat:
Programming Languages
Some languages are well suited to write an OS kernel, others are less so. Read the page about using some language other than C.
Zwischen C und C++ wird man wohl immer schwanken. Beides ist möglich, wobei ich bisher nur Erfahrung mit OSDev und C habe. Ich würde es gerne probieren ein OS in C++ zu bauen, aber am Wesentlichen ändert es nichts. Man wird sich mit C++ eher isoliert vorkommen, da die Literatur C oder Assembler einsetzt.
Zitat:
Booting Problems
Especially in early stages and with a self-built boot loader, the reason is frequently that too few sectors are fetched from disk. Either adjust the amount of sectors you fetch from disk, or have the boot loader / second stage loader parse the file system.
Inzwischen verwenden wir ein FAT12-Filesystem in dem verwendeten zweistufigen BL, ein ganz angenehmes aber ziemlich unflexibles Verfahren, wenn man z.B. von ubs-stick booten und nachladen will. Niemand hat die Zeit sich intensiv mit dem BL zu befassen. GRUB muss dennoch etwas warten, wobei wir vorbereitet sein müssen. Der Kernel wächst und wächst.
Zitat:
Strings
When your Kernel is written in C, gcc puts strings and constants in a special section called "read-only data". This section needs to be in your linkscript, in the .text section, otherwise it can cause all sorts of weird problems (unable to print text to the screen, kernel suddenly becoming 1MB larger, GRUB giving a loading error saying "kernel too large", etc). The section is called ".rodata" in ELF and ".rdata" in COFF/PE (the output format for MinGW/Cygwin). Building a GCC Cross-Compiler will help you to safely assume ".rodata" everywhere.
You could also tweak your linker script to include either section:
...
.text 0x100000 {
*(.text)
*(.rodata*) /* <---- ELF Cross Compiler or ELF *NIX (eg. Linux) */
*(.rdata*) /* <---- COFF/PE MinGW or Cygwin on Windows */
}
...
Die beste Antwort hierauf dürfte unser Linker-Script für den Kernel sein:
Wir hatten intern diskutiert, was wichtiger sei: Netzwerk oder Grafik (wenn auch nur für verbesserte und höheraufgelöste Textdarstellung). Die Antwort fiel eher zugunsten von Grafik aus. Daher haben wir VM86-Tasks eingeführt. Heute haben wir es zum ersten Mal geschafft, den Video-Mode mittels VM86-task stabil umzuschalten und ein paar bunte Tupfer darzustellen.
Der "Sündenfall" ist erfolgt. Das Text-"Paradies" ist beendet.
Nun wird die Pixel-"Hölle" folgen.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
VM86 Tasks laufen stabil. Damit ist das BIOS innerhalb des Protected Mode wieder zugänglich geworden. Wir verwenden die Interrupt Vector Table (IVT) zum Beispiel zur Ausführung von INT 10h. Das BIOS verzweigt in Speicherregionen oberhalb C0000h.
VM86 erlaubt uns das Umschalten des Video-Modes mittels 16 bit Real Mode Code mitten im Protected Mode.
Nun steigen wir mittels vbe.h/c in den Bereich der Grafik ein und werden Texte langfristig mit in Pixelmustern definierten Zeichen darstellen.
Als erstes Projekt steht der Bootscreen als 256-Farben-Bmp-File an.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 19:54:40 18.07.2010, insgesamt 1-mal bearbeitet
Positionierung und Palette sind zwar noch nicht korrekt, aber das Einlesen des bmp-headers und der bmp-Daten von hinten beginnend und das Auffüllen der Zeilen von rechts nach links gelingt schon.
Da liegt aber noch einiges an Arbeit zur Stabilisierung und bezüglich Fehlerabfragen vor dem Team.
EDIT: Mit Revision 0.0.1.112 hats geklappt mit der Geometrie und den Farben der Palette.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 21:26:17 26.07.2010, insgesamt 2-mal bearbeitet
Das "Ping" (Ping Request) hat sein "Pong" (Ping Reply) gesehen!
Auf zwei Systemen - einmal qemu via TAP (Linux), einmal PC über Router (WinXP) - unabhängig bestätigt.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Sehr interessantes Thema, auch wenn mir die Zeit dafür fehlt. Ich habe schon genug zu tun für ein bestehendes System zu programmieren. Obwohl ich das auch aufgegeben habe und nur noch Qt mit OpenGL setzen werde, damit das System darunter so ziemlich egal ist.
Aber ich schweife ab, ich frage mich ob der Thread hier ein Monolog ist? Und wenn ja warum du da nicht ne eigene Seite daraus machst als sowas in ein fremdes Forum zu posten?
@funcoder (nun offenbar unregistriert): ich verstehe, dass Du auch gerne osdev machen würdest, aber da muss man schon in der Lage sein, etwas mehr Tiefgang zu zeigen und sich intensiv in ein Thema hinein graben. Wenn Du keine Zeit hast, wirst Du halt User bleiben, selbst, wenn Du "programmierst".
Im übrigen bin ich von Anfang an dabei in diesem Forum, also ist mir das nicht "fremd".
Mit deinen <100 Beiträgen solltest du diesen Thread vielleicht mal von Anfang an lesen, um wirklich zu verstehen, was mich hier "treibt". Deine Anwürfe erscheinen mir, wie wenn der Hund den Mond anbellt.
@all: Dieser Thread ist das Rückgrat meines Einstiegs in OSDev. Daher wird er als Logbuch weiter geführt, solange dieses "Projekt" läuft.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 22:30:49 12.12.2010, insgesamt 5-mal bearbeitet
MrX hat - basierend auf Cuervo's Treiber für COMx - die Funktion serial_log eingebaut, dessen Ausgaben über COMx man z.B. bei VBox oder qemu in eine Textdatei umleiten kann. Ich habe dies sofort im vm86-monitor anstelle printf getestet, und es lief prächtig, langsam in VBox und schnell in qemu. http://henkessoft.de/OS_Dev/Bilder/serielle1.txt
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 00:45:25 18.09.2010, insgesamt 1-mal bearbeitet
Hi,
Könnt ihr mir sagen mit welcher Toolchain ich das kompilieren muss ?
Ich habe das Tutorial von "http://www.henkessoft.de/OS_Dev/OS_Dev1.htm" ausprobiert, letztens mit Cygwin. Aber ich bekomme beim linken immer den Fehler "File format not recognized"
Und wenn ich statt 'aout' z.b. 'elf' nehme, heißt es "cannot perform PE operations on not PE files" und wenn ich 'bin' nehme kann ich keine externen Referenzen einsetzen
Hinweis:
Ich empfehle diese DJGPP-Toolchain (gcc 3.1, ld 2.13, ...), da bei Einsatz der MinGW (GCC) Compiler Suite über Linker-Probleme mit dem in diesem Tutorial erfolgreich verwendeten NASM Output-Format a.out (assembler output)-Objektdatei-Format (inzwischen durch COFF und ELF abgelöst) berichtet wurde.
Inzwischen wurde im zweiten Ansatz (siehe Teil 2 dieses Tutorials) ein Work-around gefunden:
Momentan haben wir eine "Durchhängephase". Niemand hat Zeit, Energie usw. genug, um ein so komplexes Projekt auf freiwilliger Basis weiter zu treiben. Auch solche Zeiten gehören dazu.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Glücklicherweise zieht es langsam wieder an. Großes Lob an die Developer, die PrettyOS in voller Blüte erhalten und sogar weiter verfeinern. Namentlich erwähnen möchte ich MrX, der nun ca. 1 Jahr bei diesem Thema dabei ist.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Wow. Ich habe die ersten Seiten überflogen und dachte nur "Was für ein endlos erscheinender Aufwand". Dennoch bleibt ihr anscheinend bei der Sache (wayne Flauten) und zeigt richtig Durchhaltevermögen!
Ich habe mich bisher nicht intensiv mit solchen Themen beschäftigt, aber es kann gut sein, dass ich das Tutorial Mal reinschauen werde. Und auch sonst großes Lob an euch
Ich hab mir den Thread nicht komplett durchgelesen, aber dein Tutorial find ich bisher echt richtig gut. Ich habe mich schonmal kurz mit Assembler beschäftigt, dann aber mangels an Verständnis wieder abgebrochen. Bin dann letztens doch wieder auf die Idee gekommen mal ein eigenes kleines OS zu schreiben, da mir kein Projekt( normales Programm ) mit C/C++ eingefallen ist was mich motiviert hat. Ich wollt nicht was machen wo ich recht schnell fertig bin, sondern was wo länger geht und wo ich auch immer weiter ausbauen kann, was aber gleichzeitig auch weiterhin motiviert und mir viel bringt( in Bezug auf lernen ). Dadurch kam ich dann wieder auf die Idee mich mit Assembler und anschließend mit der OS-Entwicklung zu beschäftigen, dabei bin ich dann über dein Tutorial gestoßen und arbeite es gerade durch und es ist echt top. Werds jetzt erst mal durcharbeiten und dann selbst weiter am Ball bleiben;-)
Lg freeG und danke für das super Tutorial;-)
_________________ Das kann ich: C/C++, Asm, HTML, CSS, Javascript;
Hab per google das Tutorial gefunden... Ersmal riesen Lob: So ein schweres Thema so einfach erklärt!
Naja auf jeden Fall haben die ersten Teile noch gut funktioniert... Dann wurde der C-Kernel eingeführt und da fingen meine Probleme an...
Auf jedenfall kommt was ich auch mache bei dem Linker die Fehlermeldung:
"kernel.o: file not recognized: File format not recognized"
Hier genaueres:
==================================================
attempt to open kernel.o succeeded
opened script file kernel.o
kernel.o: file not recognized: File format not recognized
Bezieh mich hierbei auf Kapitel 1 des Tutorials.
Ich meine abc.w hatte auf Seite 13 vom selben Problem berichtet. leider hat seine Lösung bei mir irgendwie nicht funktioniert.
Könnte irgendeiner mir erklären wie ich das Problem wegkriege?? Bin wahrscheinlich einfach zu doof dafür......
MfG fishat64
p.s. Ich bitte meine Rechtschreibung zu entschuldigen!
Wenn unter Windows:
Du musst einen ld für ELF-Dateien verwenden (gibts hier: http://www.lowlevel.eu/wiki/Crosscompiler_f%C3%BCr_Windows). Das "Supported emulations: i386pe" sieht für mich danach aus, dass du einen LD für Windows-Binaries verwendest.
Freut mich, dass Du das so siehst, denn dies war genau mein Ziel.
Allerdings wird das Thema OSDev rasch komplex, auch bedingt durch die manuelle Nutzung aller Tools. Man lernt dabei aber eine Menge, vor allem Beharrlichkeit und Tiefgang.
So einfach also manche Wegstrecke ist, OSDev bietet immer wieder harte Hürden, aber genau dafür habe ich "PrettyOS" als konkretes Beispiel für ein Hobby-OS-Projekt, zuerst alleine später mit anderen zusammen, entwickelt. Im Team kommt man eher vom toten Punkt weg hin zu einer Lösung. Es gibt aber auch Gebiete, da bleibt man ziemlich einsam, so hart das auch sein mag. Das sind dann wirkliche Herausforderungen.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 00:19:34 06.06.2011, insgesamt 1-mal bearbeitet
Das Thema "Netzwerk" ist wieder voll in die Gänge gekommen. Ein neuer "developer", der professionelles know-how mitbringt, hat unser Team verstärkt. PrettyOS hat die Durststrecke hinter sich gelassen. Solche Phasen gibt es bei vielen Projekten, wahrlich kein Grund aufzugeben, sondern erst recht beharrlich zu bleiben.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 14:43:58 28.05.2011, insgesamt 1-mal bearbeitet
Wenn ich mich jetzt bei der OS-Entwicklung auch anmelden würde, würdet ihr mich dann erstmal über die OS-Entwicklung belehren? Assembler habe ich gestern angefangen.
Nach dem "passive open" ist nun auch das "active open" gelungen. Dabei senden wir das SYN und das ACK.
Der Datentausch gelingt bereits gut, z.B. sowohl als telnet Client als auch als Server. Server kann PrettyOS sein mit telnet als Client, oder auch umgekehrt mit PrettyOS als Client und dem telnet dienst (in Win XP) als Server bzw. einem selbst geschriebenen Server (z.B. dem von MrX).
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 00:20:23 19.06.2011, insgesamt 1-mal bearbeitet
Heute ist gelungen, dass sich ein User-Programm aus PrettyOS heraus in den IRC "einwählte" und sich in die Kanäle #PrettyOS und #Lost einloggte.
Wieder ein kleiner Meilenstein. Gerade die Nutzung des Internets macht wirklich Laune, und man lernt die einzelnen Anwendungen und Abläufe hierbei detailliert kennen. Wer Netzwerk bei OSDev macht, der kennt sich wirklich aus.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
ich wollt das ganze mit Netzwerk ja mal testen ,
nur hier auf dem PC läuft das fertige Image mit Bochs und Qemu nicht mehr richtig, und neu builden lässt sich das ganze auch nicht
der andere computer (laptop) lief gerade bei Bochs 'so nen bischen' heiß,
dann bin ich da auch nicht zu gekommen.
Wie wichtig und nützlich jedes einzelne Modul sein kann, erkennt man z.B. bei der Protokollierung ausgewählter TCP-Paketdaten über die serielle Schnittstelle in ein File mittels serial_log. Thx to Cuervo für den Einbau dieses Serial-Moduls. MrX hat es heute printf-analog adaptiert. Damit kann man hervorragend arbeiten und das OS im Hintergrund debuggen.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 01:04:05 16.07.2011, insgesamt 1-mal bearbeitet
Heute hat MrX seine Überarbeitung der VBE-Shell (Original stammt offenbar von internet) präsentiert. Ein weiterer Meilenstein in der Geschichte von PrettyOS.
Sein Kommentar im chat:
Zitat:
<ehenkes>MrX: tell us a little bit about about your vbe ideas
<MrX>I would like to create an interface that covers all graphical output inclusive text output.
<ehenkes>w/o windows?
<MrX>The aim is, to be able to use any graphics device to use PrettyOS.
<MrX>At the moment, windows are not supported, but support can be added.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 14:14:45 24.07.2011, insgesamt 2-mal bearbeitet
In version = "0.0.2.292 - Rev: 1144" wurde der bereits legendäre Zug beim Personenwaggon etwas aufgemotzt und ihm ein Schiff zur Seite gestellt. Spaß muss sein. Die Scrolling-Ticker-Line war eigentlich für wechselnde Informationen gedacht. Inzwischen ist dies auch in einigen Fällen geschehen, Z.B. stets aktuelle Floppy- oder malloc/free-Infos. Dieses Feature kann man sicher noch ausbauen. Cuervo hat uns Text-Message-Boxes geliefert. Dort wird z.B. abgefragt, ob man PrettyOS als User oder Developer verwenden will. Dem User könnte man kurzweilige bewegte Motive zeigen, dem Developer zusätzlich Informationen, z.B. über das Innenleben von PrettyOS.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Das "Umschalten" von 0.0.2.x auf 0.0.3.x kann man als Meilenstein bezeichnen, da die Zeitspanne zwischen zwei derartigen Sub-Versionen inzwischen sehr lange ist. Der nächste Schritt ist Umsetzung und Ausbau des IPC-Konzeptes.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Inzwischen ist usb durch allgemeine Transfers und Transaktionen soweit abstrahiert, dass man beginnen konnte, die HCs (bei PrettyOS: e/o/uhci) alle einzubinden. Heute wurde erstmals mit ohci begonnen, allerdings fehlt noch das saubere Management der nun variablen Ports pro HC.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 00:48:44 19.09.2011, insgesamt 1-mal bearbeitet
Meilenstein: Die USB 1.1 control-Transfers (mit ohci) laufen auf VBox! Hat länger gedauert, als ich geplant hatte, aber ich war bei ohci selbst leider alleine. Das abstrakte usb-transfer/transaction System ist gut einsetzbar.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Das Thema mit der Paketgröße ist inzwischen durch Aufteilung in mehrere Transaktionen und ein neues toggle-Steuerungssystem gelöst. Es gibt bei uhci allerdings noch Sticks, die bei den Transfers aussteigen, und bei ohci-Hardware Blockaden durch interrupts bzw. Enable-Probleme.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Man merkt nun, dass dieses PrettyOS-Projekt in eine neue Phase gelangt. Die Community ist leider wenig lebendig (siehe chat), und die Aufgaben kommen nicht mehr durch echte Notwendigkeiten, das OS funktionsfähig zu gestalten, auf die Developer zu. Verwendbar ist PrettyOS aber auch noch nicht. Dazu fehlt noch zu viel auf der user-Seite, abgesehen von einigen Hardware-Problemen.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Hallo,
ich verfolge mit Interesse dieses Projekt, hab aber imme wieder vergessen, mich einzulesen und mich zu beteiligen. So ein tolles Projekt will ich eigentlich nicht sterben sehen. Ich würde ein bisschen Werbung machen und versuchen, mich selbst einzubringen. Ich finde euer Projekt unglaublich spannend! Bitte macht weiter!
Ich finde euer Projekt unglaublich spannend! Bitte macht weiter!
Danke für das positive Feedback! MrX und ich, und sicher auch noch andere ab und an, werden bei PrettyOS weiter machen, soweit die Zeit es uns erlaubt. Allerdings wäre es wirklich hilfreich, wenn noch ein bis zwei aktive Developer/Tester dazu stoßen würden, wenn auch nur temporär, denn bei der Problemlösung hilft ein größeres Team zumeist (mehr Ideen, breitere Testbasis, höhere Motivation).
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 00:40:59 01.11.2011, insgesamt 1-mal bearbeitet
Heute kam auch das bereits seit langem eingebaute CDI am Beispiel des e1000 Treibers (Intel PRO/1000) zum Laufen. Der e1000 Treiber wurde von tyndur übernommen.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Allerdings wäre es wirklich hilfreich, wenn noch ein bis zwei aktive Developer/Tester dazu stoßen würden, wenn auch nur temporär, denn bei der Problemlösung hilft ein größeres Team zumeist (mehr Ideen, breitere Testbasis, höhere Motivation).
Ich könnt (soweit ich gerade nicht allzuviel mit der Schule bschäftigt bin) auch mal ein wenig mitcoden.
Als Tester bin ich eher wenig geeignet, denn ich hab quälend langsames EDGET-Internet, also sind große Downloads (z.B. Emulatoren) nur sehr schlecht möglich. Ich hätt aber 2 alte Test-PCs da (ich müsste mir nur endlich mal ein USB-Floppy-Drive für meinen Laptop zulegen, um das Ganze dann auch auf Floppys für die PCs zu bekommen). Als Emulator um mein eigenes Zeug zu testen hätt ich VBox im angebot.
Ich werd mir mal am Wochenende den Sourcecode anschauen (in der Woche wie gesagt Schule und deswegen zu wenig Zeit). Mal sehn ob ich auch ein paar Beiträge zu PrettyOS beisteuern kann.
_________________ Befehl: SOFORT bei Mafia anmelden. Wer das liest und es nicht macht, wird wegen Befehlsverweigerung angezeigt!!!
wäre dann internet in vBox möglich (oder geht es doch schon)?
Prinzipiell könnte es gehen. Ich habe Internet bisher nur mit qemu (rtl8139, pcnet, e1000) auf Win XP als Host geschafft. VBox hat eine PRO/1000, die unter "Host only" bis DHCP ACK läuft. Die Karte sendet und empfängt also korrekt. Vielleicht könnte da jemand heraus finden, wie man da weiter kommt. Aber wie gesagt, einfach qemu einsetzen (zur Zeit am besten noch 0.14.1).
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Durch die ausgebauten USB-Treiber kann man PrettyOS vielleicht auch bereits ohne Floppy-Disk verwenden. Man kann das Floppy-Image mit dd auf einen stick schreiben (der ist dann automatisch FAT12 formatiert) und von/nach dort direkt laden/speichern, oder man verwendet noch einen zweiten Stick (FAT32).
EHCI arbeitet inzwischen so schnell, dass man damit auch real-time größere Datenmengen (z.B. Ergebnisse mathematischer Berechnungen) schreiben kann.
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Zuletzt bearbeitet von Erhard Henkes am 23:10:38 01.11.2011, insgesamt 3-mal bearbeitet
wäre dann internet in vBox möglich (oder geht es doch schon)?
Prinzipiell könnte es gehen. Ich habe Internet bisher nur mit qemu (rtl8139, pcnet, e1000) auf Win XP als Host geschafft. VBox hat eine PRO/1000, die unter "Host only" bis DHCP ACK läuft. Die Karte sendet und empfängt also korrekt. Vielleicht könnte da jemand heraus finden, wie man da weiter kommt. Aber wie gesagt, einfach qemu einsetzen (zur Zeit am besten noch 0.14.1).
An dieser Stelle darf ich mich ganz herzlich bei allen bedanken, die bei diesem OS-Projekt dabei sind und ihm die Treue halten. Euch allen wünsche ich frohe und gemütliche Festtage und ein glückliches, erfolgreiches und gesundes 2012! :xmas1:
_________________ OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Nächstes Thema anzeigen Vorheriges Thema anzeigen
Sie können keine Beiträge in dieses Forum schreiben. Sie können auf Beiträge in diesem Forum nicht antworten. Sie können Ihre Beiträge in diesem Forum nicht bearbeiten. Sie können Ihre Beiträge in diesem Forum nicht löschen. Sie können an Umfragen in diesem Forum nicht mitmachen.
c++.de ist Teilnehmer des Partnerprogramms von Amazon Europe S.à.r.l. und Partner des Werbeprogramms, das zur Bereitstellung eines Mediums
für Websites konzipiert wurde, mittels dessen durch die Platzierung von Werbeanzeigen und Links zu amazon.de
Werbekostenerstattung verdient werden kann.
Die Vervielfältigung der auf den Seiten www.c-plusplus.de, www.c-plusplus.info, www.c-sar.de, www.c-plusplus.net und www.baeckmann.de
enthaltenen Informationen ohne eine schriftliche Genehmigung des Seitenbetreibers ist untersagt
(vgl. §4 Urheberrechtsgesetz). Die Nutzung und Änderung der vorgestellten Strukturen und Verfahren in
privaten und kommerziellen Softwareanwendungen ist ausdrücklich erlaubt, soweit keine Rechte Dritter verletzt werden.
Der Seitenbetreiber übernimmt keine Gewähr für die Funktion einzelner Beiträge oder Programmfragmente, insbesondere
übernimmt er keine Haftung für eventuelle aus dem Gebrauch entstehenden Folgeschäden.