SDS-C: SCB

Tato stránka popisuje programovací jazyk SDS-C, který je dostupný na vybraných zařízeních SDS. 
Některá zařízení SDS používají FULL-C, pro který máme návody jinde na této WiKi.


Soubor .SCB

Soubor .SCB obsahuje výstup překladu vašeho zdrojového kódu jazyka SDS-C.

Provedení překladu pomocí aplikace SDSC.exe v příkazovém řádku:

C:\SDSC\SDSC.EXE compile test.c

Tento příklad vezme zdrojový kód v jazyce SDS-C v textovém souboru test.c , provede překlad, a vygeneruje binární soubor test.c.SCB .

Soubor SCB lze přímo poslat pomocí HTTP POST do všech zařízení SDS řady ST.


Formát souboru .SCB

Soubor se skládá z datových bloků o typicky pevné velikosti (jen poslední blok může být kratší).

Na začátku každého bloku je hlavička, a následuje datový obsah.

Bloky jsou těsně na sobě, za sebou, uspořádány v SCB souboru. Obsahem SCB souboru není nic jiného než tyto bloky.

Do SDS se soubor SCB přenáší kompletně v původním stavu, tak jak je zapsán na disku, beze změn. Modul SDS provádí interně kontrolu přijatého obsahu souboru SCB, a při jakékoliv odchylce celý soubor odmítne.

Obsah souboru SCB, který obsahuje "n" bloků:

BLOK 0
BLOK 1
...
BLOK n-1

Počet bloků ("n") je určen překladačem, a prakticky (logicky) odpovídá velikosti programu.

Interní datový obsah každého bloku tvoří jednotlivé části přeloženého SDS-C programu, viz dále.

Nejprve je potřeba si popsat hlavičku každého bloku:

OBSAH BLOK x

 offset | length  |  detail
 -------+---------+------------------------------------------
   0    |     2 B |   a general block position ("bpoz")
   2    |     1 B |   reserved (by default set to 0xAA)
   3    |     1 B |   block 8-bit checksum ("c")
   4    |     2 B |   block length ("blen")
 -------+---------+------------------------------------------
   5    |  blen B |   internal data   

Každý blok má svou pozici - číslování začíná na nule. Podmínkou je umístit bloky do souboru SCB přesně za sebou, tak jak se jejich pozice zvyšuje. Stejně tak jsou zasílány do SDS. Pokud SDS detekuje neplatný (očekávaný) údaj pozice, je celé nahrávání SCB přerušeno a zamítnuto.

Délka bloku "blen" musí být v rozmezí 0 až 264 (včetně). Typicky všechny bloky mají délku 264 bajtů, s možnou vyjímkou jen pro úplně poslední blok, který může být jako jediný kratší.

Interní Data bloku jsou už samotným kouskem obsahu výsledku překladu SDS-C. Překladač tedy vytvoří celý binární obsah, který je rozkouskouván do bloků (kterým je přidána hlavička). Po nahrání SDS-C do modulu SDS dojde k zahození hlavičky, úpravy a kontrole interních dat a jejich celkovému opětovnému sloučení (složeny pěkně za sebou bez přerušení). To co si SDS v sobě uchová jsou už vyčištěná interní data, tedy "binární kód SDS-C".

Každý blok má svá Interní Data jednoduše upravena, což má umožnit jejich kontrolu a detekovat chybu při přenosu. Nejedná se o šifrování (to je v plné míře a správným způsobem aplikováno až u FULL-C), ale o jednoduchou logickou operaci zaměřenou na detekci bitových chyb.

Algoritmus úpravy (je už obsažen v SDSC.EXE a jeho výsledek je tedy už obsahem .SCB souboru) je celkově jednoduchý.

uint32_t i;
uint8_t yx, ck;
if (blen > 264)
{
  error: invalid blen -> exit
}
yx = c ^ 0x27;
ck = 0;
for (i = 0; i < blen; i++)
{
  block_internal_data[i] ^= yx;
  ck += block_internal_data[i];
}
if (ck != c)
{
  error - checksum not valid -> exit
} else
{
  checksum for this block IS valid -> continue to next block
}

Tento pseudokód je v principu obousměrný, lze jej použít jak pro vytvoření zabezpečeného bloku, tak pro ověření zabezpečení a odstranění tohoto zabezpečení.

Protože se vždy předpokládá přenos přes IP vrstvu (UDP, nebo TCP pro HTTP POST), není v tomto případě zabezpečení na této úrovni dále zvyšováno (v rámci IP vrstvy jsou pakety kontrolovány pomocí CRC32, což pro takto malou datovou dávku je dostatečné).


Postupy

Postup při překladu:

+-------------+                                                                                                                  +-------------+
| source code |                              +-------------+                                                                     | output file |
|             | ------->  COMPILER  -------> | binary code | --> SPLIT TO BLOCKS -> MODIFY INTERNAL DATA -> WRITE ALL BLOCKS --> |             |
|  test.c     |                              +-------------+                        FOR EACH BLOCK          TO .SCB              |  test.c.SCB |
+-------------+                                                                     + ADD HEADERS                                +-------------+

Postup při nahrávání do SDS:

+---------------+                                                                                                            
| compiled file |                               
|               | ------->  HTTP POST  -------> CHECK BLOCK HEADERS -> STRIP HEADERS -> UNMODIFY INTERNAL DATA -> WRITE INTERNAL DATA TO DATAFLASH --> RUN SDS-C
|  test.c.SCB   |          (authorized)                                                                           (= SDS-C BINARY CODE)
+---------------+                                                                                                            

Lze vidět že proces je přímočarý a z hlediska klienta (ten kdo nahrává do SDS) jednoduchý - pošle se obsah .SCB souboru beze změny, tak jak je na disku 1:1 do SDS.


Binární kód SDS-C

Binární kód je datový blob, který je výstupem překladu zdrojového kódu jazyka SDS-C. Tento binární kód je různým způsobem transportován do SDS, jeden za způsobů (rozklad do bloků a jejich zabezpečení) je popsán výše.

Původní SDS které prováděli přeložený SDS-C program byly založeny na tehdejším mimořádně omezeném dostupném hardware, zejména z hlediska pracovní paměti (RAM v desítkách kB). Přesto se povedlo do takového prostoru dostat všechny funkce SDS a k tomu provádění SDS-C programu. Tuto informaci uvádím primárně pro to, že vysvětluje celou řadu návrhových rozhodnutí, které jsou zde dále popsány. Nešlo si dovolit plýtvat pamětí a místem, a mnohdy se hledal kompromis. Teprve FULL-C tato omezení nemá a je plnohodnotný (včetně plného kryptografického zabezpečení).

Pokud nás zajímá přímý výstup překladu, tedy ještě před předáním do transportu (tedy např. rozklad na bloky do .SCB), tak je popis zde.

MASTER HEADER
 [ header ]
STRINGS HEADER
 [ OFSLEN0, OFSLEN1, ... OFSLENn ]
STRINGS BLOB
 [ STR0, STR1, ... STRn ]
PROCEDURES (CODE BLOBS)
 [ TYPE, NUMBC, BYTECODE[] ]

Tyto čtyři základní binární části jsou umístěny těsně za sebou, a jako celek tvoří spustitelný přeložený SDS-C program, který spustí a provede kterékoliv SDS První Produktové Řady.

Naprosto všechny prvky jsou zapsány jako 16bit slova (v případě potřeby uložit do bytecode např. konstanty o větší bitové šířce, je to provedeno kombinací více 16bit slov).

Jedná se plně kustomizovaný způsob zápisu binárního kódu programu, který privátně vytvořil vývoj AN-D a který je použit jedině pro moduly SDS (OnlineTechnology).


MASTER HEADER

Základní hlavička obsahuje odkazy na jednotlivé další hlavní prvky v rámci celého binárního obsahu.

MASTER HEADER

 offset |  length  |  detail
 -------+----------+------------------------------------------
   0    |      2 B |   total binary size
   2    |      2 B |   offset -> STRINGS HEADER
   4    |      2 B |   offset -> STRINGS BLOB
   6    |      2 B |   STRING BLOB size
   8    |      2 B |   STRINGS count
  10    |      2 B |   PROCEDURE count
  12    |      2 B |   VARIABLES count
  14    | 96 x 2 B |   offset -> PROCEDURE "n"

Hodnota 96 (konstanta) udává aktuální maximální počet procedur v rámci dané verze SDS-C.

Návrh hlavičky ukazuje i další podstatnou informaci - a to maximální velikosti celého přeloženého SDS-C programu, která nesmí překročit 64kB. To bylo opět dáno tehdejším hardware a jeho možnostmi. Proto vznikl jazyk FULL-C, který tato omezení nemá.


STRINGS HEADER

Pro každý (překladem detekovaný) unikátní text, je proveden jeho zápis do STRING BLOBu, a informace o pozici v rámci STRING BLOBu je umístěna právě v hlavičce (STRINGS HEADER).

Pozice textu v BLOBu je současně jeho referencí "sr" (platná hodnota: 0 až "STRINGS count" - 1 ), na kterou se pak odkazuje výkonný kód programu.

Obsahem této hlavičky je tedy pole o následujících dvou položkách:

 offset       |  length  |  detail
 -------------+----------+------------------------------------------
  sr*4 + 0    |      2 B |   offset to blob
  sr*4 + 2    |      2 B |   string length (without 0x00)

Pozor, je zcela platné, aby offset jednoho stringu byl namířen do části jiného. Příklad: mějme dva stringy: "hello" , "lo". Pro tyto dva nemá význam dělat dva samostatné záznamy, když můžeme druhý string "lo" vzít z předchozího (když už v paměti je). Překladač umí i další pokročilejší funkce sestavení celého STRING BLOBu, kde setřídí všechny stringy tak, aby celý blob měl co nejmenší velikost.


STRINGS BLOB

Všechny extrahované texty (stringy) z programu jsou uloženy v jednom společném binárním blobu. Každý jednotlivý string je zakončen 0x00, a celkově tyto stringy na sebe navazují. Pomocí STRINGS HEADER dokaže SDS rychle najít odkaz do tohoto STRINGS BLOBu na každý jednotlivý string (program si v bytecode udržuje reference do STRINGS HEADER, odkud se vezme offset do STRINGS BLOB a následně se už přečte požadovaný text).

Klíčové je použití pokročilého algoritmu pro sestavení STRINGS BLOBu tak, aby se nalezly všechny opakující se, a jakkoliv vnořené texty (a jejich nejvýhodnější kombinace) s cílem získat STRINGS BLOB co nejmenší. Toto bylo klíčové zejména pro úplně první typy modulů SDS kde byl program v malé EEPROM paměti (původně jen 2kB ! a přesto se to povedlo).


PROCEDURES

Bytecode je vždy uložen v jednotlivých funkcích (PROCEDURES). Při vykonávání SDS-C programu provádí SDS právě jednotlivé procedury, tedy jejich vnitřní obsah (bytecode). SDS vždy začne od procedury označené názvem jako "main" (toto identifikuje překladač a nastaví potřebný typ dále do binárního kódu). Jednotlivé procedury mohou ze svého bytecode zavolat jiné procedury, které se po svém provedení vrátí hned za místo původního volání (klasický očekávaný způsob běhu programu).

Tento blob tedy obsahuje 1 až 96 (dle verze SDS-C) sub-blobů, které mají vždy stejný design (logicky se liší jen samotným obsahem v položkách).

Offset na každou proceduru lze přečíst z MASTER HEADER.

 offset  |  length           |  detail
 --------+-------------------+------------------------------------------
    0    |               2 B |   procedure type
    2    |               2 B |   numbytecode 
    4    | numbytecode * 2 B |   BYTECODE ( 16bit words )


Hodnoty v "procedure type":

   0 = default
   1 = reserved (procedure ignored)
   2 = init()
   3 = reserved (procedure ignored)
   4 = main()

Je úkolem překladače identifikovat funkce init() a main() a nastavit pro jejich bytecode správný typ.

Bytecode je složen z (v hlavičce) uvedeného počtu 16bit slov. Tento bytecode je postupně vykonáván, od začátku až po konec procedury. Pokud se dojde na konec procedury, je procedura považována za ukončenou, a přejde se zpět na proceduru která tu vykonávanou zavolala. Pozor - pokud se jedná o funkci main(), tak zde dojde k opětovnému spuštění funkce main().

Z celého popisu je nejsložitější popis bytecode samotného. TBD.

Konec