| Zeile 34: | Zeile 34: | ||
und wünsche nun viel Verknügen. | und wünsche nun viel Verknügen. | ||
| − | === | + | ===Überblick=== |
Bei Multitasking gibt es 2 wichtige Teile, die man immer braucht. Der erste Teil ist eine | Bei Multitasking gibt es 2 wichtige Teile, die man immer braucht. Der erste Teil ist eine | ||
Tabelle, in der alle Tasks aufgelistet sind. Im zweiten Teil wird der aktuelle Taskzustand | Tabelle, in der alle Tasks aufgelistet sind. Im zweiten Teil wird der aktuelle Taskzustand | ||
| Zeile 84: | Zeile 84: | ||
Aufbau: | Aufbau: | ||
| − | + | http://www.joachim-neu.de/public/lowlevel/7/3_1.png | |
| + | |||
So. Nun werde ich den Aufbau genauer Erklären. Die <span style="background: #FFFF00">gelb</span> hinterlegten Einträge | So. Nun werde ich den Aufbau genauer Erklären. Die <span style="background: #FFFF00">gelb</span> hinterlegten Einträge | ||
beizeichnen das Offset des Eintrags zum Beginn der Tabelle. Alle <span style="background: #FF0000">rot</span> hinterlegten | beizeichnen das Offset des Eintrags zum Beginn der Tabelle. Alle <span style="background: #FF0000">rot</span> hinterlegten | ||
| − | Einträge haben den Wert | + | Einträge haben den Wert 0 und sollten den auch beinhalten. Alle <span style="background: #0000FF">blau</span> hinterlegten |
Einträge bezeichnen vom Programm beeinflussbare Einträge. Alle <span style="background: #800080">lila</span> hinterlegten | Einträge bezeichnen vom Programm beeinflussbare Einträge. Alle <span style="background: #800080">lila</span> hinterlegten | ||
Einträge müssen vom Betriebssystem gesetzt werden und sind nicht vom Programm | Einträge müssen vom Betriebssystem gesetzt werden und sind nicht vom Programm | ||
| Zeile 151: | Zeile 152: | ||
Auch in dieser Tabelle ist eine Zeile ein Word. | Auch in dieser Tabelle ist eine Zeile ein Word. | ||
| − | + | http://www.joachim-neu.de/public/lowlevel/7/3_3.png | |
Es gibt 2 Möglichkeiten, das Ende dieser Tabelle aus Task-Tabellen-Einträgen zu | Es gibt 2 Möglichkeiten, das Ende dieser Tabelle aus Task-Tabellen-Einträgen zu | ||
finden. | finden. | ||
| − | + | # Man speichert in einer Variable, wie viele Einträge man hat und überprüft das aktuelle und das maximale Offset, um einen WrapArround (beim Anfang anfangen) zu vollziehen. | |
| − | aktuelle und das maximale Offset, um einen WrapArround (beim Anfang anfangen) | + | # Man gibt am Ende der Tabelle dem nächsten leeren Eintrag die ID 0xFFFF oder ein sonstiges "Endezeichen" und führt einen WrapAround aus, sobald man als ID den Wert des Endezeichens gefunden hat. |
| − | zu vollziehen. | ||
| − | |||
| − | sonstiges "Endezeichen" und führt einen WrapAround aus, sobald man als ID den | ||
| − | Wert des Endezeichens gefunden hat. | ||
| − | Das war | + | Das war eigentlich schon das Wichtigste über die Task-Tabelle. |
===TaskGates=== | ===TaskGates=== | ||
| Zeile 171: | Zeile 168: | ||
(wieder ist jede Zeile ein Word) | (wieder ist jede Zeile ein Word) | ||
| − | + | http://www.joachim-neu.de/public/lowlevel/7/3_4.png | |
Alle Bereiche, die ''<span style="background: #FF0000">Reserviert</span>'' sind, sollten den Wert 0 beinhalten. Im Bereich ''<span style="background: #FFFF00">TSS-Deskriptor-Nummer</span>'' wird der Deskriptor festgehalten, auf den das TaskGate verweist. | Alle Bereiche, die ''<span style="background: #FF0000">Reserviert</span>'' sind, sollten den Wert 0 beinhalten. Im Bereich ''<span style="background: #FFFF00">TSS-Deskriptor-Nummer</span>'' wird der Deskriptor festgehalten, auf den das TaskGate verweist. | ||
| Zeile 217: | Zeile 214: | ||
folgende Anforderungen gestellt: | folgende Anforderungen gestellt: | ||
| − | + | # Sie muss fair sein, was heißt, dass alle Tasks von Grund auf gleich zu behandeln sind, sodass kein Task ständig dran kommt und kein Task nie dran kommt. | |
| − | sind, sodass kein Task ständig dran kommt und kein Task nie dran kommt. | + | # Sie darf nicht zu komplex sein, sodass siche die CPU mehr mit dieser als mit den Tasks beschäftigen würde. |
| − | + | # Sie muss unabhängig sein, das heißt, sie darf nicht mit der Anzahl der Tasks zusammenhängen, zum Beispiel nur bei 5 laufenden Tasks funktionieren. | |
| − | Tasks beschäftigen würde. | + | # Sie sollte berechenbar sein, also nicht auf Zufallswerten basieren oder auf jedem System anderst funktionieren. |
| − | + | # Sie darf nicht beeinflussbar oder manipulierbar sein. | |
| − | zusammenhängen, zum Beispiel nur bei 5 laufenden Tasks funktionieren. | ||
| − | |||
| − | System anderst funktionieren. | ||
| − | |||
Es gibt viele verschiedene Strategien. Die einen benutzen Prioritätsstufen zum Einteilen | Es gibt viele verschiedene Strategien. Die einen benutzen Prioritätsstufen zum Einteilen | ||
| Zeile 234: | Zeile 227: | ||
und damit anderen den Vortritt lässt, wenn er gerade keine Rechenzeit benötigt. Sehr | und damit anderen den Vortritt lässt, wenn er gerade keine Rechenzeit benötigt. Sehr | ||
häufige Scheduling Strategien sind ''Round Robin'', ''First Task First'' und ''Shortest | häufige Scheduling Strategien sind ''Round Robin'', ''First Task First'' und ''Shortest | ||
| − | Time Left''. Diese und einige andere werde ich nun ein bisschen | + | Time Left''. Diese und einige andere werde ich nun ein bisschen erläutern: |
| − | + | ===Round Robin=== | |
Bei Round Robin werden alle Tasks in einer Kette hintereinander abgearbeitet und | Bei Round Robin werden alle Tasks in einer Kette hintereinander abgearbeitet und | ||
jeder bekommt die gleiche Rechenzeit. Wenn man Prioritäten benutzt, so hat man | jeder bekommt die gleiche Rechenzeit. Wenn man Prioritäten benutzt, so hat man | ||
| Zeile 280: | Zeile 273: | ||
mehrere Möglichkeiten: | mehrere Möglichkeiten: | ||
| − | + | # Ein Far-Jump oder Far-Call bei dem als Selektor ein TSS angegeben wird. Der Offsetteil wird hierbei ignoriert. | |
| − | Offsetteil wird hierbei ignoriert. | + | # Ein IRET nachdem ein Task durch ein TaskGate in der IDT aufgerufen wird, also das NT-Flag gesetzt ist. |
| − | + | # Ein Far-Jump oder Far-Call auf ein Task-Gate. Auch hier wird der Offsetteil ignoriert. | |
| − | NT-Flag gesetzt ist. | ||
| − | |||
| − | ignoriert. | ||
Es gibt also 2 Möglichkeiten: | Es gibt also 2 Möglichkeiten: | ||
| Zeile 469: | Zeile 459: | ||
zum Programmieren allgemein. | zum Programmieren allgemein. | ||
| − | + | # Programmiere auf Sicherheit! Es geht nicht um einen Geschwindigkeitsrekord, sondern um ein stabiles Betriebssystem. Deswegen beachte und bedenke jeden Fehler, der auftreten könnte und richte Fehlerbehandlungen ein. Du musst sie nicht gleich rein Implementieren, aber schaffe (wenn auch nur leere) Funktionen dafür, die | |
| − | sondern um ein stabiles Betriebssystem. Deswegen beachte und bedenke jeden | + | du später erweitern kannst. Somit hast du ein sicheres Gerüst und gerätst nicht in die Falle, dass ein Code nicht geht, weil ein unerwarteter Fehler aufgetreten ist. |
| − | Fehler, der auftreten könnte und richte Fehlerbehandlungen ein. Du musst sie nicht | + | # Schreibe nicht alles ab! Code den Code selber. Es wird dir helfen, das Zeug zu verstehen und eigener Code lässt sich besser ins System integrieren. |
| − | gleich rein Implementieren, aber schaffe (wenn auch nur leere) Funktionen dafür, die | + | # Entscheide dich fest für ein System und mach keine Abstriche. Versuche deine Gedanken durchzusetzen und gib nicht auf, bloß, weil was nicht klappt. |
| − | du später erweitern kannst. Somit hast du ein sicheres Gerüst und gerätst nicht in die | + | # Achte auch die Optimierung deines Codes. Schon kleine Teile könnte vieles verschnellern und dein System sollte das Minimum an Leistung verbrauchen. |
| − | Falle, dass ein Code nicht geht, weil ein unerwarteter Fehler aufgetreten ist. | + | # Halte alles Modular, sprich baue auch für deinen Scheduler eine Funktion. Das wird es später erleichtern, die Funktion auszutauschen, wenn du eine andere Strategie verwenden willst. |
| − | + | # Und als letztes: Kommentiere deinen Code gut. Gerade in Assembler ist das sehr wichtig. | |
| − | verstehen und eigener Code lässt sich besser ins System integrieren. | ||
| − | |||
| − | Gedanken durchzusetzen und gib | ||
| − | |||
| − | verschnellern und dein System sollte das Minimum an Leistung verbrauchen. | ||
| − | |||
| − | es später erleichtern, die Funktion auszutauschen, wenn du eine andere Strategie | ||
| − | verwenden willst. | ||
| − | |||
| − | wichtig. | ||
Tja, das waren auch schon meine Tipps. | Tja, das waren auch schon meine Tipps. | ||
| Zeile 496: | Zeile 476: | ||
ermuntern, sich bei mir zu melden. Ich helfe, so gut ich kann. Meine Kontaktdaten gibt es im Impressum.</p> | ermuntern, sich bei mir zu melden. Ich helfe, so gut ich kann. Meine Kontaktdaten gibt es im Impressum.</p> | ||
| − | + | ==Designtechnisches: Multitasking== | |
| − | + | ||
| + | ===Einleitung=== | ||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
Dies ist eine Abwägung zwischen zwe Designideen. Ich will die beiden verschiedenen Möglichkeiten von Multitasking | Dies ist eine Abwägung zwischen zwe Designideen. Ich will die beiden verschiedenen Möglichkeiten von Multitasking | ||
| − | (hardwarebasierendes und softwarebasierendes) miteinander abwiegen. Dieser Text soll keine Antwort auf die Frage geben, | + | (hardwarebasierendes und softwarebasierendes) miteinander abwiegen. Dieser Text soll keine Antwort auf die Frage geben, welches Multitasking besser oder schlechter sei, sondern lediglich die verschiedenen Vor- und Nachteile aufzeigen und abwiegen. |
| − | welches Multitasking besser oder schlechter sei, sondern lediglich die verschiedenen Vor- und Nachteile aufzeigen und | + | |
| − | abwiegen. | + | ===Grundlagen=== |
| − | + | Was ist Multitasking? Dieser Text soll sich zwar darum nicht kümmern, trotzdem werde ich die grundlegendsten Grundlagen aufzeigen. Multitasking ist die Fähigkeit eines Betriebssystems, mehrere Programme, sogenannte Tasks gleichzeitig/parallel laufen zu lassen. Dies ist auf einem mehrprozessorigen System leicht zu verstehen, jeder Prozessor bekommt einen Task zugewiesen, was aber, wenn nur ein Prozessor vorhanden ist? Dann muss man diesen unter den verschiedenen Tasks aufteilen, de gerade bereit sind, ausgeführt zu werden. Man muss also den aktuellen Prozessorzustand speichern und den des nächsten | |
| − | Was ist Multitasking? Dieser Text soll sich zwar darum nicht kümmern, trotzdem werde ich die grundlegendsten Grundlagen | ||
| − | aufzeigen. Multitasking ist die Fähigkeit eines Betriebssystems, mehrere Programme, sogenannte Tasks gleichzeitig/parallel | ||
| − | laufen zu lassen. Dies ist auf einem mehrprozessorigen System leicht zu verstehen, jeder Prozessor bekommt einen Task | ||
| − | zugewiesen, was aber, wenn nur ein Prozessor vorhanden ist? Dann muss man diesen unter den verschiedenen Tasks aufteilen, | ||
| − | de gerade bereit sind, ausgeführt zu werden. Man muss also den aktuellen Prozessorzustand speichern und den des nächsten | ||
Tasks laden. Wichtig dabei ist, dass alle Werte gespeichert werden, die einen Task beschreiben, also Registerinhalte, | Tasks laden. Wichtig dabei ist, dass alle Werte gespeichert werden, die einen Task beschreiben, also Registerinhalte, | ||
Flags, aktuelle Position im Code und so weiter. Der Task an sich darf davon nix merken, er darf nicht bemerken, dass er | Flags, aktuelle Position im Code und so weiter. Der Task an sich darf davon nix merken, er darf nicht bemerken, dass er | ||
| − | unterbrochen wurde oder, dass etwaige Register nicht die selben Werte wie vorher haben. | + | unterbrochen wurde oder, dass etwaige Register nicht die selben Werte wie vorher haben. |
| − | Es gibt zwei | + | |
| + | Es gibt zwei verschiedene Methoden, Multitasking durchzuführen, kooperativ oder preemptiv. Kooperatives Multitasking besagt, | ||
dass das Programm die Kontrolle über den PC von alleine wieder an das Betriebssystem zurück gibt, welches diese dann neu | dass das Programm die Kontrolle über den PC von alleine wieder an das Betriebssystem zurück gibt, welches diese dann neu | ||
verteilt, preemptives Multitasking besagt, dass dem Task die Kontrolle in bestimmten Zeitabständen vom System entzogen und | verteilt, preemptives Multitasking besagt, dass dem Task die Kontrolle in bestimmten Zeitabständen vom System entzogen und | ||
| Zeile 530: | Zeile 499: | ||
das System umbiegen kann, und somit in regelmäßigen Abständen die Kontrolle über das System zurück erlangt. <br> | das System umbiegen kann, und somit in regelmäßigen Abständen die Kontrolle über das System zurück erlangt. <br> | ||
Es gibt auch Mischformen zwischen preemptivem und kooperativem Multitasking, in welchen das System darauf setzt, dass das | Es gibt auch Mischformen zwischen preemptivem und kooperativem Multitasking, in welchen das System darauf setzt, dass das | ||
| − | Programm die Kontrolle von alleine abgibt, dies aber über den Timer überwacht und notfalls einschreitet. | + | Programm die Kontrolle von alleine abgibt, dies aber über den Timer überwacht und notfalls einschreitet. |
| + | |||
Ein Multitaskingsystem benötigt immer einen Interrupt, das die Kontrolle entzieht, einen Dispatcher, der den Status | Ein Multitaskingsystem benötigt immer einen Interrupt, das die Kontrolle entzieht, einen Dispatcher, der den Status | ||
| − | speichert und beim neuen wieder läd und einen Scheduler, der den Task auswählt, der als nächstes zu starten ist. Der | + | speichert und beim neuen wieder läd und einen Scheduler, der den Task auswählt, der als nächstes zu starten ist. Der Scheduler wählt diesen Task anhand der Scheduling Strategie aus. |
| − | Scheduler wählt diesen Task anhand der Scheduling Strategie aus. | + | |
Die Unterschiede zwischen Softwaretasking und Hardwaretasking liegen lediglich im Dispatcher. | Die Unterschiede zwischen Softwaretasking und Hardwaretasking liegen lediglich im Dispatcher. | ||
| − | + | ===Softwaretasking=== | |
So, nun an die echte Arbeit. Softwaretasking. Beim softwarebasierenden Tasking setzt der Dispatcher darauf, den aktuellen | So, nun an die echte Arbeit. Softwaretasking. Beim softwarebasierenden Tasking setzt der Dispatcher darauf, den aktuellen | ||
Taskstatus auf dem taskeigenen Stack, dem Stapelspeicher zu speichern und später wieder von diesem zu laden. Verfechter | Taskstatus auf dem taskeigenen Stack, dem Stapelspeicher zu speichern und später wieder von diesem zu laden. Verfechter | ||
| Zeile 550: | Zeile 520: | ||
einfaches System, mit dem man schnell die Grundlagen von Multitasking erfüllen kann. Kompliziert wird es allerdings, wenn | einfaches System, mit dem man schnell die Grundlagen von Multitasking erfüllen kann. Kompliziert wird es allerdings, wenn | ||
man das Konzept ausbauen und verfeinern will. Außerdem kommt man nie ganz um TSS rum, auch bei Softwaretasking braucht man | man das Konzept ausbauen und verfeinern will. Außerdem kommt man nie ganz um TSS rum, auch bei Softwaretasking braucht man | ||
| − | mindestens ein TaskStateSegment. | + | mindestens ein TaskStateSegment. |
| + | |||
Auch stehen viele Funktionen des Protected Mode erst mit TSS richtig zur Verfügung, man denke an das explizite Sperren von | Auch stehen viele Funktionen des Protected Mode erst mit TSS richtig zur Verfügung, man denke an das explizite Sperren von | ||
Ports zum Beispiel. Aber auch deshalb verbraucht das Softwaretasking relativ wenig Speicher. Die Stacks können ausgelagert | Ports zum Beispiel. Aber auch deshalb verbraucht das Softwaretasking relativ wenig Speicher. Die Stacks können ausgelagert | ||
werden, sodass der Speicherverbrauch im RAM auf das Minimum reduziert wird und man kann auswählen, welche Informationen | werden, sodass der Speicherverbrauch im RAM auf das Minimum reduziert wird und man kann auswählen, welche Informationen | ||
| − | man speichern will, und welche nicht. | + | man speichern will, und welche nicht. |
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | + | {| | |
| − | Verwirrend wegen TSS | + | | + |
| − | Schwer zu erweitern | + | | - |
| − | Nicht alle Möglichkeiten des Protected Mode benutzbar | + | |- |
| − | + | | | |
| − | + | * Hohe Geschwindigkeit beim Taskwechsel | |
| − | + | * Sehr einfach zu implementieren | |
| + | * Wenig Platzverbrauch | ||
| + | | | ||
| + | * Verwirrend wegen TSS | ||
| + | * Schwer zu erweitern | ||
| + | * Nicht alle Möglichkeiten des Protected Mode benutzbar | ||
| + | |} | ||
| + | |||
| + | ===Hardwaretasking=== | ||
Hardwaretasking. Wie der Name schon sagt setzt das hardwarebasierende Multitasking auf die Möglichkeiten der CPU zum | Hardwaretasking. Wie der Name schon sagt setzt das hardwarebasierende Multitasking auf die Möglichkeiten der CPU zum | ||
Speichern des aktuellen Taskzustandes. Dazu gibt es ab jedem 286er ein sogenanntes TaskStateSegment, in welchem die | Speichern des aktuellen Taskzustandes. Dazu gibt es ab jedem 286er ein sogenanntes TaskStateSegment, in welchem die | ||
| Zeile 583: | Zeile 556: | ||
Auch ist alles das gleiche, man braucht also nicht wie bei Softwaretasking noch irgendein Segment oder ähnliches, wobei | Auch ist alles das gleiche, man braucht also nicht wie bei Softwaretasking noch irgendein Segment oder ähnliches, wobei | ||
man aber mindestens zwei Tasks braucht, die gerade laufen, da man sonst nicht wechseln kann, weil TSS-Tasks nicht reentrant | man aber mindestens zwei Tasks braucht, die gerade laufen, da man sonst nicht wechseln kann, weil TSS-Tasks nicht reentrant | ||
| − | sind, sprich man einen aktiven Task nicht wieder aktivieren kann. | + | sind, sprich man einen aktiven Task nicht wieder aktivieren kann. |
| − | + | {| | |
| − | + | | + | |
| + | | - | ||
| + | |- | ||
| + | | | ||
| + | * Die CPU nimmt viele Aufgaben ab | ||
| + | * Man kann alle Möglichkeiten des PM ausschöpfen | ||
| + | * Lässt sich leicht erweitern | ||
| + | |- | ||
| + | * Verbraucht viel Platz | ||
| + | * Taskwechsel dauert relativ lange | ||
| + | * Komplizierter Taskwechsel mit Opcodes | ||
| + | |} | ||
| − | + | ===Schluss=== | |
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
So, ich hoffe, ich hab mit dem Text hier einigen die Entscheidung zwischen Software- und Hardwaretasking erleichtert. | So, ich hoffe, ich hab mit dem Text hier einigen die Entscheidung zwischen Software- und Hardwaretasking erleichtert. | ||
Letztendlich liegt es nur am Programmierer selber, was er am liebsten mag. Bei Fragen bin ich auch wieder durch meine | Letztendlich liegt es nur am Programmierer selber, was er am liebsten mag. Bei Fragen bin ich auch wieder durch meine | ||
| − | Kontaktdaten im Impressum erreichbar. | + | Kontaktdaten im Impressum erreichbar. |
| − | + | ==Architekturtechnisches: Datenveränderungen im RAM== | |
| − | + | ===Einleitung=== | |
| − | |||
Ich schreibe dieses Tutorial für Leute, die wie ich anfangs das Prinzip des Speicherns von Daten im RAM nicht verstehen. | Ich schreibe dieses Tutorial für Leute, die wie ich anfangs das Prinzip des Speicherns von Daten im RAM nicht verstehen. | ||
Dabei geht es nicht um Programmiersprachenbefehle, sondern viel mehr darum, was mit den Daten im RAM gemacht wird. | Dabei geht es nicht um Programmiersprachenbefehle, sondern viel mehr darum, was mit den Daten im RAM gemacht wird. | ||
| − | Dieses | + | Dieses "Phänomen" kann man besonders gut sehen, wenn man mal das DWORD 0x12345678 byteweise ausließt. Man erhält |
| − | das hier: 0x78,0x56,0x34 und 0x12. Man stellt sich nun die Frage, warum dies so ist. Nun, vielleicht sind Ihnen | + | das hier: 0x78,0x56,0x34 und 0x12. Man stellt sich nun die Frage, warum dies so ist. Nun, vielleicht sind Ihnen |
| − | BigEndian und LittleEndian ein Begriff. Falls nicht, werde ich dies erstmal erklären. | + | BigEndian und LittleEndian ein Begriff. Falls nicht, werde ich dies erstmal erklären. |
| − | + | ||
| + | ===BigEndian / LittleEndian=== | ||
BigEndian ist eine Form, eine Zahl zu speichern. Bei dieser Form speichert man das wichtigste Bit (also das, das bei | BigEndian ist eine Form, eine Zahl zu speichern. Bei dieser Form speichert man das wichtigste Bit (also das, das bei | ||
einer Veränderung die Zahl am meisten beeinflussen würde) an erster Stelle und danach die anderen, also das zweitwichtigste | einer Veränderung die Zahl am meisten beeinflussen würde) an erster Stelle und danach die anderen, also das zweitwichtigste | ||
und so weiter. Dies ist das System, nach welchem auch wir unsere Zahlen darstellen. Die höchstwertigste Stelle als erstes. | und so weiter. Dies ist das System, nach welchem auch wir unsere Zahlen darstellen. Die höchstwertigste Stelle als erstes. | ||
| − | |||
| − | |||
LittleEndian ist das genaue Gegenteil von BigEndian. Es beginnt mit dem „last significant“ Bit, also dem unwichtigsten | LittleEndian ist das genaue Gegenteil von BigEndian. Es beginnt mit dem „last significant“ Bit, also dem unwichtigsten | ||
| − | im Gegensatz zu dem | + | im Gegensatz zu dem ''most significant'' Bit bei BigEndian. Das System von LittleEndian wird in der normalen Zahlenwelt |
der Menschen nicht benutzt. Jedoch benutzt es der Computer, da diese Art von Datenspeicherung ermöglicht, dass der | der Menschen nicht benutzt. Jedoch benutzt es der Computer, da diese Art von Datenspeicherung ermöglicht, dass der | ||
Computer Zahlenwerte aus dem RAM lesen kann, ohne vorher deren Anzahl von Stellen zu kennen. Bei LittleEndian muss | Computer Zahlenwerte aus dem RAM lesen kann, ohne vorher deren Anzahl von Stellen zu kennen. Bei LittleEndian muss | ||
der PC nur Bit für Bit lesen und die Zahl, welche er davor hat mit 2 multiplizieren, um dann das neue Bit zu addieren. | der PC nur Bit für Bit lesen und die Zahl, welche er davor hat mit 2 multiplizieren, um dann das neue Bit zu addieren. | ||
Bei BigEndian müsste der Computer wissen, wie viele Stellen die Zahl hat, um dem 1. Bit mit dem passenden Stellenwert | Bei BigEndian müsste der Computer wissen, wie viele Stellen die Zahl hat, um dem 1. Bit mit dem passenden Stellenwert | ||
| − | zu multiplizieren. | + | zu multiplizieren. |
| − | + | ||
| + | ===Auflösung=== | ||
Da wir nun also geklärt haben, was Big- und LittleEndian sind und deren Aufbau, sowie deren Benutzung geklärt haben, | Da wir nun also geklärt haben, was Big- und LittleEndian sind und deren Aufbau, sowie deren Benutzung geklärt haben, | ||
können wir mit einem Beispiel fortfahren. Gegeben ist eine Zahl von der Größe WORD, also 16 Bit. Diese sieht so aus: | können wir mit einem Beispiel fortfahren. Gegeben ist eine Zahl von der Größe WORD, also 16 Bit. Diese sieht so aus: | ||
| Zeile 631: | Zeile 604: | ||
0010110001001000. Dies ergibt in Hex 0x2C48. Da wir nun aber als kleinste Einheit Byts lesen können würden wir diese Bytes | 0010110001001000. Dies ergibt in Hex 0x2C48. Da wir nun aber als kleinste Einheit Byts lesen können würden wir diese Bytes | ||
erhalten: 0x34 und 0x12. Die beiden Bytes währen also vertauscht. Wenn man ein DWORD schreibt, und dies in Bytes ausließt, | erhalten: 0x34 und 0x12. Die beiden Bytes währen also vertauscht. Wenn man ein DWORD schreibt, und dies in Bytes ausließt, | ||
| − | erhält man zuerst das letzte, dann das vorletzte, dann das zweite und dann das erste Byte. | + | erhält man zuerst das letzte, dann das vorletzte, dann das zweite und dann das erste Byte. |
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | + | ===Einsatz=== | |
| + | Wichtig ist diese Kenntnis, wenn wir Tabellen, die wir sehr oft aus Tutorials bekommen in anderen Datentypen belegen und benutzen wollen, als vorgegeben. Wir müssen diese Phönomene erkennen und uns danach richten, damit wir immer die richtigen Stellen in einem Speicherteil benutzen. | ||
| − | + | ===Schluss=== | |
| − | + | So, das war auch schon alles, was ich dazu zu sagen hab. Ich hoffe, alles is klar geworden, falls nicht bin ich immer unter meinen Kontaktdaten erreichbar. Diese stehen im Impressum. | |
| + | |||
| + | ==Hardwaretechnisches: Der PC-Lautsprecher== | ||
| + | ===Theorie=== | ||
Wir werden jetzt zuerst ein bisschen Theorie machen und uns danach mit der Praxis beschäftigen. | Wir werden jetzt zuerst ein bisschen Theorie machen und uns danach mit der Praxis beschäftigen. | ||
Der PC Lautsprecher wird über den Timer-Baustein angesteuert, der als Zeitgeber und Zähler | Der PC Lautsprecher wird über den Timer-Baustein angesteuert, der als Zeitgeber und Zähler | ||
| Zeile 653: | Zeile 623: | ||
haben, weshalb im Code immer exakt geprüft werden sollte, ob die Port-Adressen stimmen.<br> | haben, weshalb im Code immer exakt geprüft werden sollte, ob die Port-Adressen stimmen.<br> | ||
Jeder dieser 3 Kanäle besitzt ein Latch-Register und ein Counter-Register, die beide 16 Bit breit | Jeder dieser 3 Kanäle besitzt ein Latch-Register und ein Counter-Register, die beide 16 Bit breit | ||
| − | sind, zwei Signaleingänge (CLOCK und GATE) sowie einen Signalausgang (OUT). | + | sind, zwei Signaleingänge (CLOCK und GATE) sowie einen Signalausgang (OUT). |
| + | |||
Jetzt zur Funktionsweise: Am Eingang CLOCK liegt eine konstante Frequenz von 1,19318 Mhz an. | Jetzt zur Funktionsweise: Am Eingang CLOCK liegt eine konstante Frequenz von 1,19318 Mhz an. | ||
(Das heißt, dass vom Timer 1193180 mal pro Sekunde eins an den Eingang geschickt wird.) In das | (Das heißt, dass vom Timer 1193180 mal pro Sekunde eins an den Eingang geschickt wird.) In das | ||
| Zeile 660: | Zeile 631: | ||
das Counter-Register den Wert 0 erreicht, wird ein Puls über Ausgang OUT geschickt. Nacheinander | das Counter-Register den Wert 0 erreicht, wird ein Puls über Ausgang OUT geschickt. Nacheinander | ||
entsteht so bei Wiederholung eine Frequenz von 1193180/Teiler, die beim Lautsprecher | entsteht so bei Wiederholung eine Frequenz von 1193180/Teiler, die beim Lautsprecher | ||
| − | ankommt, der das ganze dann in einen Ton umwandelt. | + | ankommt, der das ganze dann in einen Ton umwandelt. |
| + | |||
Das Kommandoregister wird zum Seuern dieses Bausteins mit dem Kommandobyte geladen, | Das Kommandoregister wird zum Seuern dieses Bausteins mit dem Kommandobyte geladen, | ||
| − | welches z.B. einstellt, welche Betriebsart gewählt wurde. Das Kommandobyte ist so aufgebaut: | + | welches z.B. einstellt, welche Betriebsart gewählt wurde. Das Kommandobyte ist so aufgebaut: |
| − | + | ||
| + | http://www.joachim-neu.de/public/lowlevel/7/6_1.png | ||
| + | |||
Die Betriebsart bestimmt die Länge der Pulsfolge und, ob sie sich automatisch wiederholt usw. Der | Die Betriebsart bestimmt die Länge der Pulsfolge und, ob sie sich automatisch wiederholt usw. Der | ||
Code 011 bestimmt, dass eine Pulsfolge erzeugt wird, die zum Erzeugen von Tönen nützlich ist. | Code 011 bestimmt, dass eine Pulsfolge erzeugt wird, die zum Erzeugen von Tönen nützlich ist. | ||
| Zeile 669: | Zeile 643: | ||
noch festgelegt, ob nur das High-Byte, nur das Low-Byte oder beide hintereinander (zuerst das | noch festgelegt, ob nur das High-Byte, nur das Low-Byte oder beide hintereinander (zuerst das | ||
Low-Byte, dann das High-Byte) bearbeitet werden sollen. Das 0.Bit legt fest, ob die Zahl im | Low-Byte, dann das High-Byte) bearbeitet werden sollen. Das 0.Bit legt fest, ob die Zahl im | ||
| − | Kommandobyte binär- oder BCD-codiert lesen soll. | + | Kommandobyte binär- oder BCD-codiert lesen soll. |
| − | Hier ein Beispiel: | + | |
| + | Hier ein Beispiel: Das Kommandobyte 0xB6 = 10110110 stellt ein, dass... | ||
| + | * der Timer-Kanal 2 programmiert werden soll, | ||
| + | * erst das Low-und dann das High-Byte bearbeitet werden soll | ||
| + | * eine symetrische Pulsfolge erzeugt werden soll, | ||
| + | * das Kommandobyte binär zu interpretieren ist. | ||
| + | |||
| + | Die Programmierung des Timer-Bausteins erfolgt in 2 Schritten: | ||
| + | # Kommandobyte in den Kommandoport schreiben, und dadurch die Anweisungen verteilen. | ||
| + | # Teiler in den Port des augewählten Timer-Kanals schreiben. | ||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
So jetzt könnten wir zwar Töne ausgeben, aber wir müssen dazu zuerst noch den Lautsprecher | So jetzt könnten wir zwar Töne ausgeben, aber wir müssen dazu zuerst noch den Lautsprecher | ||
anstellen (und später wieder ausstellen). Das ist ganz schlau geregelt. Der Lautsprecher ist über ein | anstellen (und später wieder ausstellen). Das ist ganz schlau geregelt. Der Lautsprecher ist über ein | ||
| Zeile 689: | Zeile 664: | ||
wieder ausgeschaltet. Das ist eigendlich schon die gesamte Theorie, kommen wir jetzt zur Praxis! | wieder ausgeschaltet. Das ist eigendlich schon die gesamte Theorie, kommen wir jetzt zur Praxis! | ||
| − | + | ===Praxis=== | |
Wir werden jetzt ein paar Funktionen für die Programmiersprache C zusammen entwickeln. | Wir werden jetzt ein paar Funktionen für die Programmiersprache C zusammen entwickeln. | ||
Nebenher schreiben wir die Funktionen dann auch noch in Assembler. | Nebenher schreiben wir die Funktionen dann auch noch in Assembler. | ||
| Zeile 696: | Zeile 671: | ||
Wir wissen, wir schalten den Lautsprecher an, indem die letzten 2 Bits in Port 0x61 auf 11b stehen. | Wir wissen, wir schalten den Lautsprecher an, indem die letzten 2 Bits in Port 0x61 auf 11b stehen. | ||
Daraus machen wir schnell eine Funktion: | Daraus machen wir schnell eine Funktion: | ||
| − | + | ||
| + | <pre> | ||
void switch_on() | void switch_on() | ||
{ | { | ||
outp(0x61,inp(0x61) | 3); | outp(0x61,inp(0x61) | 3); | ||
} | } | ||
| − | </pre> | + | </pre> |
| + | |||
Okay. In Assembler baue ich das schon alles mit in die _sound-Funktion ein. | Okay. In Assembler baue ich das schon alles mit in die _sound-Funktion ein. | ||
So, an können wir ihn machen, nur aus noch nicht, aber wir wissen, wenn die beiden letzten Bits in | So, an können wir ihn machen, nur aus noch nicht, aber wir wissen, wenn die beiden letzten Bits in | ||
Port 0x61 auf 00b stehen, dann ist der Lautsprecher aus. Das geht also ganz leicht: | Port 0x61 auf 00b stehen, dann ist der Lautsprecher aus. Das geht also ganz leicht: | ||
| − | + | ||
| + | <pre> | ||
void switch_off() | void switch_off() | ||
{ | { | ||
outp(0x61,inp(0x61) &~3); | outp(0x61,inp(0x61) &~3); | ||
} | } | ||
| − | </pre> | + | </pre> |
| + | |||
Und in Assembler: | Und in Assembler: | ||
| − | + | <pre> | |
_switch_off: | _switch_off: | ||
IN AL,61h | IN AL,61h | ||
| Zeile 718: | Zeile 697: | ||
OUT 61h,AL | OUT 61h,AL | ||
RET | RET | ||
| − | </pre> | + | </pre> |
| + | |||
Fertig. An und aus können wir ihn schon machen, aber das Herzstückt kommt noch! Einen Ton | Fertig. An und aus können wir ihn schon machen, aber das Herzstückt kommt noch! Einen Ton | ||
ausgeben. Das geht mit folgendem, diesmal zuerst in Assembler ;-): | ausgeben. Das geht mit folgendem, diesmal zuerst in Assembler ;-): | ||
| − | |||
| + | <pre> | ||
_sound: | _sound: | ||
MOV AX,34DDH ;Die Frequenz wird in BX übergeben. | MOV AX,34DDH ;Die Frequenz wird in BX übergeben. | ||
| Zeile 744: | Zeile 724: | ||
DONE1: | DONE1: | ||
RET | RET | ||
| − | </pre> | + | </pre> |
| + | |||
So, jetzt in C: | So, jetzt in C: | ||
| − | + | <pre> | |
void sound(unsigned frequenz) | void sound(unsigned frequenz) | ||
{ | { | ||
| Zeile 758: | Zeile 739: | ||
//switch_off aufgerufen werden. | //switch_off aufgerufen werden. | ||
} | } | ||
| − | </pre> | + | </pre> |
| − | + | ||
| + | ===Schluss=== | ||
So, geschafft! Das waren die Geheimnisse des PC-Lautsprechers! Zwei Sachen muss beim | So, geschafft! Das waren die Geheimnisse des PC-Lautsprechers! Zwei Sachen muss beim | ||
Nachmachen noch beachtet werden: 1. Es muss zwischen den Tönen eine Funktion benutzt werden, | Nachmachen noch beachtet werden: 1. Es muss zwischen den Tönen eine Funktion benutzt werden, | ||