Montageschule: Betriebssystementwicklung. Flug des Kolibris. Wozu ein vollständig in Assemblersprache geschriebenes Betriebssystem in der Lage ist. Assemblieren und Kompilieren

Ich habe mich vor Kurzem entschieden, Assembler zu lernen, war aber nicht daran interessiert, Codezeilen zu verschwenden. Ich dachte, dass ich mit dem Assembler-Studium einige Fachgebiete beherrschen würde. Daher fiel meine Entscheidung darauf, einen Bootloader zu schreiben. Das Ergebnis meiner Erkenntnisse gibt es hier auf diesem Blog.

Ich möchte gleich sagen, dass ich die Kombination von Theorie und Praxis liebe, also fangen wir an.

Zuerst zeige ich Ihnen, wie Sie ein einfaches erstellen MBR damit wir uns schnellstmöglich über das Ergebnis freuen können. Da wir mit praktischen Beispielen komplexer werden, werde ich theoretische Informationen geben.

Lassen Sie uns zunächst einen Bootloader für ein USB-Flash-Laufwerk erstellen!

Aufmerksamkeit!!! Unser erstes Assembler-Programm funktioniert sowohl für ein Flash-Laufwerk als auch für andere Geräte wie eine Diskette oder Festplatte. Damit alle Beispiele korrekt funktionieren, werde ich anschließend einige Erläuterungen zur Funktionsweise des Codes auf verschiedenen Geräten geben.

Wir werden weiterschreiben Fasm, da es als der beste Compiler zum Schreiben von Ladeprogrammen gilt MBR. Der zweite Grund für die Wahl von Fasm ist, dass es das Kompilieren von Dateien sehr einfach macht. Keine Richtlinien Befehlszeile usw. Unsinn, der Sie völlig davon abhalten kann, Assembler zu lernen und Ihre Ziele zu erreichen. In der Anfangsphase benötigen wir also zwei Programme und einige unnötig Flash-Laufwerk mit minimaler Größe. Ich habe 1 GB ausgegraben (es formatiert sich schnell und ist, wenn überhaupt, nicht schade). Nachdem unser Bootloader funktioniert, funktioniert das Flash-Laufwerk nicht mehr normal. Mein Windows 7 weigert sich, das Flash-Laufwerk zu formatieren. Ich empfehle die Verwendung eines Dienstprogramms, um das Flash-Laufwerk wieder zum Leben zu erwecken PS USB-Disk Speicherformat-Tool ( HPUSBFW.EXE) oder andere Dienstprogramme zum Formatieren von Flash-Laufwerken.

Wir installieren sie und werfen die entsprechenden Verknüpfungen auf den Desktop oder wo immer Sie möchten.

Die Vorbereitungen sind abgeschlossen, jetzt geht es los mit der Aktion

Öffnen Sie Fasmw.exe und schreiben Sie dort Folgendes. Wir werden das Nötigste an Code skizzieren, um das Ergebnis zu sehen. Später werden wir analysieren, was hier gekritzelt wird. Ich werde meine Kommentare kurz abgeben.

FASM-Code: ============= boot.asm ==============

org 7C00h ; Unsere Programmadressen werden unter Berücksichtigung dieser Richtlinie berechnet

use16 ; Es wird ein Hexadezimalcode generiert

cli ;Interrupts zum Ändern von Adressen in Segmentregistern deaktivieren

mov ax, 0

mov sp, 7C00h

sti ;Interrupts aktivieren (nach Adressänderung)

mov ax, 0003h ;Stellt den Videomodus für die Anzeige einer Zeile auf dem Bildschirm ein

int 10h

mov ax, 1301h ;die eigentliche Ausgabe des Strings ist die Funktion 13h int 10h (mehr Details später)

mov bp, stroka ;Adresse des Ausgabestrings

mov dx, 0000h ;Zeile und Spalte, in der der Text angezeigt wird

mov cx, 15 ;Anzahl der Zeichen der Ausgabezeichenfolge

mov bx, 000eh ;00-Seitennummer des Videos (besser nicht berühren) 0e-Zeichenattribute (Farbe, Hintergrund)

int 10h

jmp $ ;auf Wasser treten (das Programm wird an dieser Stelle in einer Schleife ausgeführt)

string db „Ok, MBR geladen!“

mal 510 - ($ - $$) db 0 ;den Raum zwischen dem vorherigen Byte und dem nächsten mit Nullen füllen

db 0x55 ,0xAA ;letzte zwei Bytes

Kompilieren Sie diesen Code (Strg+F9) in fasm"e und speichern Sie die resultierende Binärdatei als boot.bin an einem geeigneten Ort. Bevor wir unsere Binärdatei auf ein Flash-Laufwerk schreiben, ein wenig Theorie.

Wenn Sie ein Flash-Laufwerk an den Computer anschließen, ist es für das BIOS-System absolut nicht offensichtlich, dass Sie vom Flash-Laufwerk booten möchten. Daher müssen Sie in den BIOS-Einstellungen das Gerät auswählen, von dem Sie booten möchten. Also haben wir uns entschieden um von USB zu booten (Sie müssen selbst herausfinden, wie das geht, da die BIOS-Schnittstelle verschiedene Variationen hat ... Sie können es googeln BIOS-Einstellungen für dein Hauptplatine. Da gibt es in der Regel nichts Kompliziertes.

Da das BIOS nun weiß, dass Sie von einem Flash-Laufwerk booten möchten, muss es sicherstellen, dass der Sektor Null auf dem Flash-Laufwerk bootfähig ist. Dazu führt das BIOS einen Scan durch letzten zwei Bytes des Sektors Null und wenn sie gleich 0x55 0xAA sind, wird nur dann geladen RAM. Andernfalls umgeht das BIOS einfach Ihr Flash-Laufwerk. Nachdem es diese beiden magischen Bytes gefunden hat, lädt es den Sektor Null in den RAM an der Adresse 0000:7С00h, vergisst dann das Flash-Laufwerk und überträgt die Kontrolle an diese Adresse. Jetzt liegt die gesamte Macht über den Computer bei Ihrem Bootloader und dieser kann aus dem RAM heraus zusätzlichen Code von einem Flash-Laufwerk laden. Jetzt werden wir sehen, wie genau dieser Sektor im DMDE-Programm aussieht.

1. Stecken Sie Ihr Flash-Laufwerk in den Computer und stellen Sie sicher, dass es nicht die benötigten Informationen enthält.

2.Öffnen Sie das DMDE-Programm. Lesen Sie alle weiteren Schritte in den Bildern:

Nachdem Sie diesen Comic gesehen haben, können Sie Ihren MBR auf ein Flash-Laufwerk laden. Und so sieht das lang erwartete Ergebnis unseres Laders aus:


Wenn wir über den minimalen Bootloader-Code sprechen, könnte dieser übrigens so aussehen:

Org 7C00h
jmp$
db 508 dup(0)
Datenbank 0x55.0xAA

Nachdem ein solcher Bootloader die Kontrolle übernommen hat, hängt er den Computer einfach auf und führt einen bedeutungslosen jmp $-Befehl in einer Schleife aus. Ich nenne sie Wassertreten.

Ich habe auf YouTube ein Video gepostet, das Ihnen helfen könnte:

Abschließend noch ein paar kurze Fakten zum Bootloader:

1. Der Bootloader, auch Bootloader, auch MBR genannt, hat eine Größe von 512 Byte. Historisch,
dass diese Bedingung erfüllt sein muss, um ältere Medien und Geräte zu unterstützen.
2. Der Bootloader befindet sich immer im Nullsektor eines Flash-Laufwerks, einer Diskette, Festplatte, aus Sicht des DMDE-Programms oder anderer Hex-Editoren, mit denen Sie mit Geräten arbeiten können. Um eine Binärdatei (unsere boot.bin) auf eines der aufgelisteten Geräte zu laden, müssen wir uns keine Gedanken über deren interne physische Struktur machen. Das DMDE-Programm weiß einfach, wie es die Sektoren auf diesen Geräten liest und zeigt sie im LBA-Modus an (nummeriert sie einfach von 0 bis zum letzten Sektor). Sie können über LBA lesen
3. Der Bootloader muss immer mit zwei Bytes 0x55 0xAA enden.
4. Der Bootloader wird immer unter der Adresse 0000:7С00h in den Speicher geladen.
5. Das Betriebssystem beginnt mit dem Bootloader.


Original: AsmSchool: Erstellen Sie ein Betriebssystem
Autor: Mike Saunders
Veröffentlichungsdatum: 15. April 2016
Übersetzung: A. Panin
Übersetzungsdatum: 16. April 2016

Teil 4: Mit den Fähigkeiten, die Sie durch das Lesen der vorherigen Artikel dieser Serie erworben haben, können Sie mit der Entwicklung Ihres eigenen Betriebssystems beginnen!

Wofür ist das?

  • Um zu verstehen, wie Compiler funktionieren.
  • CPU-Anweisungen verstehen.
  • Um Ihren Code hinsichtlich der Leistung zu optimieren.

Im Laufe mehrerer Monate haben wir einen schwierigen Weg zurückgelegt, der mit der Entwicklung begann einfache Programme in Assemblersprache für Linux und endete im letzten Artikel der Serie mit der Entwicklung von eigenständigem Code, der auf einem Personal Computer ohne Betriebssystem läuft. Nun werden wir versuchen, alle Informationen zusammenzutragen und ein echtes Betriebssystem zu erstellen. Ja, wir werden in die Fußstapfen von Linus Torvalds treten, aber zuerst müssen wir die folgenden Fragen beantworten: „Was ist ein Betriebssystem? Welche seiner Funktionen müssen wir neu erstellen?“

In diesem Artikel konzentrieren wir uns nur auf die Grundfunktionen des Betriebssystems: das Laden und Ausführen von Programmen. Komplexe Betriebssysteme führen viele weitere Funktionen aus, beispielsweise die Verwaltung und Verarbeitung des virtuellen Speichers Netzwerkpakete, aber ihre korrekte Implementierung erfordert jahrelange kontinuierliche Arbeit, daher werden wir in diesem Artikel nur die Grundfunktionen betrachten, die in jedem Betriebssystem vorhanden sind. Letzten Monat haben wir ein kleines Programm entwickelt, das in den 512-Byte-Sektor einer Diskette (ihren ersten Sektor) passt, und jetzt werden wir es ein wenig modifizieren, um die Funktion zum Laden zusätzlicher Daten von der Diskette hinzuzufügen.

Bootloader-Entwicklung

Wir könnten versuchen, die Größe unseres Betriebssystem-Binärcodes so weit wie möglich zu reduzieren, um ihn in den ersten 512-Byte-Sektor der Diskette zu passen, der vom BIOS geladen wird, aber in diesem Fall wäre uns das nicht möglich um interessante Funktionen zu implementieren. Daher werden wir diese 512 Bytes verwenden, um den Binärcode eines einfachen System-Bootloaders unterzubringen, der den Binärcode des Betriebssystemkernels in den RAM lädt und ausführt. (Danach werden wir den Betriebssystemkernel selbst entwickeln, der den Binärcode anderer Programme von der Festplatte lädt und ihn auch ausführt, aber darüber werden wir etwas später sprechen.)

Sie können den Quellcode für die in diesem Artikel besprochenen Beispiele unter www.linuxvoice.com/code/lv015/asmschool.zip herunterladen. Und das ist der Code unseres System-Bootloaders aus einer Datei namens boot.asm:

BITS 16 jmp Kurzstart ; Zur Marke springen, Disc-Beschreibung überspringen nop ; Zusatz vor der Festplattenbeschreibung %include "bpb.asm" start: mov ax, 07C0h ; Ladeadresse mov ds, ax ; Datensegment mov ax, 9000h ; Stapelvorbereitung mov ss, ax mov sp, 0FFFFh ; Der Stapel wächst nach unten! cld ; Setzen des Richtungsflags mov si, kern_filename call load_file jmp 2000h:0000h ; Übergang zum OS-Kernel-Binärcode, geladen aus der Datei kern_filename db „MYKERNELBIN“ %include „disk.asm“ mal 510-($-$$) db 0 ; Auffüllen des Binärcodes mit Nullen bis zu 510 Bytes dw 0AA55h ; Bootloader-Binär-Endmarkierungspuffer: ; Beginn des Puffers für Festplatteninhalte

In diesem Code ist der erste CPU-Befehl der jmp-Befehl, der sich nach der BITS-Direktive befindet, die dem NASM-Assembler mitteilt, dass der 16-Bit-Modus verwendet wird. Wie Sie sich wahrscheinlich aus dem vorherigen Artikel der Serie erinnern, beginnt die Ausführung des 512-Byte-Binärcodes, der vom BIOS von der Festplatte geladen wird, von Anfang an, aber wir müssen zu einer Marke springen, um einen speziellen Datensatz zu überspringen. Offensichtlich haben wir letzten Monat den Code einfach an den Anfang der Festplatte geschrieben (mit dem Dienstprogramm dd) und den Rest des Speicherplatzes leer gelassen.

Jetzt müssen wir eine Diskette mit einem geeigneten MS-DOS-Dateisystem (FAT12) verwenden und um mit diesem Dateisystem korrekt zu arbeiten, müssen wir am Anfang des Sektors einen Satz spezieller Daten hinzufügen. Dieser Satz wird als BIOS-Parameterblock (BPB) bezeichnet und enthält Daten wie Datenträgerbezeichnung, Anzahl der Sektoren usw. Es sollte uns an dieser Stelle nicht interessieren, da mehr als eine Artikelserie diesem Thema gewidmet werden könnte, weshalb wir alle damit verbundenen Anweisungen und Daten in einer separaten Quellcodedatei namens bpb.asm abgelegt haben.

Basierend auf dem oben Gesagten ist diese Anweisung aus unserem Code äußerst wichtig:

%include „bpb.asm“

Dies ist eine NASM-Anweisung, die es ermöglicht, den Inhalt einer angegebenen Quelldatei während der Assemblierung in die aktuelle Quelldatei einzubinden. Auf diese Weise können wir unseren Bootloader-Code so kurz und verständlich wie möglich gestalten, indem wir alle Details der Implementierung des BIOS-Parameterblocks in einer separaten Datei platzieren. Der BIOS-Parameterblock muss sich drei Bytes nach dem Start des Sektors befinden, und da der jmp-Befehl nur zwei Bytes beansprucht, müssen wir den nop-Befehl verwenden (sein Name steht für „no operation“ – das ist ein Befehl, der dies tut). nichts als CPU-Zyklen verschwenden), um das verbleibende Byte zu füllen.

Arbeiten mit dem Stapel

Als nächstes müssen wir Anweisungen verwenden, die denen im letzten Artikel ähneln, um Register und den Stapel vorzubereiten, sowie die cld-Anweisung (steht für „Clear Direction“), die es uns ermöglicht, das Richtungsflag für bestimmte Anweisungen zu setzen, z B. den lodsb-Befehl, der bei seiner Ausführung den Wert im SI-Register erhöht, anstatt ihn zu dekrementieren.

Danach geben wir die Adresse des Strings in das SI-Register ein und rufen unsere Funktion „load_file“ auf. Aber denken Sie einen Moment darüber nach – wir haben diese Funktion noch nicht entwickelt! Ja, das stimmt, aber die Implementierung finden Sie in einer anderen von uns eingebundenen Quellcodedatei mit dem Namen disk.asm.

Das FAT12-Dateisystem, das auf in MS-DOS formatierten Disketten verwendet wird, ist eines der einfachsten verfügbaren Dateisysteme, aber die Arbeit mit seinem Inhalt erfordert auch eine beträchtliche Menge Code. Die Unterroutine „load_file“ ist etwa 200 Zeilen lang und wird in diesem Artikel nicht gezeigt, da wir uns mit der Entwicklung eines Betriebssystems und nicht mit der Entwicklung eines Treibers für ein bestimmtes Dateisystem befassen. Daher ist es nicht sehr ratsam, darauf Speicherplatz zu verschwenden Log-Seiten auf diese Weise. Im Allgemeinen haben wir die Quellcodedatei disk.asm fast vor dem Ende der aktuellen Quellcodedatei eingefügt und können sie vergessen. (Wenn Sie immer noch an der Struktur des FAT12-Dateisystems interessiert sind, können Sie die hervorragende Übersicht unter http://tinyurl.com/fat12spec lesen und sich dann die Quellcodedatei disk.asm ansehen – der darin enthaltene Code ist gut kommentiert .)

In beiden Fällen lädt die Load_File-Routine den Binärcode aus der im SI-Register genannten Datei in Segment 2000 bei Offset 0, woraufhin wir zur Ausführung an den Anfang springen. Und das ist alles – der Betriebssystemkernel ist geladen und der System-Bootloader hat seine Aufgabe erledigt!

Möglicherweise ist Ihnen aufgefallen, dass unser Code MYKERNELBIN anstelle von MYKERNEL.BIN als Betriebssystem-Kernel-Dateinamen verwendet, was gut in das 8+3-Benennungsschema passt, das auf Disketten unter DOS verwendet wird. Tatsächlich verwendet das FAT12-Dateisystem eine interne Darstellung von Dateinamen, und wir sparen Platz, indem wir einen Dateinamen verwenden, der garantiert nicht erfordert, dass unsere Load_File-Routine einen Mechanismus zum Nachschlagen des Punktzeichens und zum Konvertieren des Dateinamens in den implementiert interne Darstellung des Dateisystems.

Nach der Zeile mit der Anweisung zum Verbinden der Quellcodedatei disk.asm gibt es zwei Zeilen, die den Binärcode des System-Bootloaders mit Nullen bis zu 512 Bytes auffüllen und die Endmarkierung seines Binärcodes einschließen sollen (dies wurde besprochen). im vorherigen Artikel). Schließlich befindet sich ganz am Ende des Codes die Bezeichnung „buffer“, die von der Routine „load_file“ verwendet wird. Grundsätzlich benötigt die Routine „load_file“ freien Speicherplatz im RAM, um bei der Suche nach einer Datei auf der Festplatte einige Zwischenarbeiten zu erledigen, und wir haben nach dem Laden des Bootloaders genügend freien Speicherplatz, also platzieren wir den Puffer hier.

Um den System-Bootloader zusammenzustellen, verwenden Sie den folgenden Befehl:

Nasm -f bin -o boot.bin boot.asm

Jetzt müssen wir ein virtuelles Disketten-Image im MS-DOS-Format erstellen und unseren Bootloader-Binärcode mit den folgenden Befehlen zu den ersten 512 Bytes hinzufügen:

Mkdosfs -C floppy.img 1440 dd conv=notrunc if=boot.bin of=floppy.img

An diesem Punkt kann der Prozess der Entwicklung eines System-Bootloaders als abgeschlossen betrachtet werden! Wir verfügen jetzt über ein bootfähiges Disketten-Image, mit dem wir den Binärcode des Betriebssystemkerns aus einer Datei namens mykernel.bin laden und ausführen können. Als nächstes erwartet uns ein interessanterer Teil der Arbeit – die Entwicklung des Betriebssystemkernels selbst.

Betriebssystemkernel

Wir möchten, dass unser Betriebssystemkernel viele wichtige Aufgaben ausführt: eine Begrüßungsnachricht anzeigen, Eingaben des Benutzers akzeptieren, feststellen, ob es sich bei der Eingabe um einen unterstützten Befehl handelt, und Programme von der Festplatte ausführen, wenn der Benutzer ihre Namen angibt. Dies ist der Kernelcode des Betriebssystems aus der Datei mykernel.asm:

Mov ax, 2000h mov ds, ax mov es, ax loop: mov si, prompt call lib_print_string mov si, user_input call lib_input_string cmp byte , 0 je loop cmp word , „ls“ je list_files mov ax, si mov cx, 32768 call lib_load_file jc Load_fail Aufruf 32768 JMP-Schleife Load_Fail: Mov Si, Load_fail_msg Aufruf lib_print_string JMP-Schleife List_files: Mov Si, File_list Aufruf lib_get_file_list Aufruf lib_print_string JMP-Schleife Prompt DB 13, 10, „MyOS >“, 0 Load_fail_msg DB 13, 10, „Nicht gefunden! ", 0 user_input mal 256 db 0 file_list mal 1024 db 0 %include „lib.asm“

Bevor Sie sich den Code ansehen, sollten Sie auf die letzte Zeile mit der Anweisung achten, die Quellcodedatei lib.asm einzubinden, die sich auch im Archiv asmschool.zip auf unserer Website befindet. Dies ist eine Bibliothek nützlicher Routinen für die Arbeit mit Bildschirm, Tastatur, Zeichenfolgen und Festplatten, die Sie auch verwenden können. In diesem Fall fügen wir diese Quellcodedatei der Reihe nach ganz am Ende der Hauptquellcodedatei des Betriebssystemkerns ein Letzteres so kompakt und schön wie möglich zu gestalten. Weitere Informationen finden Sie im Abschnitt „Lib.asm-Bibliotheksroutinen“. Weitere Informationenüber alle verfügbaren Unterprogramme.

In den ersten drei Zeilen des Kernelcodes des Betriebssystems füllen wir die Segmentregister mit Daten, um auf Segment 2000 zu verweisen, in das der Binärcode geladen wurde. Dies ist garantiert wichtig korrekte Bedienung Anweisungen wie lodsb, die Daten aus dem aktuellen Segment und nicht aus einem anderen lesen müssen. Danach werden wir keine weiteren Operationen an den Segmenten mehr durchführen; Unser Betriebssystem läuft mit 64 KB RAM!

Weiter im Code gibt es eine Beschriftung, die dem Anfang der Schleife entspricht. Zunächst verwenden wir eine der Routinen aus der Bibliothek lib.asm, nämlich lib_print_string, um die Begrüßung auszudrucken. Die Bytes 13 und 10 vor der Begrüßungszeile sind Escape-Zeichen. Neue Zeile, wodurch die Begrüßung nicht unmittelbar nach der Ausgabe eines Programms, sondern immer in einer neuen Zeile angezeigt wird.

Danach verwenden wir eine weitere Routine aus der lib.asm-Bibliothek namens lib_input_string, die die Tastatureingaben des Benutzers entgegennimmt und sie in einem Puffer speichert, auf den im SI-Register verwiesen wird. In unserem Fall wird der Puffer am Ende des Kernelcodes des Betriebssystems wie folgt deklariert:

User_input mal 256 db 0

Mit dieser Deklaration können Sie einen Puffer mit 256 Zeichen erstellen, der mit Nullen gefüllt ist – seine Länge sollte ausreichen, um Befehle für ein einfaches Betriebssystem wie unseres zu speichern!

Als nächstes führen wir eine Benutzereingabevalidierung durch. Wenn das erste Byte des user_input-Puffers Null ist, hat der Benutzer einfach die Eingabetaste gedrückt, ohne einen Befehl einzugeben. Vergessen Sie nicht, dass alle Zeichenfolgen mit Nullzeichen enden. In diesem Fall sollten wir also einfach zum Anfang der Schleife gehen und die Begrüßung erneut drucken. Falls der Benutzer jedoch einen Befehl eingibt, müssen wir zunächst prüfen, ob er den ls-Befehl eingegeben hat. Bisher konnten Sie in unseren Assemblerprogrammen nur Vergleiche einzelner Bytes beobachten, vergessen Sie aber nicht, dass es auch möglich ist, Doppelbyte-Werte oder Maschinenwörter zu vergleichen. In diesem Code vergleichen wir das erste Maschinenwort aus dem user_input-Puffer mit dem Maschinenwort, das der ls-Zeile entspricht, und wechseln, wenn sie identisch sind, zum Codeblock darunter. Innerhalb dieses Codeblocks verwenden wir eine andere Routine aus lib.asm, um eine durch Kommas getrennte Liste von Dateien auf der Festplatte abzurufen (die im Puffer „file_list“ gespeichert werden sollte), diese Liste auf dem Bildschirm auszugeben und zurück in die Schleife zu wechseln Benutzereingaben verarbeiten.

Ausführung von Drittprogrammen

Wenn der Benutzer den ls-Befehl nicht eingibt, gehen wir davon aus, dass er den Namen des Programms von der Festplatte eingegeben hat. Daher ist es sinnvoll, zu versuchen, es zu laden. Unsere Bibliothek lib.asm enthält eine Implementierung der nützlichen Routine lib_load_file, die die Dateisystemtabellen der FAT12-Festplatte analysiert: Sie benötigt einen Zeiger auf den Anfang einer Zeile mit dem Dateinamen über das AX-Register sowie einen Offset-Wert zum Laden Binärcode aus einer Programmdatei über das CX-Register. Wir verwenden das SI-Register bereits zum Speichern eines Zeigers auf die Zeichenfolge mit Benutzereingaben. Daher kopieren wir diesen Zeiger in das AX-Register und platzieren dann den Wert 32768, der als Offset zum Laden des Binärcodes aus der Programmdatei verwendet wird. in das CX-Register.

Aber warum verwenden wir diesen bestimmten Wert als Offset, um Binärcode aus einer Programmdatei zu laden? Nun, das ist nur eine der Speicherzuordnungsoptionen für unser Betriebssystem. Da wir in einem einzelnen 64-KB-Segment arbeiten und unsere Kernel-Binärdatei bei Offset 0 geladen wird, müssen wir die ersten 32 KB Speicher für Kerneldaten und die restlichen 32 KB für Ladeprogrammdaten verwenden. Somit liegt Offset 32768 in der Mitte unseres Segments und ermöglicht es uns, sowohl dem Betriebssystemkernel als auch den geladenen Programmen ausreichend RAM zur Verfügung zu stellen.

Die Routine lib_load_file führt dann einen sehr wichtigen Vorgang aus: Wenn sie eine Datei mit dem angegebenen Namen nicht auf der Festplatte finden oder sie aus irgendeinem Grund nicht von der Festplatte lesen kann, wird sie einfach beendet und ein spezielles Carry-Flag gesetzt. Dies ist ein CPU-Statusflag, der während der Ausführung einiger mathematischer Operationen gesetzt wird und uns im Moment nicht interessieren sollte, aber gleichzeitig können wir das Vorhandensein dieses Flags feststellen, um schnelle Entscheidungen zu treffen. Wenn die Routine lib_load_asm das Carry-Flag setzt, verwenden wir die jc-Anweisung (Jump if Carry), um zu einem Codeblock zu springen, der die Fehlermeldung ausgibt und zum Anfang der Benutzereingabeschleife zurückkehrt.

Im gleichen Fall können wir, wenn das Übertragungsflag nicht gesetzt ist, daraus schließen, dass die Unterroutine lib_load_asm den Binärcode erfolgreich aus der Programmdatei in den RAM an Adresse 32768 geladen hat. In diesem Fall müssen wir lediglich die Ausführung der Binärdatei initiieren Der Code wird an dieser Adresse geladen. Das heißt, starten Sie die Ausführung des vom Benutzer angegebenen Programms! Und nachdem die ret-Anweisung in diesem Programm verwendet wurde (um zum aufrufenden Code zurückzukehren), müssen wir einfach zur Schleife zurückkehren, um Benutzereingaben zu verarbeiten. So haben wir ein Betriebssystem erstellt: Es besteht aus einfachsten Befehlsanalyse- und Programmlademechanismen, implementiert in etwa 40 Zeilen Assembler-Code, allerdings mit viel Hilfe von Routinen aus der lib.asm-Bibliothek.

Um den Kernelcode des Betriebssystems zusammenzustellen, verwenden Sie den folgenden Befehl:

Nasm -f bin -o mykernel.bin mykernel.asm

Danach müssen wir die Datei mykernel.bin irgendwie zur Disketten-Image-Datei hinzufügen. Wenn Sie mit dem Trick des Mountens von Disk-Images mithilfe von Loopback-Geräten vertraut sind, können Sie mit floppy.img auf den Inhalt des Disk-Images zugreifen, aber es gibt einen einfacheren Weg mit GNU Mtools (www.gnu.org/software/mtools). Hierbei handelt es sich um eine Reihe von Programmen für die Arbeit mit Disketten, die MS-DOS/FAT12-Dateisysteme verwenden und in Paket-Repositorys verfügbar sind Software alles beliebt Linux-Distributionen Sie müssen also lediglich apt-get, yum, pacman oder ein anderes Dienstprogramm verwenden, mit dem Sie Softwarepakete auf Ihrer Distribution installieren.

Nach der Installation des entsprechenden Softwarepakets müssen Sie den folgenden Befehl ausführen, um die Datei mykernel.bin zur Disk-Image-Datei floppy.img hinzuzufügen:

Mcopy -i floppy.img mykernel.bin::/

Beachten Sie die lustigen Symbole am Ende des Befehls: Doppelpunkt, Doppelpunkt und Schrägstrich. Jetzt sind wir fast bereit, unser Betriebssystem zu starten, aber was nützt es, wenn es keine Apps dafür gibt? Lassen Sie uns dieses Missverständnis korrigieren, indem wir eine äußerst einfache Anwendung entwickeln. Ja, jetzt entwickeln Sie eine Anwendung für Ihr eigenes Betriebssystem – stellen Sie sich vor, wie stark Ihre Autorität unter den Geeks steigen wird. Speichern Sie den folgenden Code in einer Datei namens test.asm:

Org 32768 mov ah, 0Eh mov al, „X“ int 10h ret

Dieser Code verwendet einfach die BIOS-Funktion, um ein „X“ auf dem Bildschirm zu drucken, und gibt dann die Kontrolle an den Code zurück, der ihn aufgerufen hat – in unserem Fall ist dieser Code der Betriebssystemcode. Die Organisationszeile, mit der der Quellcode der Anwendung beginnt, ist kein CPU-Befehl, sondern eine NASM-Assembler-Anweisung, die ihr mitteilt, dass der Binärcode bei Offset 32768 in den RAM geladen wird und daher alle Offsets neu berechnet werden müssen, um dies zu berücksichtigen.

Dieser Code muss ebenfalls zusammengestellt werden und die resultierende Binärdatei muss der Disketten-Image-Datei hinzugefügt werden:

Nasm -f bin -o test.bin test.asm mcopy -i floppy.img test.bin::/

Atmen Sie nun tief durch, machen Sie sich bereit, über die beispiellosen Ergebnisse Ihrer eigenen Arbeit nachzudenken und starten Sie das Disketten-Image mit einem PC-Emulator wie Qemu oder VirtualBox. Hierzu kann beispielsweise der folgende Befehl verwendet werden:

Qemu-system-i386 -fda floppy.img

Voila: Der System-Bootloader boot.img, den wir in den ersten Sektor des Disk-Images integriert haben, lädt den Betriebssystemkernel mykernel.bin, der eine Willkommensnachricht anzeigt. Geben Sie den Befehl ls ein, um die Namen von zwei Dateien auf der Festplatte abzurufen (mykernel.bin und test.bin), und geben Sie dann den Namen der letzten Datei ein, um sie auszuführen und ein X auf dem Bildschirm anzuzeigen.

Es ist cool, nicht wahr? Jetzt können Sie mit der Finalisierung beginnen Befehlsshell Ihr Betriebssystem, fügen Sie Implementierungen neuer Befehle hinzu und fügen Sie auch zusätzliche Programmdateien zur Festplatte hinzu. Wenn Sie dieses Betriebssystem auf einem echten PC ausführen möchten, sollten Sie den Abschnitt „Ausführen des Bootloaders auf einer echten Hardwareplattform“ aus dem vorherigen Artikel der Serie lesen – Sie benötigen genau die gleichen Befehle. Nächsten Monat werden wir unser Betriebssystem leistungsfähiger machen, indem wir herunterladbaren Programmen die Nutzung von Systemfunktionen ermöglichen und ein Code-Sharing-Konzept einführen, um die Codeduplizierung zu reduzieren. Ein Großteil der Arbeit liegt noch vor uns.

lib.asm-Bibliotheksroutinen

Wie bereits erwähnt, bietet die Bibliothek lib.asm eine große Menge nützlicher Routinen zur Verwendung in Ihren Kerneln Betriebssysteme und individuelle Programme. Einige von ihnen verwenden Anweisungen und Konzepte, die in den Artikeln dieser Serie noch nicht behandelt wurden, andere (z. B. Festplattenroutinen) stehen in engem Zusammenhang mit dem Design von Dateisystemen, aber wenn Sie sich in diesen Angelegenheiten für kompetent halten, können Sie lesen Machen Sie sich mit ihren Implementierungen vertraut und verstehen Sie das Funktionsprinzip. Es ist jedoch wichtiger, herauszufinden, wie Sie sie aus Ihrem eigenen Code aufrufen können:

  • lib_print_string – akzeptiert einen Zeiger auf eine nullterminierte Zeichenfolge über das SI-Register und gibt die Zeichenfolge auf dem Bildschirm aus.
  • lib_input_string – akzeptiert einen Zeiger auf einen Puffer über das SI-Register und füllt diesen Puffer mit Zeichen, die der Benutzer über die Tastatur eingegeben hat. Nachdem der Benutzer die Eingabetaste gedrückt hat, wird die Zeile im Puffer mit Null beendet und die Steuerung kehrt zum aufrufenden Programmcode zurück.
  • lib_move_cursor – bewegt den Cursor auf dem Bildschirm an eine Position mit Koordinaten, die über die Register DH (Zeilennummer) und DL (Spaltennummer) übertragen werden.
  • lib_get_cursor_pos – Diese Unterroutine sollte aufgerufen werden, um die aktuellen Zeilen- und Spaltennummern mithilfe der DH- bzw. DL-Register zu erhalten.
  • lib_string_uppercase – nimmt mithilfe des AX-Registers einen Zeiger auf den Anfang einer nullterminierten Zeichenfolge und wandelt die Zeichen der Zeichenfolge in Großbuchstaben um.
  • lib_string_length – nimmt über das AX-Register einen Zeiger auf den Anfang eines nullterminierten Strings und gibt seine Länge über das AX-Register zurück.
  • lib_string_compare – akzeptiert Zeiger auf den Anfang von zwei nullterminierten Strings mithilfe der SI- und DI-Register und vergleicht diese Strings. Setzt das Carry-Flag, wenn die Zeilen identisch sind (um einen Sprungbefehl abhängig vom jc-Carry-Flag zu verwenden) oder löscht dieses Flag, wenn die Zeilen unterschiedlich sind (um den jnc-Befehl zu verwenden).
  • lib_get_file_list – Nimmt über das SI-Register einen Zeiger auf den Anfang eines Puffers und fügt in diesen Puffer eine nullterminierte Zeichenfolge ein, die eine durch Kommas getrennte Liste von Dateinamen von der Festplatte enthält.
  • lib_load_file – nimmt mithilfe des AX-Registers einen Zeiger auf den Anfang einer Zeile, die den Dateinamen enthält, und lädt den Inhalt der Datei an dem durch das CX-Register übergebenen Offset. Gibt mithilfe des BX-Registers die Anzahl der in den Speicher kopierten Bytes (d. h. die Dateigröße) zurück oder setzt das Carry-Flag, wenn eine Datei mit dem angegebenen Namen nicht gefunden wird.

Ich sage gleich, schließen Sie den Artikel nicht mit den Gedanken „Verdammt, noch ein Popov.“ Er hat nur ein ausgefeiltes Ubuntu, während ich alles von Grund auf neu habe, einschließlich des Kernels und der Anwendungen. Also Fortsetzung unterm Strich.

Betriebssystemgruppe: Hier.
Zuerst gebe ich Ihnen einen Screenshot.

Es gibt keine mehr davon, und jetzt wollen wir ausführlicher darüber sprechen, warum ich es schreibe.

Es war ein warmer Aprilabend, Donnerstag. Seit meiner Kindheit träumte ich davon, ein Betriebssystem zu schreiben, als ich plötzlich dachte: „Jetzt kenne ich die Vorteile und asm, warum nicht meinen Traum wahr werden lassen?“ Ich habe Websites zu diesem Thema gegoogelt und einen Artikel von Habr gefunden: „Wie man mit dem Schreiben eines Betriebssystems beginnt und es nicht aufgibt.“ Vielen Dank an den Autor für den Link zum OSDev-Wiki unten. Ich ging dorthin und begann zu arbeiten. Alle Daten zum Minimalbetriebssystem waren in einem Artikel zusammengefasst. Ich habe angefangen, Cross-GCC und Binutils zu erstellen, und habe dann von dort aus alles neu geschrieben. Du hättest meine Freude sehen sollen, als ich die Aufschrift „Hallo, Kernel-Welt!“ sah. Ich sprang sofort von meinem Stuhl auf und erkannte, dass ich nicht aufgeben würde. Ich habe „Konsole“ geschrieben (in Anführungszeichen; ich hatte keinen Zugriff auf eine Tastatur), entschied mich dann aber, ein Fenstersystem zu schreiben. Am Ende hat es funktioniert, aber ich hatte keinen Zugriff auf die Tastatur. Und dann beschloss ich, mir einen Namen auszudenken, der auf dem X-Window-System basiert. Ich habe Y Window System gegoogelt – es existiert. Aus diesem Grund habe ich Z Window System 0.1 genannt, das in OS365 Pre-Alpha 0.1 enthalten ist. Und ja, niemand außer mir hat sie gesehen. Dann habe ich herausgefunden, wie ich die Tastaturunterstützung implementieren kann. Screenshot der allerersten Version, als es noch nichts gab, nicht einmal ein Fenstersystem:

Wie Sie sehen, hat sich der Textcursor nicht einmal bewegt. Dann habe ich ein paar geschrieben einfache Anwendungen basierend auf Z. Und hier ist die Version 1.0.0 Alpha. Es gab dort viele Dinge, sogar Systemmenüs. A Dateimanager und der Rechner funktionierte einfach nicht.

Ich wurde direkt von einem Freund terrorisiert, der sich nur um Schönheit kümmert (Mitrofan, sorry). Er sagte: „Waschen Sie den VBE-Modus 1024*768*32 herunter, waschen Sie ihn ab, waschen Sie ihn ab!“ Nun, lasst es uns austrinken!“ Nun ja, ich war es schon leid, ihm zuzuhören und habe ihn trotzdem niedergemacht. Über die Implementierung weiter unten.

Ich habe alles mit meinem Bootloader, nämlich GRUB, gemacht. Mit dessen Hilfe kann man den Grafikmodus unkompliziert einstellen, indem man ein paar magische Zeilen in den Multiboot-Header einfügt.

Stellen Sie ALIGN, 1 ein<<0 .set MEMINFO, 1<<1 .set GRAPH, 1<<2 .set FLAGS, ALIGN | MEMINFO | GRAPH .set MAGIC, 0x1BADB002 .set CHECKSUM, -(MAGIC + FLAGS) .align 4 .long MAGIC .long FLAGS .long CHECKSUM .long 0, 0, 0, 0, 0 .long 0 # 0 = set graphics mode .long 1024, 768, 32 # Width, height, depth
Und dann nehme ich aus der Multiboot-Informationsstruktur die Framebuffer-Adresse und die Bildschirmauflösung und schreibe dort Pixel. VESA hat alles sehr verwirrend gemacht – RGB-Farben müssen in umgekehrter Reihenfolge eingegeben werden (nicht R G B, sondern B G R). Mehrere Tage lang habe ich nicht verstanden, warum die Pixel nicht angezeigt wurden!? Am Ende wurde mir klar, dass ich vergessen hatte, die Werte von 16 Farbkonstanten von 0...15 auf ihre RGB-Äquivalente zu ändern. Infolgedessen habe ich es freigegeben und gleichzeitig den Hintergrund mit Farbverlauf reduziert. Dann habe ich eine Konsole und zwei Anwendungen erstellt und 1.2 veröffentlicht. Oh ja, ich hätte es fast vergessen – Sie können das Betriebssystem unter herunterladen

Monteur

Monteur(aus dem Englischen montieren – zusammenbauen) – ein Compiler von Assembler in Maschinensprachebefehle.
Für jede Prozessorarchitektur und für jedes Betriebssystem oder jede Betriebssystemfamilie gibt es einen Assembler. Es gibt auch sogenannte „Cross-Assembler“, mit denen Sie Programme für eine andere Zielarchitektur oder ein anderes Betriebssystem auf Maschinen mit einer Architektur (oder in der Umgebung eines Betriebssystems) zusammenstellen und ausführbaren Code in einem für die Ausführung geeigneten Format erhalten der Zielarchitektur oder im Zielumgebungs-Betriebssystem.

x86-Architektur

Assembler für DOS

Die bekanntesten Assembler für das DOS-Betriebssystem waren Borland Turbo Assembler (TASM) und Microsoft Macro Assembler (MASM). Auch der einfache Assembler A86 war einst beliebt.
Anfangs unterstützten sie nur 16-Bit-Anweisungen (bis zur Einführung des Intel 80386-Prozessors). Spätere Versionen von TASM und MASM unterstützen sowohl 32-Bit-Anweisungen als auch alle in moderneren Prozessoren eingeführten Anweisungen und architekturspezifische Befehlssysteme (wie beispielsweise MMX, SSE, 3DNow! usw.).

Microsoft Windows

Mit dem Aufkommen des Microsoft Windows-Betriebssystems erschien eine TASM-Erweiterung namens TASM32, die es ermöglichte, Programme für die Ausführung in der Windows-Umgebung zu erstellen. Die neueste bekannte Version von Tasm ist 5.3, die MMX-Anweisungen unterstützt und derzeit im Turbo C++ Explorer enthalten ist. Doch offiziell wurde die Entwicklung des Programms komplett eingestellt.
Microsoft unterhält ein Produkt namens Microsoft Macro Assembler. Es entwickelt sich bis heute weiter, wobei die neuesten Versionen in den DDKs enthalten sind. Die Version des Programms zur Erstellung von Programmen für DOS wird jedoch nicht entwickelt. Darüber hinaus hat Stephen Hutchesson ein MASM-Programmierpaket namens „MASM32“ erstellt.

GNU und GNU/Linux

Das GNU-Betriebssystem umfasst den gcc-Compiler, der den Gas-Assembler (GNU Assembler) enthält, der im Gegensatz zu den meisten anderen gängigen Assemblern, die die Intel-Syntax verwenden, die AT&T-Syntax verwendet.

Tragbare Monteure

Es gibt auch ein Open-Source-Assembler-Projekt, das für verschiedene Betriebssysteme verfügbar ist und über das Sie Objektdateien für diese Systeme erhalten können. Dieser Assembler heißt NASM (Netwide Assembler).
YASM ist eine neu geschriebene Version von NASM unter der BSD-Lizenz (mit einigen Ausnahmen).
FASM (Flat Assembler) ist ein junger Assembler unter einer BSD-Lizenz, die so geändert wurde, dass eine erneute Lizenzierung verboten ist (einschließlich unter der GNU GPL). Es gibt Versionen für KolibriOS, GNU/Linux, MS-DOS und Microsoft Windows, verwendet Intel-Syntax und unterstützt AMD64-Anweisungen.

RISC-Architekturen


MCS-51
AVR
Derzeit gibt es zwei Compiler von Atmel (AVRStudio 3 und AVRStudio4). Die zweite Version ist ein Versuch, die nicht sehr erfolgreiche erste zu korrigieren. Der Assembler ist auch in WinAVR enthalten.
ARM
AVR32
MSP430
PowerPC

Zusammenbauen und Kompilieren

Der Prozess der Übersetzung eines Programms in Assemblersprache in Objektcode wird üblicherweise als Assembler bezeichnet. Im Gegensatz zum Kompilieren ist das Zusammenfügen ein mehr oder weniger eindeutiger und umkehrbarer Prozess. In der Assemblersprache entspricht jede Mnemonik einer Maschinenanweisung, während in höheren Programmiersprachen jeder Ausdruck eine große Anzahl unterschiedlicher Anweisungen verbergen kann. Im Prinzip ist diese Einteilung recht willkürlich, daher wird die Übersetzung von Assemblerprogrammen manchmal auch als Kompilierung bezeichnet.

Assemblersprache

Assemblersprache- eine Art Low-Level-Programmiersprache, bei der es sich um ein Format zur Aufzeichnung von Maschinenbefehlen handelt, das für die menschliche Wahrnehmung geeignet ist. Der Kürze halber wird es oft einfach Assembler genannt, was nicht stimmt.

Assemblerbefehle entsprechen eins zu eins den Prozessorbefehlen und stellen tatsächlich eine praktische symbolische Form der Aufzeichnung (mnemonischer Code) von Befehlen und ihren Argumenten dar. Die Assemblersprache bietet auch grundlegende Softwareabstraktionen: die Verknüpfung von Programmteilen und Daten durch Labels mit symbolischen Namen (bei der Assemblierung wird für jedes Label eine Adresse berechnet, wonach jedes Vorkommen des Labels durch diese Adresse ersetzt wird) und Anweisungen.
Mit Assembly-Anweisungen können Sie Datenblöcke (explizit beschrieben oder aus einer Datei gelesen) in ein Programm einbinden. ein bestimmtes Fragment eine bestimmte Anzahl von Malen wiederholen; Kompilieren Sie das Fragment entsprechend der Bedingung. Legen Sie die Fragmentausführungsadresse anders als die Speicherortadresse fest[angeben!]; Etikettenwerte während der Kompilierung ändern; Verwenden Sie Makrodefinitionen mit Parametern usw.
Jedes Prozessormodell verfügt im Prinzip über einen eigenen Befehlssatz und eine entsprechende Assemblersprache (oder Dialekt).

Vorteile und Nachteile

Vorteile der Assemblersprache

Minimaler redundanter Code, d. h. die Verwendung von weniger Anweisungen und Speicherzugriffen, ermöglicht eine höhere Geschwindigkeit und eine geringere Programmgröße.
Gewährleistung der vollständigen Kompatibilität und maximalen Nutzung der Funktionen der gewünschten Plattform: Verwendung spezieller Anweisungen und technischer Funktionen dieser Plattform.
Beim Programmieren in Assembler stehen besondere Fähigkeiten zur Verfügung: direkter Zugriff auf Hardware, Ein-/Ausgabe-Ports und spezielle Prozessorregister sowie die Fähigkeit, selbstmodifizierenden Code zu schreiben (d. h. Metaprogrammierung, ohne dass ein Software-Interpreter erforderlich ist). .
Die neuesten Sicherheitstechnologien, die in Betriebssystemen eingeführt wurden, erlauben die Erstellung von sich selbst modifizierendem Code nicht, da sie die gleichzeitige Möglichkeit der Ausführung von Anweisungen und des Schreibens in denselben Speicherbereich ausschließen (W^X-Technologie in BSD-Systemen, DEP in Windows).

Nachteile der Assemblersprache

Große Codemengen und eine große Anzahl zusätzlicher kleiner Aufgaben, was dazu führt, dass der Code sehr schwer zu lesen und zu verstehen ist und daher das Debuggen und Ändern des Programms sowie die Implementierung von Programmierparadigmen schwieriger wird und alle anderen Konventionen. was zur Komplexität der gemeinsamen Entwicklung führt.
Geringere Anzahl verfügbarer Bibliotheken, geringe Kompatibilität untereinander.
Nicht auf andere Plattformen portierbar (außer binärkompatible Plattformen).

Anwendung

Daraus ergeben sich direkt die Vor- und Nachteile.
Da große Programme in Assemblersprache äußerst umständlich zu schreiben sind, werden sie in Hochsprachen geschrieben. Im Assembler werden kleine Fragmente oder Module geschrieben, bei denen es auf Folgendes ankommt:
Leistung (Fahrer);
Codegröße (Bootsektoren, Software für Mikrocontroller und Prozessoren mit begrenzten Ressourcen, Viren, Softwareschutz);
besondere Fähigkeiten: direktes Arbeiten mit Hardware oder Maschinencode, also Betriebssystem-Loadern, Treibern, Viren, Sicherheitssystemen.

Assemblercode mit anderen Sprachen verknüpfen

Da meist nur Fragmente eines Programms in Assembler geschrieben sind, müssen diese mit anderen Teilen in anderen Sprachen verknüpft werden. Dies wird im Wesentlichen auf zwei Arten erreicht:
In der Kompilierungsphase— Einfügen von Assembler-Fragmenten (Inline-Assembler) in ein Programm mithilfe spezieller Sprachanweisungen, einschließlich Schreibprozeduren in Assemblersprache. Die Methode ist praktisch für einfache Datentransformationen, aber vollwertiger Assemblercode mit Daten und Unterprogrammen, einschließlich Unterprogrammen mit vielen Ein- und Ausgaben, die von Hochsprachen nicht unterstützt werden, kann damit nicht erstellt werden.
In der Layoutphase oder separate Zusammenstellung. Damit die zusammengestellten Module interagieren können, reicht es aus, dass die Verbindungsfunktionen die erforderlichen Aufrufkonventionen und Datentypen unterstützen. Einzelne Module können in jeder Sprache geschrieben werden, einschließlich Assemblersprache.

Syntax

Für die Syntax von Assemblersprachen gibt es keinen allgemein anerkannten Standard. Es gibt jedoch Standards, an die sich die meisten Assembler-Entwickler halten. Die wichtigsten dieser Standards sind die Intel-Syntax und die AT&T-Syntax.

Anweisungen

Das allgemeine Format zum Aufzeichnen von Anweisungen ist für beide Standards gleich:

[label:] opcode [operands] [;comment]

Dabei ist Opcode die direkte Mnemonik von Anweisungen an den Prozessor. Es können Präfixe hinzugefügt werden (Wiederholungen, Änderungen in der Adressierungsart usw.).
Die Operanden können Konstanten, Registernamen, Adressen im RAM usw. sein. Die Unterschiede zwischen den Intel- und AT&T-Standards beziehen sich hauptsächlich auf die Reihenfolge, in der die Operanden aufgelistet sind, und ihre Syntax für verschiedene Adressierungsmethoden.
Die verwendeten Mnemoniken sind normalerweise für alle Prozessoren derselben Architektur oder Architekturfamilie gleich (zu den weithin bekannten Mnemoniken gehören Motorola-, ARM- und x86-Prozessoren und -Controller). Sie sind in den Prozessorspezifikationen beschrieben. Mögliche Ausnahmen:
Wenn der Assembler plattformübergreifende AT&T-Syntax verwendet (ursprüngliche Mnemoniken werden in AT&T-Syntax konvertiert)
Wenn es zunächst zwei Standards für die Aufzeichnung von Mnemoniken gab (das Befehlssystem wurde von einem Prozessor eines anderen Herstellers geerbt).
Beispielsweise hat der Zilog Z80-Prozessor das Intel i8080-Befehlssystem geerbt, es erweitert und die Mnemonik (und Registerbezeichnungen) auf seine eigene Weise geändert. Ich habe zum Beispiel Intels mov in ld geändert. Motorola Fireball-Prozessoren übernahmen das Z80-Befehlssystem und reduzierten es etwas. Gleichzeitig ist Motorola offiziell zur Intel-Mnemonik zurückgekehrt. Und derzeit arbeitet die Hälfte der Assembler für Fireball mit Intel-Mnemoniken und die andere Hälfte mit Zilog-Mnemoniken.

Richtlinien

Zusätzlich zu Anweisungen kann ein Programm Anweisungen enthalten: Befehle, die nicht direkt in Maschinenanweisungen übersetzt werden, sondern den Betrieb des Compilers steuern. Ihr Satz und ihre Syntax variieren erheblich und hängen nicht von der Hardwareplattform, sondern vom verwendeten Compiler ab (der Dialekte von Sprachen innerhalb derselben Architekturfamilie generiert). Als „Gentleman-Richtlinien“ können wir Folgendes hervorheben:
Definition von Daten (Konstanten und Variablen)
Verwaltung der Programmorganisation im Speicher und Ausgabedateiparameter
Einstellen des Compiler-Betriebsmodus
alle Arten von Abstraktionen (d. h. Elemente von Hochsprachen) – vom Entwurf von Prozeduren und Funktionen (zur Vereinfachung der Implementierung des prozeduralen Programmierparadigmas) bis hin zu bedingten Konstrukten und Schleifen (für das strukturierte Programmierparadigma)
Makros

Beispielprogramm

Ein Beispiel für ein Hello-World-Programm für MS-DOS für x86-Architektur im TASM-Dialekt:

.MODEL TINY CODE SEGMENT ASSUME CS:CODE, DS:CODE ORG 100h START: mov ah,9 mov dx,OFFSET Msg int 21h int 20h Msg DB "Hello World",13,10,"$" CODE ENDS END START

Ursprünge und Kritik des Begriffs „Assemblersprache“

Dieser Sprachtyp hat seinen Namen vom Namen des Übersetzers (Compilers) dieser Sprachen – Assembler (englischer Assembler). Der Name Letzteres ist auf die Tatsache zurückzuführen, dass es auf den ersten Computern keine höheren Sprachen gab und die einzige Alternative zur Programmerstellung mit Assembler die Programmierung direkt in Codes war.
Die Assemblersprache wird im Russischen oft „Assembler“ (und etwas Ähnliches – „Assembler“) genannt, was laut der englischen Übersetzung des Wortes falsch ist, aber in die Regeln der russischen Sprache passt. Allerdings wird der Assembler selbst (das Programm) auch einfach „Assembler“ und nicht „Assembler-Compiler“ usw. genannt.
Die Verwendung des Begriffs „Assemblersprache“ kann auch zu der falschen Vorstellung führen, dass es eine einzige Low-Level-Sprache oder zumindest einen Standard für solche Sprachen gibt. Bei der Benennung der Sprache, in der ein bestimmtes Programm geschrieben ist, empfiehlt es sich zu klären, für welche Architektur es gedacht ist und in welchem ​​Dialekt der Sprache es geschrieben ist.

Heute gibt es in unserem Kuriositätenkabinett ein merkwürdiges Beispiel – ein in reinem Assembler geschriebenes Betriebssystem. Zusammen mit Treibern, grafischer Shell, Dutzenden vorinstallierten Programmen und Spielen nimmt es weniger als eineinhalb Megabyte ein. Lernen Sie das außergewöhnlich schnelle und überwiegend russische Betriebssystem „Hummingbird“ kennen.

Die Entwicklung von „Hummingbird“ ging bis 2009 recht zügig voran. Der Vogel lernte das Fliegen auf anderer Hardware und benötigte mindestens den ersten Pentium und acht Megabyte RAM. Die Mindestsystemanforderungen für Hummingbird sind:

  • CPU: Pentium, AMD 5x86 oder Cyrix 5x86 ohne MMX mit einer Frequenz von 100 MHz;
  • RAM: 8 MB;
  • Grafikkarte: VESA-kompatibel mit Unterstützung für VGA-Modus (640 × 480 × 16).

Modern „Hummingbird“ ist ein regelmäßig aktualisierter „Nightly Build“ der neuesten offiziellen Version, die Ende 2009 veröffentlicht wurde. Wir haben Build 0.7.7.0+ vom 20. August 2017 getestet.

WARNUNG

In den Standardeinstellungen hat KolibriOS keinen Zugriff auf Festplatten, die über das BIOS sichtbar sind. Überlegen Sie sorgfältig und erstellen Sie ein Backup, bevor Sie diese Einstellung ändern.

Die Änderungen bei den nächtlichen Builds sind zwar gering, haben sich aber im Laufe der Jahre ziemlich stark angesammelt. Der aktualisierte „Hummingbird“ kann auf FAT16–32 / ext2 – ext4-Partitionen schreiben und unterstützt andere gängige Dateisysteme (NTFS, XFS, ISO-9660) im Lesemodus. Es fügte Unterstützung für USB- und Netzwerkkarten hinzu und fügte einen TCP/IP-Stack und Audio-Codecs hinzu. Generell kann man darin schon einiges machen, und nicht nur einmal ein ultraleichtes Betriebssystem mit GUI anschauen und sich von der Startgeschwindigkeit beeindrucken lassen.



Wie frühere Versionen ist auch der neueste „Hummingbird“ in Flat Assembler (FASM) geschrieben und belegt eine Diskette – 1,44 MB. Dadurch kann es vollständig in einem speziellen Speicher abgelegt werden. Beispielsweise haben Handwerker KolibriOS direkt in das Flash-BIOS geschrieben. Während des Betriebs kann es bei manchen Prozessoren vollständig im Cache liegen. Stellen Sie sich vor: Das gesamte Betriebssystem samt Programmen und Treibern wird zwischengespeichert!

DIE INFO

Wenn Sie die Website kolibrios.org besuchen, warnt Sie der Browser möglicherweise vor der Gefahr. Der Grund sind offenbar die Assembler-Programme in der Distribution. VirusTotal definiert die Website nun als völlig sicher.

„Hummingbird“ kann einfach von einer Diskette, Festplatte, einem Flash-Laufwerk, einer Live-CD oder in einer virtuellen Maschine geladen werden. Zum Emulieren geben Sie einfach den Betriebssystemtyp „Andere“ an und weisen ihm einen Prozessorkern und etwas RAM zu. Es ist nicht notwendig, das Laufwerk anzuschließen, und wenn Sie einen Router mit DHCP haben, stellt „Hummingbird“ sofort eine Verbindung zum Internet und zum lokalen Netzwerk her. Unmittelbar nach dem Download wird Ihnen eine entsprechende Benachrichtigung angezeigt.


Ein Problem besteht darin, dass das HTTPS-Protokoll vom in Kolibri integrierten Browser nicht unterstützt wird. Daher war es nicht möglich, die darin enthaltene Website anzusehen, genauso wie das Öffnen der Seiten von Google, Yandex, Wikipedia, Sberbank... tatsächlich keine übliche Adresse. Jeder ist längst auf ein sicheres Protokoll umgestiegen. Die einzige Website mit reinem HTTP der alten Schule, die ich gefunden habe, war das „Russische Regierungsportal“, aber auch in einem Textbrowser sah es nicht besonders gut aus.



Die Darstellungseinstellungen in Hummingbird haben sich im Laufe der Jahre verbessert, sind aber immer noch alles andere als ideal. Eine Liste der unterstützten Videomodi wird auf dem Hummingbird-Ladebildschirm angezeigt, wenn Sie die a-Taste drücken.



Die Liste der verfügbaren Optionen ist klein und die erforderliche Auflösung ist möglicherweise nicht vorhanden. Wenn Sie eine Grafikkarte mit einer AMD (ATI) GPU haben, können Sie sofort benutzerdefinierte Einstellungen hinzufügen. Dazu müssen Sie den Parameter -m an den ATIKMS-Loader übergeben X X , Zum Beispiel:

/RD/1/DRIVERS/ATIKMS -m1280x800x60 -1

Hier ist /RD/1/DRIVERS/ATIKMS der Pfad zum Bootloader (RD – RAM Disk).

Bei laufendem System kann der ausgewählte Videomodus mit dem vmode-Befehl angezeigt und (theoretisch) manuell umgeschaltet werden. Wenn „Hummingbird“ in einer virtuellen Maschine läuft, bleibt dieses Fenster leer, aber mit einem sauberen Neustart können Intel-Grafiktreiber von i915 bis einschließlich Skylake hinzugefügt werden.

Überraschenderweise kann KolibriOS eine Menge Spiele unterstützen. Darunter sind Logik- und Arcade-Spiele, Tag, Snake, Tanks (nein, nicht WoT) – ein ganzes „Game Center“! Sogar Doom und Quake wurden auf Kolibri portiert.



Eine weitere wichtige Sache war der FB2READ-Reader. Es funktioniert korrekt mit Kyrillisch und verfügt über Einstellungen für die Textanzeige.



Ich empfehle, alle Benutzerdateien auf einem Flash-Laufwerk zu speichern, es muss jedoch über einen USB 2.0-Anschluss angeschlossen sein. Unser USB 3.0-Stick (an einem USB 2.0-Anschluss) mit einer Kapazität von 16 GB und dem NTFS-Dateisystem wurde sofort identifiziert. Wenn Sie Dateien schreiben müssen, sollten Sie ein Flash-Laufwerk mit einer FAT32-Partition anschließen.



Das Kolibri-Distributionskit umfasst drei Dateimanager, Dienstprogramme zum Anzeigen von Bildern und Dokumenten, Audio- und Videoplayer und andere Benutzeranwendungen. Der Schwerpunkt liegt jedoch auf der Assemblersprachenentwicklung.



Der integrierte Texteditor verfügt über ASM-Syntaxhervorhebung und ermöglicht sogar den sofortigen Start eingegebener Programme.



Zu den Entwicklungstools gehören der Oberon-07/11-Compiler für i386 Windows, Linux und KolibriOS sowie Low-Level-Emulatoren: E80 – ZX Spectrum-Emulator, FCE Ultra – einer der besten NES-Emulatoren, DOSBox v.0.74 und Andere. Alle wurden speziell auf Kolibri portiert.

Wenn Sie KolibriOS für ein paar Minuten verlassen, startet der Bildschirmschoner. Auf dem Bildschirm erscheinen Codezeilen, in denen Sie einen Verweis auf MenuetOS sehen können.

Die Fortsetzung steht nur Mitgliedern zur Verfügung

Option 1: Treten Sie der „Site“-Community bei, um alle Materialien auf der Site zu lesen

Durch die Mitgliedschaft in der Community innerhalb des angegebenen Zeitraums erhalten Sie Zugriff auf ALLE Hacker-Materialien, erhöhen Ihren persönlichen kumulativen Rabatt und können eine professionelle Xakep-Score-Bewertung erwerben!


Spitze