====== Lyx OS – Anwendungen entwickeln ====== Diese Seite richtet sich an Entwickler, die Programme für Lyx OS schreiben möchten. Voraussetzung: Grundkenntnisse der [[lyx_-_programmiersprache:start|Lyx-Sprache]] und ein laufendes LyxOS-System (→ [[lyxos:erste-schritte|Erste Schritte]]). → [[lyxos:syscalls|Syscall-Referenz]] · [[lyxos:architektur|Architektur]] · [[lyxos:start|Übersicht]] ---- ===== 1. Zielplattform: --target=lyxos ===== Jedes Lyx-Programm für LyxOS wird mit dem Flag ''--target=lyxos'' kompiliert. Dieses Target unterscheidet sich wesentlich vom Standard-Linux-Target: ^ Eigenschaft ^ Linux-Target (default) ^ LyxOS-Target (''--target=lyxos'') ^ | Laufzeitbibliothek | libc (glibc / musl) | ''lyxrt_lyxos.lyu'' (keine libc) | | Ausgabe ''PrintLn(s)'' | ''write(1, ...)'' via libc | ''sys_write(FD_STDOUT, ...)'' direkt | | Speicher ''mmap(...)'' | ''mmap()'' via libc | ''sys_mmap(...)'' direkt | | Binärformat | ELF64 (Linux ABI) | nativer ''LYX!''-Container (Magic ''4C 59 58 21''), vom LX-34-Loader geladen; ELF wird nur noch mitgeladen | | Einstiegspunkt | ''main()'' via libc-Initialisierung | ''main()'' direkt (kein CRT0) | | Syscall-Nummern | Linux-Tabelle | LyxOS-Tabelle (''doku/syscalls.md'') | # Kompilieren für LyxOS lyxc --target=lyxos meinprogramm.lyx -o meinprogramm.elf Das Resultat ist ein ''LYX!''-Container ohne externe Shared-Library-Abhängigkeiten — nachgemessen mit ''lyxc 1.1.11B''. Die Dateiendung ''.elf'' in den Beispielen ist Gewohnheit, kein Formathinweis. ---- ===== 1b. Was auf diesem Ziel trägt — und was nicht ===== Bis in den August 2026 war ''--target=lyxos'' ein Ziel, das vieles **still** falsch machte: Programme übersetzten, liefen an und rechneten daneben. Diese Serie ist mit ''lyxc 1.1.8A''–''1.1.11A'' abgearbeitet. Der Stand, nachgemessen mit **1.1.11B**: ^ Was ^ Stand ^ Vorher ^ | ''if''/''else if''-Ketten | trägt (1.1.8C) | **jede** Kette wurde übersprungen, auch der zutreffende Zweig ([[https://github.com/SEOLizer/LyX-Compiler/issues/1757|#1757]]) | | Funktionsargumente ab dem 7. | trägt (1.1.8G) | still verworfen, die Funktion rechnete mit dem 6. weiter ([[https://github.com/SEOLizer/LyX-Compiler/issues/1761|#1761]]) | | ''con''-Konstanten | trägt (1.1.8A) | jede Modulkonstante war 0 ([[https://github.com/SEOLizer/LyX-Compiler/issues/1751|#1751]]); negative Werte ebenso ([[https://github.com/SEOLizer/LyX-Compiler/issues/1798|#1798]]) | | ''new'' und Interface-Aufrufe | trägt (1.1.9G) | Seitenfehler mit ''CR2=0x46''; Interface-Aufruf kehrte nicht zurück ([[https://github.com/SEOLizer/LyX-Compiler/issues/1767|#1767]], [[https://github.com/SEOLizer/LyX-Compiler/issues/1779|#1779]], [[https://github.com/SEOLizer/LyX-Compiler/issues/1787|#1787]]) | | ''peek64(param + 0)'' | trägt | lieferte 0, wenn der Versatz ein literales Null auf einem Parameter war ([[https://github.com/SEOLizer/LyX-Compiler/issues/1784|#1784]]) | | ''GetArgC()'' / ''argv'' | trägt | ''_start'' ignorierte argc/argv, ''GetArgC()'' war immer 0 ([[https://github.com/SEOLizer/LyX-Compiler/issues/1754|#1754]]) | | Feldzugriff über Modulvariable | trägt | ''g_obj.feld'' lieferte Müll ([[https://github.com/SEOLizer/LyX-Compiler/issues/1812|#1812]]) | | ''alloc''/''free'', ''std.io'', ''std.fs'' | trägt (1.1.4D) | fehlende Builtins ([[https://github.com/SEOLizer/LyX-Compiler/issues/1715|#1715]], [[https://github.com/SEOLizer/LyX-Compiler/issues/1717|#1717]], [[https://github.com/SEOLizer/LyX-Compiler/issues/1718|#1718]]) | | ''Print''/''PrintLn'' mit Variable | trägt (1.1.4D) | nur Zeichenketten-Literale ([[https://github.com/SEOLizer/LyX-Compiler/issues/1716|#1716]]) | > **''match'' wählt auf diesem Ziel keinen Zweig** — auch nicht den Auffangfall ''case _''. Das Programm übersetzt ohne Meldung, läuft durch und gibt aus dem ''match'' **nichts** aus ([[https://github.com/SEOLizer/LyX-Compiler/issues/1825|#1825]], offen; Ausgangsbefund im geschlossenen [[https://github.com/SEOLizer/LyX-Compiler/issues/1757|#1757]]). > > Bis das zu ist: ''if''/''else if''-Ketten benutzen. Die tragen seit 1.1.8C. ==== Netzwerk: nur die Client-Seite ==== LyxOS kennt kein Server-Socket-Modell. Die Kernel-Aufrufe haben eine andere **Gestalt** als BSD-Sockets, nicht bloß andere Nummern: ^ LyxOS ^ Bedeutung ^ | ''sys_tcp_connect(ip, port)'' | liefert den Sockel **selbst** — kein ''socket()'' davor | | ''sys_tcp_send'' / ''sys_tcp_recv'' | flacher Puffer, kein ''msghdr'' | | ''bind'' / ''listen'' / ''accept'' | **kein Gegenstück** | Angebunden ist das als eigene Builtins in der Gestalt des Kernels (IDs 414–418, [[https://github.com/SEOLizer/LyX-Compiler/issues/1734|#1734]]), nicht als BSD-Nachbau: ein erfundenes ''socket()'', das ohne den Kernel einen Deskriptor zurückgibt, sähe richtig aus und träfe nichts. Die zehn BSD-Namen melden sich beim Übersetzen und nennen den Weg, den es gibt. Aus demselben Grund sind die **Notification-Queues nicht angebunden** ([[https://github.com/SEOLizer/LyX-Compiler/issues/1810|#1810]]): unsere Form kennt Flags, Nutzlast und Frist, der Kernel keines davon. Eine 1:1-Zuordnung hätte drei Angaben stillschweigend verworfen und Ereignisse **ohne Inhalt** zugestellt. Stattdessen kommt eine Meldung, die den Grund nennt — dass es die Nummern gibt, heißt nicht, dass sie sich anbinden lassen. ---- ===== 2. Basis-Programm ===== Das einfachste lauffähige LyxOS-Programm: fn main(): int64 { PrintLn("Lyx OS — Ring-3 aktiv!"); return 0; } ''PrintLn'' wird vom Compiler auf ''sys_write(FD_STDOUT, ...)'' abgebildet. ''return 0'' ruft ''sys_exit(0)'' auf. Kein ''import'' nötig. ---- ===== 3. Ausgabe und Eingabe ===== ==== Ausgabe ==== // Einzeiliger String (mit Newline) PrintLn("Hello LyxOS"); // Ohne Newline Print("Zähler: "); PrintLn(IntToStr(42)); // Fehlerausgabe (FD_STDERR) EPrintLn("Fehler: Ressource nicht verfügbar"); // Formatierte Ausgabe import std.io; PrintfSI("User %s hat ID %d", "admin", 1001); ==== Tastatureingabe (ab M5) ==== // Zeichen einlesen via sys_read(FD_STDIN, buf, 1) var buf: [4]uint8 := []; var n: int64 := 0; // (n, err) := syscall(SYS_READ, FD_STDIN, ^buf[0] as int64, 1); ---- ===== 4. Dateisystem ===== Die FAT32-Partition wird als ''/ '' gemountet. Alle Pfad-Syscalls nehmen einen ''dir_fd'' als erstes Argument. ''AT_CWD = -1'' verweist auf das aktuelle Arbeitsverzeichnis. // Datei öffnen (sys_open, Gruppe 0x0200) // var fd: int64 := 0; // var err: int64 := 0; // (err, fd) := syscall(SYS_OPEN, AT_CWD, "/data/config.txt", O_RDONLY, 0); // Konventionell über Lyx-Builtins (werden intern gemappt): import std.fs; var fd: int64 := open("/data/config.txt", O_RDONLY, 0); if (fd < 0) { EPrintLn("Datei nicht gefunden"); return 1; } var buf: [512]uint8 := []; var n: int64 := read(fd, ^buf[0] as pchar, 512); close(fd); > **Pfad-Konventionen:** > LyxOS verwendet Forward-Slashes. Die Wurzel des FAT32-Volumes ist ''/'' im VFS. Verzeichnisnamen unterscheiden keine Groß-/Kleinschreibung (FAT32-Einschränkung). ---- ===== 5. Speicherverwaltung ===== Ring-3 erhält Speicher über ''sys_mmap''. Das Lyx-Builtin ''mmap'' wird auf diesen Syscall gemappt: con PROT_READ: int64 := 1; con PROT_WRITE: int64 := 2; con MAP_PRIVATE: int64 := 2; con MAP_ANONYMOUS: int64 := 32; // 4096 Byte anonymes Mapping var buf: int64 := mmap(0, 4096, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); if (buf == 0) { EPrintLn("mmap fehlgeschlagen"); return 1; } poke8(buf, 42); // schreiben var val: uint8 := peek8(buf); // lesen munmap(buf, 4096); Statische Arrays ''[N]T'' und lokale Variablen liegen im **Stack** (kein explizites Freigeben). ''mmap''/''munmap'' für dynamischen Heap-Speicher. ---- ===== 6. Prozesse und Tasks ===== ==== Prozess starten (ab M5) ==== // sys_spawn: neuer Prozess aus ELF-Binary // Kein fork() — Prozesse werden immer frisch erzeugt con SYS_SPAWN: int64 := 0x0003; con AT_CWD: int64 := -1; // var err, proc_fd := syscall(SYS_SPAWN, AT_CWD, "/bin/editor", argv, envp, 0); ==== Task (leichtgewichtig, automatisch parallelisiert) ==== // sys_task_group: Tasks laufen auf freien Cores ohne Programmierer-Eingriff con SYS_TASK_GROUP_CREATE: int64 := 0x0B00; con SYS_TASK_GROUP_ADD: int64 := 0x0B01; con SYS_TASK_GROUP_AWAIT: int64 := 0x0B02; // var err, group := syscall(SYS_TASK_GROUP_CREATE, 0); // for i in range 0..8 { syscall(SYS_TASK_GROUP_ADD, group, worker_fn, args[i], 8); } // syscall(SYS_TASK_GROUP_AWAIT, group, -1); ''@parallel''-Annotation im Lyx-Code lässt den Compiler dieses Muster automatisch generieren. ---- ===== 7. Fehlerbehandlung ===== Jeder Syscall gibt ''rax'' (Fehlercode) und ''rdx'' (Nutzwert) zurück. In Lyx --target=lyxos übersetzt der Compiler diese Zwei-Register-Rückgabe in Tupel-Syntax: // Zwei-Register-Rückgabe als Lyx-Tupel (ab M5 vollständig unterstützt): // var err, fd := sys_open(AT_CWD, "/config.txt", O_RDONLY, 0); // if (err != 0) { // EPrintLn(StrConcat("Fehler: ", IntToStr(err))); // ERR_NOENT = 2 usw. // return err; // } // Bis M5: Fehlercheck via Rückgabewert var fd: int64 := open("/config.txt", O_RDONLY, 0); if (fd < 0) { EPrintLn("open fehlgeschlagen"); return 1; } Vollständige Fehlercode-Tabelle: [[lyxos:syscalls#fehlercodes|Syscall-Referenz — Fehlercodes]] ---- ===== 8. Programm in die Disk-Image integrieren ===== Bis M5 die Shell fertig ist, muss ein Programm manuell in den Kernel-Startvorgang eingebunden werden. Möglichkeit: direkt in ''kernel.lyx'' aufrufen. // kernel.lyx (nach allen Initialisierungen): import meinprogramm; pub fn main(boot_info_ptr: int64): int64 { // ... Kernel-Init ... return meinprogramm.main(); } Oder: Binary auf FAT32 kopieren und im Build-Skript hinzufügen: lyxc --target=lyxos hello.lyx -o hello.elf mcopy -i bootloader/lyx_boot.img@@1048576 hello.elf ::/hello.elf Letzte Aktualisierung: 2026-08-27 — Abschnitt 1b ergänzt (Stand des lyxos-Ziels nach der Fix-Serie 1.1.8A–1.1.11A, gegen ''lyxc 1.1.11B'' nachgemessen); Binärformat auf den ''LYX!''-Container richtiggestellt; Netzwerk-Abschnitt aus #1734 und #1810. Vorherige Aktualisierung: 2026-06-09