Files
TerminalBasic/docs/tbvm-design.md
Chili Palmer 52ccbb5848 Phase 3 (Kern): Laufzeitbibliothek, Bildschirm und Datei-E/A
Setzt den OpenSpec-Change phase-3-runtime-bildschirm um (77/77 Aufgaben).
Abdeckung laut Inventar: 195 implementiert, 37 offen, 53 Non-Feature.

Vollstaendigkeits-Inventar
- docs/inventar.md mit 285 Eintraegen aus den Themenlisten von
  bas7advr.hlp und qb45advr.hlp, je mit Status und Fundstelle
- crates/tb-frontend/tests/inventar.rs haelt die Tabelle in beide
  Richtungen gegen den Code; der Abdeckungsstand kann nicht veralten

Bruchschritt (Puffer, Host, Korpus)
- Cell/TextScreen ziehen ratatui-frei nach tb-runtime::screen; tb-ui
  behaelt Farbabbildung, Widget und den neuen Terminal-Host
- Host wechselt vom Zeichenstrom auf Anzeige des Zellenpuffers plus
  Ereignisse (Taste, Groessenaenderung, Abbruch)
- Korpusvergleich auf getrimmten Bildschirm-Snapshot; die acht
  bestehenden Sollausgaben blieben dabei unveraendert

Groessenunabhaengigkeit
- 80x25 ist nirgends mehr eine feste Grenze; Groessenaenderungen waehrend
  der Ausfuehrung werden zugestellt (Inhalt oben links erhalten, Cursor
  und VIEW PRINT geklemmt)
- Korpusnachweis: dasselbe Programm bei 80x25 und 120x40 mit je eigener
  Sollausgabe, plus ein Programm mit Groessenwechsel mitten im Lauf

Bibliothek
- Breite Unicode-Zeichen belegen zwei Zellen (Cursor, POS, Randumbruch)
- Bildschirm: CLS, COLOR, LOCATE, WIDTH, VIEW PRINT, SCREEN, CSRLIN, POS
- Tastatur: INKEY$, INPUT$, Funktionstasten-Makros (KEY n / LIST / ON)
- PRINT USING, LPRINT USING, FORMAT$, SetFormatCC
- Mathematik mit kompatiblem PRNG (gleiche Saat, gleiche Folge)
- Datum und Zeit mit Serienwerten ab 1899
- Finanzmathematik: FV#, PV#, Pmt#, IPmt#, PPmt#, NPer#, Rate#, NPV#,
  IRR#, MIRR#, SLN#, SYD#, DDB#
- Datei-E/A: sequenziell, RANDOM (Recordpuffer und UDT-Variablen),
  BINARY, FIELD/LSET/RSET, Statusfunktionen, Dateisystem, MK$/CV
- System: ENVIRON, FRE, CLEAR, TRON/TROFF, STACK, ERDEV, ERR-Anweisung

Altlasten aus Phase 2
- ON ERROR GOTO auf Modulebene ist aus Prozeduren ansprechbar
  (prozeduruebergreifender Fixup im Codegenerator)
- DATA behaelt seinen Rohtext (Gross-/Kleinschreibung, innerer Leerraum)
- Die vier TODO-verify-Zellen der Konvertierungsmatrix sind aufgeloest

Vom Inventar aufgedeckte Fehler
- Zwoelf Non-Features wies der Compiler entgegen der Phase-1-Spec nicht
  ab (CALLS, SSEG, POINT, VIEW, COM, PEN, STRIG, STICK, die
  String*-Routinen, LINE und OPEN "COMn:") -- geschlossen
- Drei Gruppen fehlten im urspruenglichen Umfang: Finanzmathematik,
  Record-Konvertierung, Rest der Dateisystemfunktionen
- bas7advr.hlp allein ist keine vollstaendige Quelle; das Inventar
  bildet die Vereinigung mit qb45advr.hlp

Neue Changes
- phase-3-isam: schliesst Phase 3 ab (Speicherschicht redb)
- phase-3-ortszeit: zieht die UTC-Abweichung zurueck

Neue Abhaengigkeit: unicode-width.
Dokumentiert: sprachreferenz.md und tbvm-design.md sind TODO-frei,
docs/bibliothek.md neu, PLAN.md fortgeschrieben.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-04 06:54:19 +02:00

17 KiB
Raw Blame History

TBVM — Designentwurf

Eigene Stack-basierte Bytecode-VM (Entscheidung siehe PLAN.md, 0.2). Die VM ist in tb (IDE) und in von tbc erzeugte Executables eingebettet — identischer Code, identisches Verhalten, schneller Turnaround.

Werte-Modell

Tagged Enum, kein NaN-Boxing (Einfachheit und Debugbarkeit vor Mikro- performance):

enum Value {
    Int(i16),          // INTEGER
    Long(i32),         // LONG
    Single(f32),       // SINGLE
    Double(f64),       // DOUBLE
    Currency(i64),     // CURRENCY, Festkomma ×10 000
    Str(Rc<str>),      // STRING (immutabel geteilt; Copy-on-Write bei MID$-Anweisung)
    // Arrays und TYPE-Instanzen leben im Heap-Bereich der VM,
    // Variablen-Slots referenzieren sie per Handle (Rc<RefCell<…>>).
}
  • Kein GC nötig: der Dialekt kennt keine Zyklen (keine Objektreferenzen in TYPEs, keine Closures) → Rc genügt.
  • Feste Strings (STRING * n) und TYPE-Records werden als eigene Speicherobjekte mit fester Zeichen-/Elementstruktur geführt.

Bytecode

  • Stack-Maschine, Opcodes 1 Byte + Operanden variabler Länge (u8/u16/u32, little-endian).
  • Modul-Struktur: Konstantenpool (Strings, Zahlen), Typtabelle (TYPE-Layouts), Prozedurtabelle (Signatur, Locals-Anzahl, Code-Offset), globale Slots, DATA-Segment (für READ/RESTORE), Zeilentabelle.
  • Zeilentabelle: Abbildung Code-Offset → (Moduldatei, Zeile). Grundlage für ERL, Fehlermeldungen, Breakpoints und Einzelschritt.
  • Aufrufkonventionen:
    • SUB/FUNCTION: eigener Frame (Locals-Slots, Operandenstack-Basis); BYREF-Parameter als Referenz-Slots (Handle auf Variablen-Slot).
    • GOSUB: kein Frame — Rücksprungadresse auf separatem GOSUB-Stack im aktuellen Frame (RETURN prüft diesen zuerst).
  • Fehlerbehandlung: Zwei Ebenen (Scoping geklärt 2026-09-04). ON LOCAL ERROR GOTO x setzt einen Handler im aktuellen Frame; sein Sprungziel muss im selben Rumpf liegen. ON ERROR GOTO x ohne LOCAL setzt den modulweiten Handler — auch aus einer Prozedur heraus, und sein Sprungziel liegt dann im Modulrumpf (der Codegenerator löst es über Prozedur 0 auf, die zuerst übersetzt wird). Ein Laufzeitfehler sucht zuerst lokale Handler die Aufrufkette hinauf, dann den modulweiten; beim modulweiten wird bis Frame 0 abgewickelt, weshalb RESUME NEXT dort hinter dem Prozeduraufruf fortsetzt. Der Handler setzt ERR/ERL; RESUME nutzt den gemerkten Anweisungs-Offset. Ein Fehler im Handler ist fatal (keine Kaskaden).

Ausführungsmodell / Unterbrechbarkeit

  • Die Interpreterschleife läuft in Ticks: nach jeder Anweisung (Grenze aus der Zeilentabelle) prüft sie ein Flag-Set: Breakpoint? Einzelschritt? Strg+Untbr? Ereignis-Queue nicht leer und Zustellung erlaubt?
  • Ereigniszustellung (Forms, Timer) erfolgt kooperativ: nur an Zustellpunkten (DOEVENTS, SLEEP, blockierende Eingabe, Ende einer Ereignisprozedur) — wie im Vorbild.
  • Die VM ist eine gewöhnliche zustandsbehaftete Struktur, step()-basiert; die einbettende Schleife (IDE-Debugger oder Runner) treibt sie. Kein eigener Thread nötig; Terminal-Events werden zwischen Ticks gepollt.

.tbc-Container (Stand Phase 2, Formatversion 1)

Magic "TBC\0" · Formatversion u16 · Flags u16 · Abschnittsanzahl u32
Abschnittstabelle: [ (Kennung 4 Byte, Offset u32, Länge u32) ]
Abschnitte:
  MODN  Modulname + OPTION BASE
  CONS  deduplizierter Stringpool
  TYPS  TYPE-Layouts (Feld-Initialisierungstypen)
  GLOB  globale Slots (Initialisierungstyp + Name für Debugger)
  PROC  Prozedurtabelle: Name, Parameteranzahl, Frame-Slots, Code
        (Zeileninfo liegt inline im Code: Stmt-/SetErl-Instruktionen —
        ersetzt den früher geplanten separaten LINES-Abschnitt)
  DATA  DATA-Konstanten (Rohtext + Quellzeile)
  JMPT  Sprungtabellen für ON n GOTO/GOSUB
  FORMS (ab Phase 4: serialisierte .FRM-Beschreibungen)

Serialisierung mit einfachem eigenem Writer/Reader (kein serde nötig, Format bleibt stabil und dokumentiert); unbekannte Formatversionen werden mit Meldung abgewiesen. Instruktions-Encoding: 1 Opcode-Byte + Operanden little-endian, Opcode-Bytes gruppenweise mit Lücken vergeben (crates/tb-vm/src/bytecode.rs ist die normative Liste).

Eigenständige Executables

tbc build --exe kopiert den vorkompilierten Runner (dieselbe tb-vm/tb-runtime/tb-ui-Bibliothek wie die IDE) und hängt das .tbc als Ressource an (Anhängen ans Binary + Fußzeile mit Offset/Magic; portabel für alle drei Plattformen). Alternative — include_bytes! + Cargo-Build beim Nutzer — verworfen: erfordert Rust-Toolchain beim Anwender.

Performance

Compiler (Instant-Compile, Anforderung siehe PLAN.md):

  • Single-Pass pro Modul: Lexen, Parsen und Codegen in einem Durchlauf; Vorwärtsreferenzen (Prozeduren, Labels) über Fixup-Listen statt zweitem Pass. Die Sprache ist dafür gemacht — das Vorbild kompilierte auf 1992er-Hardware gefühlt sofort.
  • Modulweise inkrementell: .BAS/.FRM werden unabhängig zu Bytecode- Einheiten übersetzt und beim Build nur zusammengebunden; die IDE recompiliert nur geänderte Module (Hash über Quelltext).
  • Keine Optimierungspasses. Erlaubt sind nur Gratis-Optimierungen im Codegen (Konstantenfaltung im Ausdruck, Peephole beim Emit).
  • Budget als Test verankern (Phase 2): Benchmark-Projekt (~50k Zeilen) muss unter 1 s kompilieren (Release-Build der Toolchain, Referenzrechner); einzelnes Modul < 50 ms.

VM-Ausführung:

  • Alle Namen werden zur Compilezeit aufgelöst: Variablen/Parameter sind Slot-Indizes, Prozeduren Tabellenindizes — zur Laufzeit keine Hash-Lookups.
  • Statische Typen des Dialekts ausnutzen: typisierte Opcodes (ADD_I16, ADD_F64, CONCAT …) statt generischem Dispatch über Value-Tags in heißen Pfaden.
  • Dichte Opcodes, match-Dispatch in einer engen Schleife; Tick-Prüfung (Events/Breakpoints) nur an Anweisungsgrenzen über ein einzelnes Flag-Wort, nicht pro Opcode.
  • Strings immutabel via Rc<str> (Kopien sind Pointer-Kopien); Konstantenpool dedupliziert.
  • Messlatte (Phase 2, Benchmarks in benches/): typische Schleifen-/String-Lasten mindestens ~100× schneller als das Vorbild auf Originalhardware; Richtwert grob Lua-Interpreter-Klasse, gemessen und dokumentiert statt geraten.

Benchmark-Ergebnisse (2026-09-02, Release-Build, Entwicklerrechner Windows 11, cargo bench -p tb-vm):

  • Compile-Budget (benches/compile.rs):

    • Einzelmodul, 508 Zeilen: 1,2 ms (Budget 50 ms — eingehalten)
    • Projekt, 49 760 Zeilen über 20 Module: 124 ms (Budget 1 000 ms — eingehalten); Durchsatz ≈ 400 000 Zeilen/s
  • VM-Durchsatz (benches/vm.rs):

    • INTEGER-Schleife (Arithmetik + MOD): ≈ 4,8 Mio Iterationen/s
    • DOUBLE-Schleife: ≈ 5,2 Mio Iterationen/s
    • SUB-Aufrufe mit BYREF: ≈ 5,2 Mio Aufrufe/s
    • String-Lasten (MID$/INSTR/Verkettung): ≈ 0,8 Mio Runden/s Nachmessung nach Phase 3 (2026-09-04, macOS, Apple Silicon, cargo bench -p tb-vm) — die Zustellpunkte des neuen Host (Anzeige nur bei geändertem Bildschirm, Ereignisabholung alle 1024 Anweisungen) kosten nichts Messbares:
  • Compile-Budget: Einzelmodul 508 Zeilen 0,77 ms; Projekt 49 760 Zeilen 53 ms (≈ 950 000 Zeilen/s)

  • VM-Durchsatz: INTEGER-Schleife 11,0 Mio/s, DOUBLE 12,2 Mio/s, SUB-Aufrufe 10,7 Mio/s, String-Lasten 2,4 Mio Runden/s

(Die höheren Zahlen gegenüber 2026-09-02 gehen auf die andere Maschine zurück, nicht auf eine Optimierung — gemessen wurde beide Male derselbe Benchmark.)

  • Einordnung: bei ≈ 13 Instruktionen je Schleifeniteration entspricht das ≈ 60 Mio Instruktionen/s — Vorbild-Programme auf 1992er-Hardware werden um weit mehr als den Faktor 100 übertroffen; für 80×25- Terminalprogramme mehr als ausreichend. Ein Performance-Pass ist erst bei Bedarf vorgesehen (Phase 6).

Zahlenkonvertierungs-Matrix (Feindesign, 2026-09-02)

Grundlage: Original-Hilfe (Topics „Arithmetic Operators", „CINT/CLNG", „MOD", „Numeric Types"). Vier Punkte sind dort nicht geregelt; sie wurden am 2026-09-04 entschieden und sind unten als Projektentscheidung gekennzeichnet — sie gelten damit als bewusste Festlegung, nicht als rekonstruiertes Vorbildverhalten. Der Korpustest konvertierung.bas verankert die Matrix ausführbar.

Rangfolge für Promotion: INTEGER < LONG < CURRENCY < SINGLE < DOUBLE. Ausnahme: CURRENCY gemischt mit SINGLE oder DOUBLE ergibt DOUBLE (nicht SINGLE). Projektentscheidung 2026-09-04: Die Original-Hilfe regelt diesen Fall nicht. CURRENCY trägt 19 signifikante Stellen, SINGLE nur 7 — eine Promotion nach SINGLE zerstörte genau die Genauigkeit, für die es den Typ gibt. DOUBLE (16 Stellen) ist die nächstbeste Wahl.

Ergebnistyp binärer Operatoren (l op r; T = promoteter Typ):

Operator Ergebnistyp Besonderheiten
+ - * T; gleiche Typen bleiben erhalten Ganzzahl-/CURRENCY-Überlauf → Fehler 6 (auch INTEGER+INTEGER promotet nicht)
/ DOUBLE, wenn ein Operand DOUBLE oder CURRENCY, sonst SINGLE Division durch 0 → Fehler 11
\ MOD INTEGER, wenn beide Operanden INTEGER, sonst LONG Operanden werden vorher kaufmännisch (banker's) auf Ganzzahl gerundet; außerhalb LONG → Fehler 6; Divisor 0 → Fehler 11; MOD-Vorzeichen wie Dividend
^ DOUBLE, wenn ein Operand DOUBLE oder CURRENCY, sonst SINGLE (Projektentscheidung 2026-09-04) Rechnung intern in DOUBLE; 0 ^ negativ → Fehler 11; negative Basis mit nicht-ganzem Exponent → Fehler 5
= <> < <= > >= INTEGER (1/0) Vergleich im promoteten Typ; Strings codepoint-weise
NOT AND OR XOR EQV IMP INTEGER, wenn alle Operanden INTEGER, sonst LONG bitweise; Operanden vorher banker's-gerundet auf Ganzzahl, außerhalb LONG → Fehler 6
unäres - Typ des Operanden (32768) INTEGER → Fehler 6

Konvertierung Quelle → Ziel (implizit bei Zuweisung/Argument, explizit via CINT/CLNG/CSNG/CDBL/CCUR — identische Semantik):

von \ nach INTEGER LONG SINGLE DOUBLE CURRENCY
INTEGER exakt exakt exakt ×10 000
LONG Bereich prüfen → 6 nächstgelegener f32 exakt ×10 000
SINGLE banker's + Bereich → 6 banker's + Bereich → 6 exakt (Widening) banker's auf 4 Nachkommastellen + Bereich → 6
DOUBLE banker's + Bereich → 6 banker's + Bereich → 6 Bereich prüfen → 6, sonst nächstgelegener f32 banker's auf 4 Nachkommastellen + Bereich → 6
CURRENCY ÷10 000, banker's + Bereich → 6 ÷10 000, banker's + Bereich → 6 ÷10 000.0 ÷10 000.0
  • Banker's Rounding: zur nächsten Ganzzahl, bei .5 zur geraden (Original-Hilfe: CINT). Gilt für alle Gleitkomma→Ganzzahl-Übergänge inklusive \/MOD/Logik-Operanden und CURRENCY-Skalierung.
  • Zahl ↔ String nie implizit (Compile-Zeit „Type mismatch"); explizit nur über STR$/VAL.

Textdarstellung (PRINT und STR$):

  • INTEGER/LONG: dezimal ohne Dezimalpunkt.
  • SINGLE: bis 7 signifikante Stellen, kürzeste Darstellung, keine führende Null vor dem Dezimalpunkt (.5), keine nachgestellten Nullen; außerhalb des Festformat-Bereichs Exponentialform mE±xx. Projektentscheidung 2026-09-04: Die Original-Hilfe nennt keine Schwelle. Gewählt ist der Dezimalexponent < 7 oder ≥ 7 — damit bleibt jeder Wert im Festformat, der sich mit den 7 signifikanten Stellen des Typs verlustfrei schreiben lässt (9999999 fest, 1E+07 exponentiell; .0000001 fest, 1E-08 exponentiell).
  • DOUBLE: wie SINGLE mit bis 16 signifikanten Stellen und D±xx als Exponentmarke.
  • CURRENCY: Festformat mit bis zu 4 Nachkommastellen, nachgestellte Nullen entfallen (2.5000@2.5, 1.10@1.1). Projektentscheidung 2026-09-04: Die Original-Hilfe nennt kein Ausgabeformat. Gewählt ist dieselbe Regel wie bei den übrigen Zahlentypen — nachgestellte Nullen entfallen —, damit PRINT über alle Typen einheitlich wirkt. Wer feste Nachkommastellen braucht, nutzt PRINT USING.
  • PRINT stellt nicht-negativen Zahlen ein Leerzeichen, negativen ein - voran und hängt stets ein Leerzeichen an; STR$ nur das führende Leerzeichen/-, kein nachgestelltes.

Opcode-Satz (Feindesign, 2026-09-02)

In-Memory führt die VM dekodierte Instruktionen (Vec<Instr>, ein Rust- Enum mit eingebetteten Operanden — Wort-Dispatch, keine Byte-Dekodierung im heißen Pfad); die .tbc-Serialisierung bildet jede Instruktion auf 1 Opcode-Byte + Operanden (little-endian) ab. Typkürzel: I2=INTEGER, I4=LONG, R4=SINGLE, R8=DOUBLE, CY=CURRENCY, STR=STRING.

Gruppe Instruktionen Bemerkung
Anweisungsgrenze Stmt(line:u32) · SetErl(n:u32) Stmt prüft das Tick-Flag-Wort (Breakpoint/Einzelschritt/Abbruch), aktualisiert Zeile und Resume-Punkt; SetErl bei numerischen Zeilennummern
Konstanten/Stack PushInt(i16) PushLng(i32) PushSng(f32) PushDbl(f64) PushCur(i64) PushStr(pool:u16) · Dup Pop Stringpool dedupliziert
Variablen LoadGlobal/StoreGlobal(u16) · LoadLocal/StoreLocal(u16) · MakeRefGlobal/MakeRefLocal(u16) · LoadRef/StoreRef(u16) Slots statisch aufgelöst; *Ref bedienen BYREF-Parameter (Referenz-Werte)
Arrays DimGlobal/DimLocal{slot,dims,elem} RedimGlobal/RedimLocal{…} EraseGlobal/EraseLocal(u16) · LoadElem/StoreElem{dims:u8} · MakeRefElem{dims:u8} Grenzen auf dem Stack (lo/hi je Dimension als LONG); Indexprüfung → Fehler 9
UDT LoadField/StoreField(u16) · MakeRefField(u16) Feldindex aus Typtabelle; verschachtelt durch Verkettung
Arithmetik Add/Sub/Mul{I2,I4,R4,R8,CY} · Neg{…} · Div{R4,R8} · IDiv/Mod{I2,I4} · PowR8 (^ rechnet in DOUBLE, SINGLE-Ergebnis per Conv) monomorph; Ganzzahl-/CY-Überlauf → Fehler 6, Division durch 0 → Fehler 11
Konvertierung Conv_<src>_<dst> für alle 20 geordneten Paare aus {I2,I4,R4,R8,CY} Semantik exakt nach Matrix (Rundung/Überlauf)
Logik Not/And/Or/Xor/Eqv/Imp{I2,I4} bitweise
Vergleich Cmp{Eq,Ne,Lt,Le,Gt,Ge}{I2,I4,R4,R8,CY,STR} Ergebnis INTEGER 1/0
Strings Concat Rc<str>, Kopien sind Pointer-Kopien
Kontrollfluss Jump(u32) JumpIfFalse(u32) JumpIfTrue(u32) Ziele absolut (Instruktionsindex) innerhalb der Code-Einheit; Fixups beim Emit
GOSUB Gosub(u32) RetGosub RetGosubTo(u32) GOSUB-Stack pro Frame; leer → Fehler 3
Berechnete Sprünge OnJump{table:u16,gosub:bool} Sprungtabellen im Modul; 0/zu groß: kein Sprung; negativ/>255 → Fehler 5
Prozeduren Call(proc:u16) RetProc RetFn CallBuiltin{id:u16,argc:u8} Argumente links→rechts auf dem Stack (Werte oder Referenzen); RetFn transportiert den Funktionswert über den Frame-Abbau
Fehler OnErrorGoto(u32) OnErrorLocal(u32) OnErrorDisable OnErrorLocalDisable · Resume0 ResumeNext ResumeLabel(u32) · RaiseError Scoping siehe unten
DATA ReadData(art) Restore(u32) art 0 = String, 1 = Zahl (DOUBLE, dann Conv nach Matrix); Ende → Fehler 4, unkonvertierbar → Fehler 13
E/A Input{…} sowie Builtins (PRINT-Familie über CallBuiltin) Konsolenwirkung ausschließlich über das Host-Trait
Ende End StopInstr SystemInstr Verhalten siehe Runner

FOR/NEXT wird ohne Spezial-Opcodes kompiliert: Grenz-/Schrittwert einmal in versteckte Slots ausgewertet; bei konstantem STEP wählt der Codegen die Vergleichsrichtung zur Compilezeit, sonst verzweigt ein einmalig berechnetes Vorzeichen-Flag auf zwei Vergleichssequenzen. Inkrement mit regulärer typisierter Addition (Überlauf → Fehler 6, wie Vorbild). SELECT CASE wertet den Selektor in einen versteckten Slot aus und wird als Vergleichs-/Sprungkette abgesenkt.

Fehlerbehandlung: Scoping (Feindesign, 2026-09-02)

  • ON ERROR GOTO x setzt den modulweiten Handler — auch aus einer Prozedur heraus. ON LOCAL ERROR GOTO x setzt einen Frame-lokalen Handler, der den modulweiten für die Dauer des Prozedurlaufs verdeckt. … GOTO 0 deaktiviert den jeweiligen Handler.
  • Fehlerfall: erst die Frame-Kette von innen nach außen nach LOCAL-Handlern absuchen, sonst modulweiter Handler; Stack wird bis zum Handler-Frame abgewickelt, Anweisungs-Offset und Frame für RESUME gemerkt, ERR/ERL gesetzt. Ohne Handler: Abbruch mit Meldung und Quellzeile.
  • Fehler während aktiver Behandlung (zwischen Handler-Eintritt und RESUME): sofortiger Abbruch, keine Kaskade (Vorbild).
  • ERL liefert die zuletzt durchlaufene numerische Zeilennummer (Labels zählen nicht; ohne numerische Zeilen: 0).

Runner-Verhalten STOP/CONT (Entscheidung 2026-09-02)

  • END, SYSTEM und das Erreichen des Programmendes beenden den Prozess mit Exit-Code 0.
  • STOP außerhalb der IDE terminiert mit der Meldung STOP in line n (bzw. Modul/Zeile) und Exit-Code ≠ 0. Offene Dateien werden wie bei END geschlossen.
  • CONT ist ein Direktmodus-Konzept der IDE (Phase 5): dort suspendiert STOP die VM (step()-Architektur), CONT setzt fort. Im Kompilat existiert kein CONT.

Offene Punkte

Keine — die Punkte aus Phase 0 (Opcode-Satz, Konvertierungsmatrix, STOP/CONT im Runner) sind oben ausgearbeitet. Die vier Stellen, zu denen die Original-Hilfe schweigt, sind am 2026-09-04 als Projektentscheidung festgelegt, an Ort und Stelle begründet und über tests/compat/konvertierung.bas ausführbar verankert. Sollte je eine belastbare Fundstelle auftauchen, die ihnen widerspricht, gilt sie — die Entscheidungen sind Lückenfüller, keine Setzungen gegen das Vorbild.