====== 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 [[lyx_-_programmiersprache:sprache:rohspeicher|Rohspeicher]].
→ [[lyx_-_programmiersprache:sprache:rohspeicher|Rohspeicher: alloc, peek & poke]] · [[lyx_-_programmiersprache:sprache:memory-management|Memory Management]] · [[lyx_-_programmiersprache:sprache:attributes-pragmas|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 [[lyx_-_programmiersprache:units|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 [[lyx_-_programmiersprache:sprache:datentypen|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 [[lyx_-_programmiersprache:sprache:std-fehlerkonventionen|Fehler-Konventionen]] und [[lyx_-_programmiersprache:sprache:memory-management|Memory Management]].
----
**Weiterführende Seiten:**
* [[lyx_-_programmiersprache:sprache:rohspeicher|Rohspeicher — alloc, peek & poke im Detail]]
* [[lyx_-_programmiersprache:sprache:memory-management|Memory Management — Lebensdauer und Freigabe]]
* [[lyx_-_programmiersprache:sprache:attributes-pragmas|Attribute & Pragmas — vollständige @-Referenz]]
* [[lyx_-_programmiersprache:sprache:datentypen|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).