Mix
Random mix of pages and files
FORTH_etc
2025.04.14 23:42:09 end SW FORTH Gilhad

FORTH_etc

Tady budou různé dodatky a prográmky a tak dál

Píšu si implementaci FORTH pro AVR, kde je nedostatek RAM, ale relativně dost FLASH paměti, proto chci mít co nejvíc věcí v programové paměti. Už jsem napsal dost primitiv na to, abych mohl další slova psát přímo ve FORTHu, ale z výše uvedeného důvodu je potřebuju převést do formy dat. V projektu mám pro tyto účely soubor asm.S, kde jsou uloženy hlavičky všech stávajících slov pomocí maker

.macro DEFWORD  lbl, attr, name, codeword // následuje dlouhá definice plná .byte a .long
.macro DEFVAR name // DEFWORD var_\name,0,"\name",push_var_\name
.macro DEFCONST name    // DEFWORD const_\name,0,"\name",push_const_\name
.macro DEFCONST1 name, value    // DEFWORD const_\name,0,"\name",f_doconst
.macro DEFCONST2 name, value    // DEFWORD const_\name,0,"\name",f_doconst2

Moje představa je, že bych do tohoto souboru dal ke konci #include "words.inc", ve kterém by byly další definice pomocí těchto maker a který by byl automaticky generovaný ze souboru words.4th, který by obsahoval definice nových slov ve FORTHu.

Chtěl bych, abys mi pomohl napsat program v pythonu, který by tento převod dělal.

Ten program by měl:

  • číst argumenty z příkazové řádky pomocí standardní knihovny a rozparsovat je (protože časem budu přidávat víc možností)
    • kromě přepínačů (-h help, -v verbose -c --col=40 a dalších) na příkazové řádce můžou být i jména souborů, pokud by měly být jiné než defaultní
    • první je words.4th - zdrojový kód ve FORTHu
    • další je asm.S - definice známých jmen pomocí maker
    • další je words.inc - výsledný soubor
  • přečíst soubor asm.S (nebo soubor daný argumenty) a zapamatovat si jména tam nadefinovaných slov (name) a jejich návěstí (lbl) - pro konstanty a proměnné si je odvodit (ostatní věci z něj ignorovat)
  • (pře)číst soubor words.4th a generovat z něj words.inc
    • words.4th obsahuje kód a komentáře ve FORTHu a do výsledného souboru bude každý řádek uložený od pozice col=40 za dvěma lomítky // jako komentář
    • komentář ve FOTHu začíná obráceným lomítkem a pokračuje do konce řádku, nebo začíná otevírací kulatou závorkou a končí zavírací kulatou závorkou (a tyto jdou do sebe vnořovat)
    • kód ve FORTHu jsou jednotlivá slova oddělená whitespace (mezerníky, taby, novými řádky a komentáři v libovolném počtu), nebo řetězce začínající znaky tečka-uvozovky ." a končící znakem uvozovky " (oboje obklopeno whitespace) - takový řetězec může obsahovat cokoli a překládá se na kód .asciiz a příslušný řetězec
    • slovo dvojtečka : znamená definici nového slova, po něm následující slovo je nově definovaným slovem. Pokud je na témže řádku komentář obsahující uvozovky, tak nick slova je obsah uvozovek, jinak nick slova je to nové slovo - toto se překládá na
DEFWORD w_\nick, 0, "nové slovo", f_docol

všechna následující slova se pak hledají ve slovníku a překládají se jako

.long w_\nick_cw

nebo, pokud se nenajdou, tak se považují za čísla a překládají jako

.long číslo

až do slova středník ; , které se překládá jako

.long w_exit_cw

a definice jím končí

Příklad:

asm.S obsahuje

DEFWORD w_dup, 0, "DUP", f_dup
DEFWORD w_plus,0, "+", f_plus

words.4th obsahuje

: DOUBLE DUP + ; ( "double" )

a přeloží se jako

DEFWORD w_double, 0, "DOUBLE", f_docol                // : DOUBLE DUP + ; ( "double" )
.long w_dup_cw
.long w_plus_cw
.long w_exit_cw

Různé úvahy a pokecy s umělákem a Rubber Duck

Vstup kláves z PS/2 a/nebo Serial

Pisu si FORTH pro atmega2560, převážně v asembleru, trochu v C, a výhledově hlavně ve FORTH samotném. V tuto chvíli mám několik prvních slov (jako @ ! + - DUP DROP ...) a engine mi je dokáže provádět, stejně jako když vybuduju složené slovo ( typu DOUBLE, které volá DUP a + ) Chodí mi komunikace po sériové lince. Chodí mi slovo EMIT (pošle znak na výstup), KEY (vrátí okamžitě další znak nebo nulu, když není dostupný) a WAIT_KEY (vrátí platný znak, s tím, že na něj případně čeká).

Teď potřebuju vymyslet, jak to chci vlastně celé koncipovat. Slovo INTERPRET bude čekat na vstup pomocí slova WORD a pokud půjde o IMMEDIATE slovo, nebo nebude kompilovat, tak slovo provede, jinak pokud půjde o slovo, tak zakompiluje adresu jeho CodeWord, pokud půjde o číslo, tak ho buď dá na zásobník, nebo zkompiluje, a jinak oznámí chybu. To je klasika, s tím problém nemám.

Ale chtel bych tam mít možnost omezené editace řádku před odesláním (v budoucnosti to bude číst HW klávesnici na úrovni PS/2 protokolu (informace o každém stisku, opakování a puštění klávesy), ale mělo by to případně číst i Seriál, nebo soubory - čili neomezeně velká a rychlá (nebo pomalá) data) tedy minimálně odmazání posledního znaku, časem i posun šipkami a vládání/mazání znaků uvnitř, v rámci nějakého bufferu s omezenou velikostí (asi 40/80/128 znaků, uvidím, kolik bude místa v RAM)

(AHA moment: sice chci přístup přez WORD s řádkovým editorem na víc slova zároveň přístup pomocí KEY pro akční hry, ale ono to vlastně nebude nikdy naráz, protože akční hra nebude volat řádkový vstup v akční fázi. Aspoň o jednu starost míň.)

WORD potřebuje nějaký buffer, kam bude ukládat znaky, dokud nedojde k odeslání řádku, potom z toho zpřístupní první slovo a při dalším volání se rozhodne, pokud je poslední znak bufferu enter, tak prostě zpracuje další slovo, jinak posune data na začátek bufferu a donačte zbytek (když třeba bude číst ze souboru a bude v něm dlouhá řádka). Pro načítání z PS/2 klávesnice a Serial bude asi používat nějaký editor řádky, který půjde následně třeba předefinovat, který bude umožňovat editaci řádky a její odeslání dřív, než se řádka začne zpracovávat.

chatGPT

Terminologie, která se osvědčila (inspirace ANS Forth)

Doporučuji převzít tyhle pojmy (i když implementace může být jiná):

🔹 TIB – Terminal Input Buffer

pevný buffer v RAM (např. 80 nebo 128 bajtů)

obsahuje aktuální řádek, který se zpracovává

🔹 >IN

index do TIB

ukazuje, kde se WORD právě nachází

🔹 #TIB (nebo LEN)

délka platných dat v TIB

Tohle ti umožní extrémně jednoduchý WORD.

3️⃣ Jak by měl fungovat WORD (doporučený model)

WORD nikdy nečte hardware. To je zásadní.

WORD:

bere znaky z TIB

ignoruje whitespace

vrátí:

adresu tokenu

délku tokenu

Pseudokód:

while >IN < #TIB and TIB[>IN] is whitespace
    >IN++

if >IN == #TIB
    return 0   (žádné další slovo)

start = >IN
while >IN < #TIB and not whitespace
    >IN++

length = >IN - start
return (start, length)

Pokud dojdeš na konec TIB → INTERPRET zavolá REFILL

4️⃣ REFILL – klíčové slovo

Tohle je přesně místo pro:

  • řádkový editor
  • serial
  • soubory
  • PS/2 klávesnici

REFILL:

naplní TIB novými daty

nastaví:

>IN = 0

#TIB = délka

vrátí:

TRUE – podařilo se

FALSE – konec vstupu (EOF)

: REFILL ( -- flag )
   read-line-into-TIB
   0 >IN !
   len #TIB !
   true ;

INTERPRET pak vypadá skoro učebnicově:

begin
  WORD ?dup
while
  interpret-word
repeat
REFILL 0= until

5️⃣ Řádkový editor – jak ho koncipovat chytře

Tady doporučuju oddělit logiku editoru od zdroje znaků.

Editor by měl pracovat s:

vstupem typu „událost“

ne nutně znak

Např.:

struct key_event {
    uint8_t type;   // CHAR, BACKSPACE, LEFT, RIGHT, ENTER…
    uint8_t value;  // ASCII nebo scancode
};

Serial:

generuje jen CHAR, BACKSPACE, ENTER

PS/2:

generuje plnou sadu událostí

Editor:

má vlastní buffer (může to být rovnou TIB)

drží:

délku

pozici kurzoru

umí:

vložit znak

smazat

posunout kurzor

při ENTER:

vrátí hotový řádek

Výhoda: Později můžeš editor:

nahradit

vypnout

přesměrovat na soubor

6️⃣ Proč je dobré, aby WORD neposouval buffer

To, co popisuješ (posouvání dat na začátek bufferu), funguje, ale:

je to drahé (memmove v AVR 😬)

komplikuje logiku

Lepší je:

nikdy TIB neposouvat

jen řídit >IN

pokud dojdeš na konec → REFILL

U dlouhých řádků:

buď je ořízneš

nebo REFILL vrátí „pokračování“ (volitelné)

7️⃣ IMMEDIATE, kompilace, čísla – máš to správně

Tohle, co píšeš:

pokud IMMEDIATE → provést pokud kompilace → zkompilovat CodeWord pokud číslo → buď push, nebo LITERAL

je přesně správně.

Jediná rada:

nech WORD vracet (addr, len), ne nulou ukončený string

parsování čísla pak může být:

BASE-aware

bez kopírování

8️⃣ Shrnutí architektonického doporučení

✔ KEY je nízkoúrovňový proud ✔ Editor řádky sedí nad KEY ✔ REFILL plní TIB ✔ WORD čte jen z TIB ✔ INTERPRET nikdy neřeší hardware ✔ Žádné posouvání bufferů ✔ >IN je král

Zajímavá slova a nápady

onlyIfOdd

Falvo - Unsuitable 1.0 - The World’s First Blog Engine Written in Forth., github

: onlyIfOdd dup 1 and 0= if r> drop then ;              \ exits from calling word otherwise
: testIt onlyIfOdd .” This number is odd.” cr ;         \ like here
3 testIt 4 testIt