Seite 1 von 1

Assembler und Windows

Verfasst: 04.10.2021, 20:36
von starcow
Abend zusammen :-)

Ich arbeite derzeit ein Tutorial durch, zum Thema Assembler auf dem 6502 Chip. Genauer gesagt, ist es ein Tutorial zur Assembler-Programmierung auf dem ATARI 2600 :-)
Ich hatte bislang noch nie etwas mit Assembler gemacht - und durch diese Auseinandersetzung mit der für mich neuen Thematik konnte ich nun einige neue Erkenntnissen gewinnen - also auch genereller Natur, unabhängig vom 6502 Chip.
Es hat aber auch, wie zu erwarten, einige neue Fragen aufgeworfen.

Wenn ich es denn richtig verstanden habe, lässt sich Assembler-Code 1:1 in "binären Prozessor-Code" übersetzen. Das heisst, es gibt für jeden Befehl in der entsprechenden Assembler-Sprache eine 1:1 Entsprechung als binären OP Code - was ja bei einer höheren Programmiersprache wie C nicht der Fall ist.
Wenn ich nun C-Code kompiliere, wird dann in jedem Fall zuerst Assembler-Code erzeugt? Oder erfolgt dieser Zwischenschritt nur auf verlangen?
Unabhängig davon, liegt ja am Ende des Prozesses Maschinencode vor. Also Maschinencode für z. B. den Atari oder eben die X86 Architektur.
Müsste dann ein Programm nicht universell auf allen X86 Plattformen laufen? Egal ob Linux, Windows oder OSX?
Wieso ist das nicht der Fall? An diesem Punkt fehlt mir gänzlich die Vorstellung, wie das Betriebssystem hier rein spielt.
Ist in diesem Maschinencode irgendwie doch mehr enthalten, als alleinige Instruktionen für die CPU? Also "Anweisungen" für ein spezifisches Betriebssystem? Erhalte ich denn anderer Assembler-Code, wenn ich für Windows kompiliere, als wenn ich für Linux kompiliert hätte?

Gruss und schönen Abend, starcow

Re: Assembler und Windows

Verfasst: 04.10.2021, 21:32
von Jonathan
[edit] Siehe bessere Antworten unten! [/edit]

Zunächst muss man sich wohl einigen, was "Assembler Code" ist. Man kann ein "add a, b" ja entweder als String speichern, oder als 3 Zahlen (die erste für die Befehlsnummer, die anderen 2 für die Parameterregister). Letzteres ist das Format, das letztendlich direkt von der CPU ausgeführt wird, die String-Darstellung ist nur nützlich, wenn der Nutzer entweder den Code lesen oder selber schreiben will. Da die 'Übersetzung' aber trivial ist, gibt es keinen Grund zunächst eine String-Variante beim Kompilieren zu erzeugen. Wenn man die braucht, kann man die on-the-fly generieren.

Zur zweiten Frage: Ich bin kein Experte, aber ich meine, dass z.B. eine exe-Datei zum einen schon direkt Code enthält, der prinzipiell von der CPU direkt ausgeführt werden kann und damit plattformunabhängig ist. Aber die exe-Datei enthält daneben auch weitere Informationen, die das Betriebssystem benötigt um den Code starten zu können und der Teil ist dann nicht plattformunabhängig. Außerdem glaube ich, dass das Betriebssystem beim Laden des Codes teilweise noch irgendwas mit dem Code anstellt (sich z.B. in Platzhalter für Speicherverwaltung einklinkt oder so?), so dass am Ende auch der Code-Teil vielleicht nicht identisch mit dem von anderen Plattformen ist. Sprich: Prinzipiell führt die CPU das selbe Programm auf unterschiedlichen Betriebssystemen schon mit dem selben Assemblercode aus (so denn die jeweiligen Compiler den selben erzeugen), aber daneben passieren noch ein paar andere Dinge, so dass man ausführbare Dateien nicht einfach so portieren kann. Aber andere können das sicherlich noch detaillierter erklären.

Re: Assembler und Windows

Verfasst: 04.10.2021, 23:58
von Krishty
starcow hat geschrieben: 04.10.2021, 20:36Wenn ich nun C-Code kompiliere, wird dann in jedem Fall zuerst Assembler-Code erzeugt? Oder erfolgt dieser Zwischenschritt nur auf verlangen?
Compiler generieren intern keinen Assembler-Code, sondern halten eine Compiler-abhängige Datenstruktur vor (die aber demnächst standardisiert werden soll). Meistens ist das die Static Single Assignment Form, weil sie sich am einfachsten optimieren lässt. Daraus wird dann direkt Machine Code emittiert, und den Compiler-Teil nennt man Backend. (Frontend ist entsprechend das Parsen deiner C-Quelldateien.)

Jeder große Compiler bringt aber auch ein textuelles Backend mit, mit dem du Assembler-Code ausgeben kannst – bei Visual Studio ist das /FA mit so tollen Optionen wie „Quelltext, Assembler-Befehle, und Machine Code ausgeben“. In Clang/GCC ist es -S.
starcow hat geschrieben:Unabhängig davon, liegt ja am Ende des Prozesses Maschinencode vor. Also Maschinencode für z. B. den Atari oder eben die X86 Architektur.
Müsste dann ein Programm nicht universell auf allen X86 Plattformen laufen? Egal ob Linux, Windows oder OSX?
Wieso ist das nicht der Fall?
Einerseits ist eine EXE nicht nur Maschinencode. Dort stehen auch die globalen Variablen drin; die Adressen; die Adressen der Adressen (für den Fall, dass der Code nicht an seine bevorzugte Position in den Speicher geladen werden kann); die minimal benötigte Menge an Stack- und Heap-Speicher; im Falle von Windows die Laufzeitumgebung (braucht der Prozess ein Konsolenfenster oder eine grafische Oberfläche?); usw usf.

Diese Zusatzinformationen liegen oft in einem OS-abhängigen Format vor. Die EXE- und DLL-Dateien von Windows sind bspw. Portable Executables; Linux und diverse Embedded-Systeme bevorzugen Common Object File Format. Es scheitert also schon am Dateiformat der Metadaten.

Weiterhin muss ein Programm Ein- und/oder Ausgaben umsetzen, sonst hat es kein beobachtbares Verhalten. Wie man aus der Konsole liest oder in eine Datei schreibt, ist von OS zu OS verschieden. Das an sich ist kein Problem – dann schiebt man den Code halt in ein austauschbares Modul – aber leider ist schon die Art, wie man andere Module aufruft, von OS zu OS verschieden. Auf Windows hast du DLLs; auf Linux Shared Objects. Windows befüllt eine Tabelle der externen Funktionen beim Laden deines PEs; Linux füllt die Tabelle on-Demand beim Aufruf. Die Art, wie Parameter übergeben werden, ist substanziell unterschiedlich (Calling Conventions).

Zuletzt kommen dann Unterschiede in der Umsetzung des C-Standards ins Spiel. C++-Exceptions und das Stack Unwinding beim Suchen des passenden catch nutzen unter Windows die Hilfe von Kernel-Aufrufen; unter Linux nicht. C hat zwar keine Exceptions, aber solche Konstrukte wie longjmp() mit ähnlicher Semantik. Das muss halt für jede Plattform unterschiedlich kompiliert werden.
Jonathan hat geschrieben: 04.10.2021, 21:32Zunächst muss man sich wohl einigen, was "Assembler Code" ist. Man kann ein "add a, b" ja entweder als String speichern, oder als 3 Zahlen (die erste für die Befehlsnummer, die anderen 2 für die Parameterregister). Letzteres ist das Format, das letztendlich direkt von der CPU ausgeführt wird
Nein; Assembler-Code ist der Quelltext ist Assembler-Sprache. Das Binäre, das die CPU liest, ist Machine Code. Ausgeführt werden am Ende µOps, die jede CPU unterschiedlich aus dem Machine Code erzeugt.

Von Assembler-Code zu Machine Code gibt es auch kein striktes 1:1-Mapping – zwei unterschiedliche Assembler-Befehle können auf den selben Machine Code mappen, und unterschiedlicher Machine Code kann den selben Assembler-Befehl bedeuten (bspw. das Lock-Flag in Befehlen, die keine Relevanz für Multi-Threading haben). Für die meisten Fälle ist das irrelevant, aber es sind halt unterschiedliche Sprachen und nicht beliebige Definitionen.

Re: Assembler und Windows

Verfasst: 06.10.2021, 17:27
von starcow
Uff! Vielen Dank für die ausführlichen Erklärungen!
Der Atari 2600 hatte ja kein OS. Könnte man denn analog zu dieser Idee ein Programm mit Assembler-X86 schreiben, dass ganz ohne OS ausgeführt werden könnte? Oder könnte man ein Programm in C schreiben und entsprechend kompilieren, so dass es sich zwar auf Windows nicht direkt ausführen liesse, jedoch direkt von der Hardware ausgeführt werden könnte - also quasi gebootet werden könnte?
Ich meine - natürlich müsste es ja irgendwie gehen, da die Betriebssysteme ja auch nicht einfach vom Himmel gefallen sind.
Daher anders gefragt: Was würde einen davon abhalten, sein (einfaches) Spiel - ganz im Stile einer alten Konsole - direkt für die X86 oder X64 Hardware zu schreiben?

Re: Assembler und Windows

Verfasst: 06.10.2021, 17:45
von Alexander Kornrumpf
starcow hat geschrieben: 06.10.2021, 17:27 Daher anders gefragt: Was würde einen davon abhalten, sein (einfaches) Spiel - ganz im Stile einer alten Konsole - direkt für die X86 oder X64 Hardware zu schreiben?
Kurz gesagt, dass die Hardware viel viel viel (man kann es nicht überbetonen) komplizierter geworden ist.

Um nur mal ein Beispiel zu geben, ich folge lose https://www.commanderx16.com/forum/inde ... about-faq/

Daraus:
Why PS/2 Keyboard and not USB?
USB is tremendously more difficult to implement than PS/2. A good analogy is like the difference between implementing RS-232 or Ethernet. PS/2 keyboards (and mice) are still manufactured, easy to find, and inexpensive. And, since the kernel is going to handle keyboard input, there’s no reason we can’t upgrade to USB later when we have the resources for that - and it shouldn’t break compatibility.

Why VGA instead of Composite or HDMI?
VGA is fairly easy to implement as compared to HDMI. And worst case, there are low-cost chips that can convert VGA to HDMI. And if you have to convert to HDMI, far better to convert from VGA than from composite
Und, wenn ich das hinzufügen darf, ein USB Keyboard ist natürlich nichts gegen eine moderne GPU.

Re: Assembler und Windows

Verfasst: 06.10.2021, 18:47
von NytroX
Das Hauptproblem sind Treiber. Die werden oft für ein bestimmtes OS geschrieben.
Du müsstest für jede Hardware wissen, wie sie genau funktioniert, und das verraten die meisten Hersteller nicht:
https://www.youtube.com/watch?v=_36yNWw_07g

Aber wo es gut funktioniert sind virtuelle Maschinen, da gibt es meistens die nötige Dokumentation zu der virtuellen Hardware.
Man kann z.B. ein OS als Library in ein C-Programm mit rein-linken. Das hat den Vorteil, dass solche Software kaum gehackt werden kann, weil der Compiler ja alles wegoptimiert, was dein Programm nicht nutzt (wenn man Link-Time-Optimization aktiviert hat).
Da kommt der Hacker nicht so recht auf die Maschine, weil es gibt keinen User, keinen root, keine Shell, kein Dateisystem, nix...
Ein Beispiel dazu wäre IncludeOS: https://www.includeos.org

Trotzdem könnte man ein relativ einfaches Spiel tatsächlich direkt laufen lassen indem man z.B. das BIOS verwendet und direkt in den Grafikspeicher schreibt.

Re: Assembler und Windows

Verfasst: 06.10.2021, 19:43
von Krishty
USB is tremendously more difficult to implement than PS/2.
<rant>USB ist außerdem Polling-basiert statt Interrupt-basiert. Sobald man ein USB-Keyboard oder eine USB-Maus ansteckt, fragt der USB-Controller 100+ Mal pro Sekunde „Wurde was gedrückt? Wurde was gedrückt? Wurde was gedrückt? …“. Das ist echt die dämlichste Variante, Eingaben zu realisieren.</rant>

Re: Assembler und Windows

Verfasst: 06.10.2021, 19:59
von Krishty
starcow hat geschrieben: 06.10.2021, 17:27Uff! Vielen Dank für die ausführlichen Erklärungen!
Der Atari 2600 hatte ja kein OS. Könnte man denn analog zu dieser Idee ein Programm mit Assembler-X86 schreiben, dass ganz ohne OS ausgeführt werden könnte? Oder könnte man ein Programm in C schreiben und entsprechend kompilieren, so dass es sich zwar auf Windows nicht direkt ausführen liesse, jedoch direkt von der Hardware ausgeführt werden könnte - also quasi gebootet werden könnte?
Ich meine - natürlich müsste es ja irgendwie gehen, da die Betriebssysteme ja auch nicht einfach vom Himmel gefallen sind.
Daher anders gefragt: Was würde einen davon abhalten, sein (einfaches) Spiel - ganz im Stile einer alten Konsole - direkt für die X86 oder X64 Hardware zu schreiben?
Jein

Du kannst etwas schreiben, das anstelle des Betriebssystems gebootet werden kann. Das geht auch *nur* ohne Betriebssystem, da du für die Verwaltung der Rechner-Peripherie z. B. Interrupts verarbeiten musst, und entsprechende Befehle sind privilegiert – also nur vom Betriebssystem ausführbar, nicht von Anwendungen. (Stell dir nur mal vor, ein Programm könnte einfach so die Speichertabellen austauschen, die das Betriebssystem nutzt!)

Die privilegierten Anweisungen wirst du aber nicht in C schreiben können, sondern via Inline-Assembly mit GCC oder via Intrinsics mit Visual Studio.

Re: Assembler und Windows

Verfasst: 06.10.2021, 22:14
von Alexander Kornrumpf
Krishty hat geschrieben: 06.10.2021, 19:59 Jein

Du kannst etwas schreiben, das anstelle des Betriebssystems gebootet werden kann. Das geht auch *nur* ohne Betriebssystem, da du für die Verwaltung der Rechner-Peripherie z. B. Interrupts verarbeiten musst, und entsprechende Befehle sind privilegiert – also nur vom Betriebssystem ausführbar, nicht von Anwendungen. (Stell dir nur mal vor, ein Programm könnte einfach so die Speichertabellen austauschen, die das Betriebssystem nutzt!)

Die privilegierten Anweisungen wirst du aber nicht in C schreiben können, sondern via Inline-Assembly mit GCC oder via Intrinsics mit Visual Studio.
Ich will mich öffentlich nicht zu sehr mit höchstens Halbwissen blamieren, aber in Bezug auf die Fragestellung scheint mir relevant zu sein, dass Protected Mode (oder entsprechende Äquivalente) an sich schon ein Quirk der x86 Architektur ist, und kein Naturgesetz.

Einerseits fällt es damit für mich immernoch unter die Überschrift "komplexere Hardware", andererseits ist die Hardware natürlich, wie das Beispiel zeigt, nicht zum Selbstzweck komplexer sondern um praktische Probleme zu lösen

Jonathan Blow zu dem Thema kennt ihr vermutlich eh alle: https://www.youtube.com/watch?v=pW-SOdj4Kkk

Re: Assembler und Windows

Verfasst: 06.10.2021, 22:28
von Krishty
Alexander Kornrumpf hat geschrieben: 06.10.2021, 22:14Einerseits fällt es damit für mich immernoch unter die Überschrift "komplexere Hardware", andererseits ist die Hardware natürlich, wie das Beispiel zeigt, nicht zum Selbstzweck komplexer sondern um praktische Probleme zu lösen

Jonathan Blow zu dem Thema kennt ihr vermutlich eh alle: https://www.youtube.com/watch?v=pW-SOdj4Kkk
Mir fiel dazu eher das Thirty-Million-Line-Problem ein, das mir lustigerweise direkt nach Blows Vortrag empfohlen wird. IIRC war die Kernthese, dass der Linux-Kernel wegen Plug & Play so unheimlich komplex geworden ist.


(Dass sie fünf Anläufe brauchten, um asynchrone I/O zu implementieren, mag aber Mitschuld tragen :P)

Edit: Oh lol, bei 18:22 erzählt er, dass jedes Amiga-Spiel mit seinem eigenen OS kam. Der Bogen zu starcow ist gespannt

Re: Assembler und Windows

Verfasst: 08.10.2021, 08:34
von Alexander Kornrumpf
Krishty hat geschrieben: 06.10.2021, 22:28 Mir fiel dazu eher das Thirty-Million-Line-Problem ein, das mir lustigerweise direkt nach Blows Vortrag empfohlen wird.
Ja, die sind in meinem Kopf wohl etwas verschwommen. Habe jetzt beide nochmal angesehen, Blow zitiert diesen, dieser ist also das "Original".

Ein Punkt der historisch überholt ist, ist sein "Apple und PC ist dieselbe Hardware, warum laufen da nicht dieselben Programme?". Apple baut jetzt wieder eigene Hardware, an mir war das aber völlig vorrübergegangen was die da genau machen. So schrieb z. B. jemand hier im Forum dass die Qualität von Apples Handykamera ja in der Software stecke, nicht in der Hardware. Das stimmt fast. Sie steckt nicht in der klassischen Kamerahardware (Sensor, Linse) aber die Bildverbesserungen machen sie _in Hardware_. So richtig gute Links gibt es nicht, das Stichwort für google ist "apple neural engine".

Praktisch wird das nicht viel mehr sein als schnelle parallele lineare Algebra, also eine GPU. Der Punkt ist, das war ja soviel ich weiß schon immer Apples Marketing, aber ganz falsch ist es nicht, wenn du die gesamte Maschine kontrollierst, dann kannst du natürlich dedizierte Hardware für Sachen bauen, die du oft machst, im Gegensatz zu Hardware, die alles können muss.

Der Vortrag ist ja wohl ursprünglich für Intel gewesen, aber ich denke Apple wäre hypothetisch in einer viel besseren Position um das umzusetzen. Mir scheint aber, Apples strategische Linie geht in die genau andere Richtung.

Re: Assembler und Windows

Verfasst: 08.10.2021, 08:50
von Krishty
Alexander Kornrumpf hat geschrieben: 08.10.2021, 08:34Der Punkt ist, das war ja soviel ich weiß schon immer Apples Marketing, aber ganz falsch ist es nicht, wenn du die gesamte Maschine kontrollierst, dann kannst du natürlich dedizierte Hardware für Sachen bauen, die du oft machst, im Gegensatz zu Hardware, die alles können muss.
Jetzt klingt es nach The value of in-house expertise: https://danluu.com/in-house/

Re: Assembler und Windows

Verfasst: 11.10.2021, 20:02
von starcow
Ein wirklich sehr spannendes Video - vielen Dank! Den Vortrag von Jonathan Blow zum Thema kannte ich bereits. Hat mich sehr fasziniert!
Wenn ich Casey Muratori jetzt richtig verstanden habe, würden sich sehr sehr viele Probleme quasi von selbst lösen, wenn jede Hardware nach aussen hin eine "saubere" ISA (Instruction Set Architekture) anbieten würde? Also das, was PC-Systeme aus den 80-ern und frühen 90-ern noch hatten. Und das, was ein Atari, NES, SNES, PlayStation (1996) oder N64 haben - richtig?
Verstehe ich das denn richtig, das Hersteller heutiger Hardware oftmals keine ISA bekannt geben? Stattdessen schreiben sie einfach Treiber für verschiedene Betriebssysteme (oder eben nur ein einzelnes!). Als Programmierer kommuniziere ich dann nur über den Treiber mit der Hardware - also quasi über einen Zwischenlayer? Das heisst, es ist mir nicht mehr möglich, "direkt" mit der Hardware zu sprechen?
Zudem:
Wenn ich meinen C/C++ Code kompiliere und linke, "spricht" dann beim Ausführen dieser Code immer direkt zur Hardware? Oder nur teils-teils - einerseits zum OS und andererseits direkt zur Hardware? An welcher Stelle klinkt sich denn das OS ein?

(Edit)
Wenn ich euch recht verstanden habe, passiert beides. Mich verwirrt an dieser Stelle, dass ich immer wieder auf Grafiken treffe, die es so illustrieren, als ob Anwendungssoftware nur mit dem Betriebssystem kommuniziert.

Bild

(Wikipedia Betriebssysteme)

Ich würds sehr gerne besser verstehen, nur wirkt dieses Thema irgendwie wie ein Buch mit sieben Siegel auf mich.
Vielleicht fehlen mir auch einfach noch nötige Grundlagen. Gibts vielleicht Übungen, die einen "hands-on" an das Thema heranführen?
Habt ihr vielleicht einen Tipp für mich?

Gruss starcow

Edit: Eine gute Quelle hab ich mir noch vermerkt, die ihr mir bereits empfholen hattet: www.lowlevel.eu

Re: Assembler und Windows

Verfasst: 12.10.2021, 19:40
von Lord Delvin
Betriebsysteme Vorlesung + Praktikum in einem Masterstudium würde das erklären.

Im Prinzip ist es grob so: Es gibt Sachen, die das Betriebssystem mithilfe der Hardware für dich macht. Dazu zählen echte Speicherallokationen und z.B. sowas wie sleep oder yield.
Dann gibt es Sachen, bei denen du direkt mit der Hardware redest, z.B. einfache Additionen und so ein Kram. Einfacher Code, der nur durch echte Prozessorinstruktionen realisiert wird.
Und dann gibt es noch neuerdings so Mitteldinge wie memory mapped files, die so halb direkt mit der Hardware gemacht werden und ab und zu bekommt das Betriebsystem einen Interrupt und macht dann was für dich...sehr vereinfacht dargestellt :)

Re: Assembler und Windows

Verfasst: 03.11.2021, 22:40
von NytroX
...weil ich gerade drübergestolpert bin und mich an diesen Thread erinnert habe:

Tetris für "BareMetal x86", also geschrieben ohne Betriebssystem:
https://www.youtube-nocookie.com/embed/FaILnmUYS_U