Kompilátor C51



V tomto textu jsou popsány jen nejdůležitější vlastnosti kompilátoru, zvláště jeho odlišnosti od normy ANSI. V převážné většině případů bude vyhovovat jeho implicitní nastavení a práce s ním bude jednoduchá. V některých případech však bude vhodné, nebo dokonce nutné, využít jeho speciální vlastnosti. Ty, které se pak nejpravděpodobněji využijí, jsou popsány v dalším textu. Je třeba upozornit, že text nemůže nahradit podrobnou příručku, do které bude někdy nutné nahlédnout.

Ovládání kompilátoru

Vstup: zdrojový_program.C
Výstupy: Soubory .obj a .lst o stejném jménu jako zdrojový_program se vytvářejí automaticky.

Vyvolání: C51 zdrojový_program.C direktiva1 direktiva2 ...

Činnost kompilátoru lze ovlivňovat nepovinnými direktivami, které se mohou uvést buď na konci příkazového řádku (jak je výše naznačeno), nebo ve zdrojovém textu za vyhrazeným slovem # pragma.

#pragma direktiva1 direktiva 2 ....

Až na výjimky se každá direktiva smí objevit ve zdrojovém textu jen jednou. Nerozlišují se velká a malá písmena. Pokud nejsou při překladu použity, neprovádí se daná akce nebo jsou dosazeny implicitní stavy. Dále jsou uvedeny nejdůležitější direktivy:

Direktivy kompilátoru

CODE v listingu bude uveden i překlad do asembleru.

 

Pro běžné případy se jako vhodné jeví vyvolání kompilátoru příkazem:

C51 zdrojový_program.C CODE SYMBOLS

Zvláštní vlastnosti a omezení C51

Běžné datové typy mají následující délky (byte): char (1), int (2), long (4), float (4), double (4 !), pointer (3). Typ double se neliší od float a je zde jen pro slučitelnost s normou pro C. Do paměti jsou proměnné ukládány nejvyšším byte napřed. To je opačně, než u PC ! Proto pozor při přejímání programů, kde se pracuje s daty prostřednictvím peek a poke.

V důsledku zvláštního hardware existuje nový datový typ bit, samozřejmě bez atributu signed či unsigned. Jednobitové proměnné se deklarují jinak obvyklým způsobem, např:

static bit bitová_proměnná;

V deklaraci nelze uvádět oblast uložení, neboť je dána konstrukcí obvodu (bity 0 až 7FH jsou v interní RAM na adresách byte 20H až 2FH). S bity lze pracovat i ve funkcích jako s parametry a návratovými hodnotami. Na tento typ však neexistuje pointer a nelze z něj vytvářet pole.

Pro úsporu datové paměti je typ double dlouhý jen 4 byte, takže stejně jako float. Ostatní typy a jejich délky souhlasí se standardem C. Pro úsporu paměti lze doporučit používání co nejkratších typů. Pro zkrácení programu je lépe používat unsigned.

Pointer má délku 3 byte. Prvý byte udává oblast uložení proměnné (1=IDATA, 2=XDATA, 3=PDATA, 4=DATA, 5=CODE), druhý byte adr. H, třetí byte adr. L. Pokud se pointer v programu nastavuje na absolutní adresu (číslo), musí se naplnit číslem typu long - např. 0x20000L. Zde 2 znamená, že proměnná, na kterou ukazuje pointer, je v oblasti XDATA. Číslo 0000 je adresa.

Oblasti uložení proměnných souvisejí s vnitřním uspořádáním mikropočítače.

Oblasti uložení se mohou použít v deklaracích proměnných, např.:

static data unsigned int var_1; /* proměnná var_1 je ve vnitřní RAM, přímo adres. */

Oblast uložení se nemusí určovat. Pak platí způsob uložení v souladu s modelem (SMALL, COMPACT, LARGE). Je-li však určena, a byl-li určen model, má deklarace přednost před tím, co plyne z modelu. U větších modelů je tedy značná vůle v uložení proměnných. U modelu LARGE jsou proměnné, deklarované uvnitř funkcí, ukládány automaticky vždy jako XDATA.

Zvláštním hardwarovým útvarem jsou speciální funkční registry (SFR). Ty lze v případě potřeby deklarovat. Všechny registry běžných typů mikropočítačů z rodiny 8x51 jsou však již deklarovány v souborech nazvaných REG.... .H, které se připojí k programu příkazem:

#include <reg..... .h>

Nové deklarace mohou být zapotřebí pro nové nebo modifikované typy mikropočítačů. U některých SFR je možné adresovat jednotlivé bity. I tyto deklarace jsou již připraveny v souborech reg... .h pro běžné verze mikropočítačů, a není třeba se jimi zabývat. Je jen třeba se důsledně držet standardních pojmenování SFR i bitů.

Ve vnitřní paměti RAM existují 4 banky registrů, každá po 8 registrech. Banku lze přepnout zásahem do slova PSW. Kompilátor to umožňuje příkazem using n v definici funkce. Číslo n může nabývat hodnot 0 až 3, a určuje číslo registrové banky. Např.:

void funct (........) using 3;

Normálně se po vstupu do funkce neukládá PSW. Uvedení using má ten efekt, že po vstupu do funkce se ihned uloží PSW do zásobníku a pak se přepíší v PSW bity, určující banku registrů. Před návratem z funkce se PSW obnovuje ze zásobníku. Using nelze použít u funkcí deklarovaných jako extern. Existuje též nežádoucí efekt přepnutí banky u funkcí, které vrací hodnotu v registrech (což je běžné) - při návratu z funkce je již přepnuta banka a na předpokládané adrese je jiný registr. Je vhodnější využívat pro předávání hodnot spíše globální proměnné.

Interruptové funkce mají v definici za identifikátorem uvedeno vyhražené slovo interrupt n, kde n je číslo interruptu (0 až 15). Interruptové funkce se od obyčejných funkcí liší tím, že na jejich začátku se automaticky ukládá do zásobníku obsah ACC, B, DPH a DPL, PSW. Při návratu se obsahy opět vracejí ze zásobníku a funkce se vrací pomocí RETI. Vektory, ukazující na začátky interruptových funkcí, jsou v programové paměti na adresách 8*n + 3. Pro každou interruptovou funkci kompilátor automaticky vyplní vektor podle skutečné adresy začátku funkce. Slovo interrupt n lze kombinovat s using n v libovolném pořadí. Přepnutí na jinou banku registrů je v interruptových funkcích zvláště vhodné. Interruptové funkce mají omezení v tom, že nemohou mít parametry, nemohou vracet hodnotu, nelze je normálně volat, a nesmějí být typu extern. V deklaraci (prototypu funkce) se slovo interrupt n nebo using n neuvádí. Příklad definice:

void intfunct (void) interrupt 2
{ ..... tělo funkce ..... }

void intfunct (void) interrupt 2 using 1
{ ..... tělo funkce .....}

 

Omezení kompilátoru co se týká struktury programu jsou minimální:

 

Standardní funkce

Standardní funkce jsou uloženy ve standardních knihovnách C51S.LIB, C51C.LIB, C51L.LIB, C51FPS.LIB, C51FPC.LIB, C51FPL.LIB. Z nich se automaticky vybere ta, která je určena pro daný model mikropočítače (SMALL, COMPACT, LARGE) a pro typy proměnných ve výrazech (integer nebo float). Knihovní funkce jsou při překladu chápány jako funkce externí a do programu skutečně z knihoven doplněny až při spojování linkerem. Na začátku zdrojového textu je však nutno uvést #include <hlavičkový_soubor.H>. Tento soubor obsahuje prototypy funkcí nebo maker. Pro každou skupinu funkcí existuje patřičný include soubor, tak jak je dále uvedeno:

ABSACC.H: Makra pro přímý přístup k proměnným.

Přímý přístup k byte:

Makro DBYTE je definováno jako (unsigned char *) 0x040000 L. Ostatní obdobně, ale s jiným prvým byte pointeru.

Přímý přístup ke slovu:

Makra jsou definována jako (unsigned int *) ....... Celá konstrukce pracuje s adresami jako s indexy pole. Pro BYTE je skutečná adresa v paměti rovna indexu, ale pro WORD je dvojnásobkem indexu !

Příklady použití:

prom = XWORD[0x1000]; /* obsah z vnější RAM, adr. 0x2000, do prom. int */
DBYTE[0x20] = 35; /* naplnění vnitřní RAM, adr. 0x20, číslem 35 */

 

STDIO.H: Standardní vstupní/výstupní funkce.

Pro vstup a výstup znaků se využívá sériový kanál, který je nutné předem naprogramovat. Jako příklad je ukázáno naprogramování na 2400 Bd při krystalu 12 MHz:

SCON = 0x52;
SMOD = 0;
TMOD = 0x20;
TH1 = 0xF3;
TR1 = 1;

char _getkey (void);
char getchar (void);
char ungetchar (char);
char putchar (char);
int printf (const char *, ...);
int sprintf (char *, const char *, ...);
char *gets (char *, int n);
int scanf (const char *, ...);
int sscanf (char *, const char *, ...);
int puts (const char *);

STDLIB.H: Standardní funkce.

float atof (void *s1);
long atol (void *s1);
int atoi (void *s1);
int rand ();
void srand (int);
void *malloc (unsigned int size);
void free (void xdata *p);
void init_mempool (void xdata *p, unsigned int size);
void *realloc (void xdata *p, unsigned int size);
void *calloc (unsigned int size, unsigned int len);

MATH.H: Matematické funkce.

char cabs (char val);
int abs (int val);
long labs (long val);
float fabs (float val);
float sqrt (float val);
float exp (float val);
float log (float val);
float log10 (float val);
float sin (float val);
float cos (float val);
float tan (float val);
float asin (float val);
float acos (float val);
float atan (float val);
float sinh (float val);
float cosh (float val);
float tanh (float val);
float atan2 (float y, float x);
float ceil (float val);
float floor (float val);
float modf (float val, float *n);
float pow (float x, float y);

CTYPE.H: Funkce pro práci se znaky.

bit isalpha (char);
bit isalnum (char);
bit iscntrl (char);
bit isdigit (char);
bit isgraph (char);
bit isprint (char);
bit ispunct (char);
bit islower (char);
bit isupper (char);
bit isspace (char);
bit isxdigit (char);
char tolower (char);
char toupper (char);
char toint (char);

STRING.H: Funkce pro práci s řetězci.

char *strcat (char *s1, char *s2);
char *strncat (char *s1, char *s2, int n);
char strcmp (char *s1, char *s2);
char strncmp (char *s1, char *s2, int n);
char *strcpy (char *s1, char *s2);
char *strncpy (char *s1, char *s2, int n);
int strlen (char *);
char *strchr (const char *s, char c);
int strpos (const char *s, char c);
char *strrchr (const char *s, char c);
int strrpos (const char *s, char c);
int strspn (char *s, char *set);
int strcspn (char *s, char *set);
char *strpbrk (char *s, char *set);
char *strrpbrk (char *s, char *set);
char memcmp (void *s1, void *s2, int n);
void *memcpy (void *s1, void *s2, int n);
void *memchr (void *s, char val, int n);
void *memccpy (void *s1, void *s2, char val, int n);
void *memmove (void *s1, void *s2, int n);
void *memset (void *s, char val, int n);

 

Spojovací program (linker)

Zde jsou popsány jen nejdůležitější vlastnosti linkeru. Linker má dvě úlohy:

Vstup: soubor nebo soubory .obj nebo .lib
Výstup: absolutní soubor. Pokud není stanoveno jinak, nemá příponu.
soubor .M51 . Je to mapa paměti s informacemi o umístění segmentů, symbolech a jejich spojeních.

Vyvolání: L51 soubor_1.obj, soubor_2.obj, ... příkaz_1 příkaz_2 ....

nebo L51 příkazový_soubor

V prvém případě je nutné zadat z klávesnice celý řádek, který může někdy přesáhnout přípustnou délku (128 pro DOS). Pak lze přidávat další pokračovací řádky znakem & <enter>. Ve druhém případě je příkazový řádek zapsán ve zvláštním příkazovém souboru, což je výhodné pro dávkové zpracování (batch).

Spojovací program automaticky přidává standardní knihovny C51S.LIB, C51C.LIB, C51L.LIB, C51FPS.LIB, C51FPC.LIB, C51FPL.LIB podle deklarovaného modelu mikropočítače (který musí ve všech vstupních modulech samozřejmě být stejný - SMALL, COMPACT, LARGE) a podle typu proměnných ve výrazech (integer nebo float). Lze záměrně přidat i jiné knihovny.

Jména výstupních souborů budou shodná se jménem prvního vstupního souboru. Ve výstupním absolutním souboru budou jednotlivé programové moduly seřazeny tak, jak byly zadávány vstupní soubory.

Řídící příkazy linkeru

Řídící příkazy se nemusí uvádět. Pak linker používá implicitní stavy, které jsou pro většinu případů vyhovující. Dále jsou uvedeny jen nejdůležitější příkazy:

Adresy se zadávají jako šestnáctková čísla (....H), dekadická čísla (...D nebo nic), výjimečně i jako binární čísla (.........B). Žádný z výše uvedených příkazů se nemusí použít. Pak nastupují implicitní hodnoty.

 

Konverzní program OHS51

Program provádí konverzi z absolutního souboru, vytvořeného linkerem, do souboru ve formátu Intel HEX.

Vstup: program
Výstup: program.hex

Vyvolání: OHS51 soubor

Na konci mohou být doplněny příkazy, modifikující činnost konverzního programu. Pro běžnou práci však nejsou zapotřebí.

Formát Intel HEX

Používá se k přenosu absolutních souborů do programátoru pamětí, často též jako vstup pro softwarový simulátor nebo hardwarový emulátor.

Celý soubor je rozdělen na krátké úseky, které mohou být zpracovány jako samostatné jednotky. V každém byte HEX souboru se přenáší v ASCII kódu jedna šestnáctková číslice (proto formát "HEX"). Číslice 0 až 9 tak odpovídají kódu 30H až 39H, šestnáctkové číslice A až F pak 41H až 46H. Vždy dva ASCII znaky tvoří dvojici, kterou tak lze přenést čísla 00 až FF. V původním binárním souboru tvořilo 8 bitů jeden byte dat. V HEX formátu je jeden byte původních dat přenášen dvěma osmibitovými ASCII znaky. Datové pole o n datových byte vyžaduje tedy 2n znaků ASCII.

Skladba úseku:

Adresa pro uložení datového pole, která je součástí úseku, umožňuje uložit každé pole nezávisle na ostatních.