Mix
Random mix of pages and files
FORTH_log_01
2025.04.14 23:40:22 end SW FORTH Gilhad

FORTH_log_01

  • 2025.04.14 23:41:19 Založení souboru
  • 2025.04.18 premýšlím jak vytvářet struktury ve FLASH (hlavně teda slova) a zkouším zapojit uměláka, zdá se, že přez C/C++ to moc nepůjde a budu stejně muset použít přímo asembler:
    • poznámka - když v asembleru použiju ".extern symbol", tak už nepotřebuju includovat žádné *.h, protože už jsem přece řekl, že "tahle adresa je někde jinde a dostanu ji od linkeru"
    • podobně ".global symbol" řiká že tuhle adresu exportuju
    • vlastně žádné hlavičky moc nepotřebuju, protože nemám žádné typy a tak a jen potřebuju překladači říct, co je odjinud, ale už mě nezajímá odkud.
      • viz "asm hlavičky" níže

Problemy s C/C++

Pisu si FORTH na AVR v C/C++ a potrebuju do FLASH nejak rozumne ulozit struktury s ruznou delkou (WORDs pro FORTH).

budou obsahovat odkazy na jine struktury a odkazy na funkce v C/C++.

V assembleru by to vypadalo asi nejak takhle:

; PLUS
w_plus:
.word 0 ; next
.byte 1, '+' ; name
.word f_plus ; address of function f_plus
; DUP
w_dup:
.word w_plus ; next
.byte 3, 'D','U','P' ; name
.word f_dup ; address of function f_dup
; DOUBLE
w_double:
.word w_dup; next
.byte 6,'D','O','U','B','L','E' ; name
.word f_docol ; address of function DOCOL to interpret next data
.word w_dup,w_plus,f_exit ; : DOUBLE DUP + ;

Základní příklad propojení mezi dvěma moduly

ALFA.S

; hlavička
.extern ALFA_in  ; symbol, na který bude odkazováno z jiného modulu
.global ALFA_out  ; globální symbol, který bude viditelný pro ostatní moduly

; data v ALFA
some_data:
    .byte 1, 2, 3, 4
    .word ALFA_in  ; adresa ALFA_in (toto bude propojeno při linkování)
    .byte 5, 6, 7, 8

ALFA_out:
    .byte 9, 10, 11  ; data na konci modulu ALFA

BETA.S

; hlavička
.extern BETA_out  ; symbol, na který bude odkazováno z jiného modulu
.global BETA_in   ; globální symbol pro tento modul

; data v BETA
some_other_data:
    .byte 21, 22, 23, 24
    .word BETA_in  ; adresa BETA_in

BETA_out:
    .byte 25, 26, 27  ; data na konci modulu BETA

Pokud máte například dva moduly (ALFA.S a BETA.S), přeložte je:

as -o ALFA.o ALFA.S
as -o BETA.o BETA.S

A poté je spojte pomocí linkeru:

ld -o demo ALFA.o BETA.o

Linker propojí symboly mezi těmito moduly a při běhu programu budou data správně propojena. You said: kdyz udelam as -o ALFA.o ALFA.S as -o BETA.o BETA.S tak dostanu ALFA.o a BETA.o, kde jeste jsou hodnoty tech ALFA_in a BETA_in nevyresene a budou doplneny az pri linkovani linkerem. Jak rict linkeru, ze do ALFA_in ma vyplnit adresu BETA_out a do BETA_in ma vyplnit nulu? ChatGPT said:

Aby bylo možné při linkování nahradit symboly jako ALFA_in a BETA_in správnými adresami (v tomto případě adresou BETA_out pro ALFA_in a nulou pro BETA_in), můžete využít několik metod, jak linker instruovat, co se má s těmito symboly stát.

Níže uvádím několik způsobů, jak toho dosáhnout: Použití skriptu linkeru (linker script)

Nejlepší způsob, jak to udělat, je použít linker script. Linker script je soubor, který umožňuje linkovacímu procesu specifikovat, jakým způsobem mají být symboly propojeny a jaké hodnoty mají být přiřazeny symbolům. Krok 1: Vytvoření linker skriptu

Vytvoříte si vlastní linker script, který definuje, jak se mají symboly při linkování přiřadit. Tento skript můžete použít při linkování a řídit tak hodnoty symbolů.

Vytvořte soubor, například custom.ld, s následujícím obsahem:

SECTIONS {
    . = 0x10000;  /* Místo, kde začíná kód (např. adresa pro mikrokontrolér) */

    .text : {
        *(.text)    /* Všechny kódy (funkce) */
    }

    .data : {
        *(.data)    /* Data, která budou inicializována */
    }

    .bss : {
        *(.bss)     /* Data, která budou nainicializována na nulu */
    }

    /* Určujeme hodnoty pro symboly ALFA_in a BETA_in */
    ALFA_in = BETA_out;  /* Nastavení ALFA_in na adresu BETA_out */
    BETA_in = 0;         /* Nastavení BETA_in na 0 */
}

Krok 2: Linkování s linker skriptem

Pak při linkování použijete tento skript takto:

ld -o demo ALFA.o BETA.o -T custom.ld

Tento skript říká linkeru, že symbol ALFA_in by měl být nahrazen adresou BETA_out a symbol BETA_in nulou.

Použití příznaků pro linker

Pokud nechcete používat celý vlastní linker skript, můžete při linkování použít příznaky, které vám umožní nastavit hodnoty symbolů přímo. Krok 1: Použití příznaků pro definici symbolů

Pokud máte pouze pár symbolů, můžete použít příznaky linkeru, které umožní definovat symboly přímo při linkování. Například, použijte -e k určení startovní funkce a -defsym pro definici symbolů.

Příklad linkování:

ld -o demo ALFA.o BETA.o -e main -defsym ALFA_in=BETA_out -defsym BETA_in=0

Tento příkaz linkuje ALFA.o a BETA.o a přiřadí symbolu ALFA_in adresu BETA_out a symbolu BETA_in hodnotu 0. * -e main: Určuje počáteční funkci (v tomto případě main, což je standardní vstupní bod programu). * -defsym ALFA_in=BETA_out: Definuje symbol ALFA_in jako adresu BETA_out. * -defsym BETA_in=0: Nastaví symbol BETA_in na hodnotu 0.

Úvaha o hlavičkách pro asembler

Možná by mělo smysl něco jako při překladu v make ještě každý modul projít a vybrat všechny .global a vytvořit nový soubor (typu ALFA.export) kde by byly jako .extern

A moduly, které chtějí něco z ALFA používat by měly #include "ALFA.export" a neobsahovaly by žádné .export direktivy, takže pokud by použily symbol z ALFA, tak by ten .export měly v tom includovaném souboru a pokud by takový symbol v ALFA nebyl, tak by překladač vyhodil chybu

a v Makefile by pro tohle bylo pravidlo

%.export: %.S

        grep "^ *.global " $< |sed "s/.global/.extern/" > $@

(a každý *.S by závisel na použitých *.extern)

  • Na druhou stranu asi bude lepsi, kdyz pri prekladu budou vsechny .extern pritomne a az pri linkovani se bude testovat, zda je neco naplni.

Pokračování - IP a stack a první slova

  • 2025.04.19 Nějak jsem se vymotal ze syntaktických problémů a mám teď zhruba funkční stack, IP a několik prvních slov, vypadá to takhle:

Definice typů

Trochu jsem se rozjel, uvidím, co bude opravdu potřeba:

typedef __memx const uint8_t memxB;     // 1B byte somewhere in FLASH or RAM
typedef __memx const char memxC;        // 1B character somewhere in FLASH or RAM
/*
 * ukazatel na ně může být buď v RAM, nebo "někde", ale musí být 3B, protože ukazuje na "někde"
 * */
typedef memxB *pB;      // 3B pointer, 1B target        pointer in RAM to Byte "somewhere"
typedef memxC *pC;      // 3B pointer, 1B target        pointer in RAM to Char "somewhere"
typedef void (*pFNC)(void);             // 2B pointer in RAM to start of function in FLASH (cannot be in RAM) - call it (*pFNC)()

typedef __memx const memxB *xpB;        // 3B pointer 1B target pointer "somewhere" to Byte "somewhere"
typedef __memx const memxC *xpC;        // 3B pointer 1B target pointer "somewhere" to char "somewhere"
typedef __memx const void (*xpFNC)(void);       // 2B pointer "somewhere"  to start of function in FLASH (cannot be in RAM) - call it (*xpFNC)()
/*
 * a ukazatele na 3B ukazatele "někde" na něco mohou opět být buď v RAM, nebo "někde"
 * */

typedef xpB *pxpB;      // 3B pointer in RAM to 3B pointer "somewhere" to Byte "somewhere"
typedef xpC *pxpC;      // 3B pointer in RAM to 3B pointer "somewhere" to Char "somewhere"
typedef xpFNC *pxpFNC;  // 3B pointer in RAM to 2B pointer "somewhere" to start of function  in FLASH - call it (*(*pxpFNC))()

typedef __memx const xpB *xpxpB;        // 3B pointer "somewhere" to 3B pointer "somewhere" to Byte "somewhere"
typedef __memx const xpC *xpxpC;        // 3B pointer "somewhere" to 3B pointer "somewhere" to Char "somewhere"
typedef __memx const xpFNC *xpxpFNC;    // 3B pointer "somewhere" to 2B pointer "somewhere" to start of function  in FLASH - call it (*(*xpxpFNC))()

/*
 * and now we can have header (two parts as we do not know len of name):
 * */
typedef struct head1 {  // {{{
        pB next;                // 3B: pointer to next header "somewhere"
        uint8_t len;            // 1B: up to 31 (=5bits), also some flags there (7+6bit)
        const char name[];      // len B:name of WORD
} head1;        // }}}
typedef __memx const head1 *xpHead1;

typedef struct head2 {  // {{{
        pFNC codepoint; // 2B: pointer to function to interpret data
        xpxpFNC data[]; // 3B: pointer to 2B pointer "somewhere" to function to interpret data - pointer to head2 "somewhere"
        } head2;        // }}}
typedef __memx const head2 *xpHead2;    // 3B pointer to head2  "somewhere"
// head2.data is array of xpHead2

typedef xpxpFNC *pxpxpFNC;      // 3B pointer in ram to 3B pointer "somewhere" to 2B pointer "somewhere" to start of function  in FLASH - call it (*(*xpxpFNC)
typedef __memx const xpxpFNC *xpxpxpFNC;

/*
 * and now we can have Instruction Pointer to data in head2 "somewhere" :
 * */
xpxpxpFNC IP;
extern xpC top_head;    // pointer to last header in asm module

datový stack

zatím takhle:

uint16_t *stack;
uint16_t pop() {return *(stack++);}
void push(uint16_t x) { *(--stack)=x;}
uint16_t peek(){ return *(stack);}

a 100 prvků stacku by mělo být dost pro každého :)

uint16_t stck[100];
void setup(){
        stack=&stck[99];        // last address in stck
        stack++;        // now we can push something and have it at top of stck

funkce pro primitiva

void f_dup(){
        push(peek());
        NEXT;
}
void f_plus(){
        push(pop()+pop());
        NEXT;
}

hlavičky slov

Protože napsat to v Céčku by byl strašný opruz, tak je to prostě asembler

.section .text
.global top_head

.global here
here:
.macro FLASH_PTR addr   ; {{{
        .word \addr
        .byte 0x80
.endm   ; }}}
.macro DEFWORD lbl,next, attr, name, codeword   ; {{{
\lbl\():
        FLASH_PTR \next
        .byte len\@ + \attr
        name_start_\@:
        .ascii "\name"                                  ; name without \0
        .equ len\@,(. - name_start_\@)
\lbl\()_cw:
        .extern \codeword
        .word \codeword                                 ; 2B address of function in FLASH
.endm   ; }}}

DEFWORD w_plus,0,0, "+", f_plus
DEFWORD w_dup, w_plus, 0, "DUP", f_dup
DEFWORD w_double, w_dup, 0, "DOUBLE", f_docol
                        FLASH_PTR w_dup_cw
                        FLASH_PTR w_plus_cw
                        FLASH_PTR f_exit
.EQU    top_head, w_double