Lyx – Low-Level: Speicherzugriff & Pragmas

Systemprogrammierung – Treiber, Kernel-Module, Echtzeit-Regler, Hardware-Abstraktionen – erfordert direkten Zugriff auf Speicheradressen. Lyx löst das ohne Zeigertypen: Eine Adresse ist ein gewöhnlicher int64, gelesen und geschrieben wird mit den eingebauten Primitiven peek/poke.

 
Ein Zeigermodell gibt es in Lyx nicht. Diese Seite beschrieb früher eine Pascal-artige Syntax (^T als Typ, ^x für die Adresse, p^ zum Dereferenzieren) sowie mehrere Pragmas. Keines dieser Konstrukte ist umgesetzt:

^ Konstrukt ^ Verhalten ^
| var p: ^int64 | unknown type in var decl '^' | > | ^x (Adresse von) | expected expression | > | p^ (Dereferenz) | Parse-Fehler | > | unsafe { … } | undefined symbol '\unsafe'\ | > | @inline, @no_opt, @section, @parallel | unbekanntes Attribut | > > ^ ist in Lyx ausschließlich der XOR-Operator: 6 ^ 3 ergibt 5. > > Die Grammatik führt in PrimaryType folgerichtig keinen Zeigertyp. Was stattdessen gilt, steht unten und ausführlich unter Rohspeicher. </box> → Rohspeicher: alloc, peek & poke · Memory Management · Attribute & Pragmas —- ===== 1. Adressen sind int64 ===== Eine Adresse wird wie eine Zahl behandelt. alloc liefert sie, peek/poke greifen darauf zu, free gibt frei — Größe ist dabei Pflicht, weil Lyx keine versteckten Metadaten führt.
import std.io;
import std.alloc;

fn main(): int64 {
    var zelle: int64 := alloc(8);      // 8 Bytes, Adresse als int64
    if (zelle == 0) { return 1; }      // 0 = kein Speicher

    poke64(zelle, 42);                 // schreiben
    PrintLn(IntToStr(peek64(zelle)));  // lesen -> 42

    free(zelle, 8);
    return 0;
}
^ Primitiv ^ Breite ^ Zweck ^ |
peek8 / poke8 | 1 Byte | Protokoll-Bytes, Zeichen | | peek16 / poke16 | 2 Byte | kompakte Felder | | peek32 / poke32 | 4 Byte | Hardware-Register, IPv4 | | peek64 / poke64 | 8 Byte | Standardfall, Adressen | | peekf64 / pokef64 | 8 Byte | Gleitkomma | Alle sind eingebaut — kein Import nötig. alloc und free kommen aus std.alloc. ==== Einen Wert zurückgeben ==== Da es keinen Adressoperator gibt, lässt sich die Adresse einer lokalen Variablen nicht nehmen. Ein Ausgabeparameter wird deshalb als Zelle übergeben:
import std.io;
import std.alloc;

fn TeileMit(a: int64, b: int64, restZelle: int64): int64 {
    poke64(restZelle, a % b);
    return a / b;
}

fn main(): int64 {
    var rest: int64 := alloc(8);
    var quotient: int64 := TeileMit(17, 5, rest);

    PrintLn(IntToStr(quotient));        // 3
    PrintLn(IntToStr(peek64(rest)));    // 2

    free(rest, 8);
    return 0;
}
—- ===== 2. Structs über Byte-Offsets ===== Ein Struct auf dem Heap wird über seine Feld-Offsets angesprochen. Die Standardbibliothek arbeitet durchgehend so —
A.feld bezeichnet in Lyx den Byte-Offset des Feldes:
import std.io;
import std.alloc;

type Punkt = struct {
    x: int64;
    y: int64;
};

fn main(): int64 {
    var p: int64 := alloc(16);

    poke64(p + Punkt.x, 3);
    poke64(p + Punkt.y, 4);

    PrintLn(IntToStr(peek64(p + Punkt.x)));   // 3
    PrintLn(IntToStr(peek64(p + Punkt.y)));   // 4

    free(p, 16);
    return 0;
}
sizeof(ausdruck) liefert die Breite eines Werts in Bytes — es nimmt einen Ausdruck, keinen Typnamen: sizeof(1) ergibt 8. Für Strukturen auf dem Stack ist der gewöhnliche Feldzugriff der richtige Weg; Rohspeicher braucht es nur, wo die Lebensdauer über den Aufruf hinausreicht oder die Größe erst zur Laufzeit feststeht. —- ===== 3. @volatile – Memory-Mapped I/O ===== Auf Mikrocontrollern sind Hardware-Register in den Adressraum eingeblendet. @volatile verhindert, dass der Compiler wiederholte Zugriffe auf dieselbe Adresse wegoptimiert.
@volatile var GPIO_OUT: int64 := 0x3FF44004;
Der Zugriff erfolgt über
poke32/peek32 auf die Adresse:
fn LedAn(pin: int64): int64 {
    poke32(0x3FF44004, 1 << pin);
    return 0;
}
—- ===== 4. packed struct – Layout ohne Füllbytes =====
packed struct legt die Felder ohne Ausrichtungslücken ab — nötig für Protokollrahmen und Hardware-Strukturen, deren Byte-Anordnung festliegt.
type Kopf = packed struct {
    typ:    uint8;
    laenge: int64;
};
flat struct gibt es ebenfalls; beide sind unter Datentypen beschrieben. —- ===== 5. null ===== null ist gültig und entspricht der Adresse 0 — dem Wert, den alloc im Fehlerfall liefert:
var zelle: int64 := alloc(64);
if (zelle == null) { return 1; }   // gleichbedeutend mit == 0
—- ===== 6. Speicherzugriff im Safety-Umfeld ===== ^ Situation ^ Empfehlung ^ | Regelzyklus (DAL-A/B) | Statische Arrays
[N]T und Structs auf dem Stack — keine Allokation | | Puffer bekannter Größe | [N]uint8 statt alloc | | Hardware-Register | @volatile plus peek32/poke32 | | Dynamische Größe | alloc/free mit gepaarter Freigabe, außerhalb des Regelzyklus | Eine Freigabe gehört in jeden Pfad, der sie erreicht — Lyx hat keine Destruktoren. Details unter Fehler-Konventionen und Memory Management. —- Weiterführende Seiten: * Rohspeicher — alloc, peek & poke im Detail * Memory Management — Lebensdauer und Freigabe * Attribute & Pragmas — vollständige @-Referenz * Datentypen — packed/flat struct Codebeispiele geprüft: gegen lyxc 1.2.5C übersetzt (Prüflauf 2026-09-08 über die gesamte Doku: 574 Vollprogramme, 0 echte Fehler; zusätzlich 5159 Aufrufe gegen die pub fn-Signaturen in aurum/std'' gehalten, 0 Abweichungen).