Základy programování v 8051

Z MediaWiki SPŠ a VOŠ Písek
Skočit na navigaci Skočit na vyhledávání

Úvod do programování jednočipových mikropočítačů

Jednočipový mikropočítač řady 8051 je v současné době nepsaným standardem v mikroprocesorové technice. Jeho architektura a instrukční soubor se staly základem pro celou řadu vyšších typů jednočipových mikropočítačů.

Úkolem této části je popis elementárních programátorských technik a postupů, které je nutno zvládnout pro programování konkrétních aplikací. Předpokládá se alespoň základní znalost architektury 8051, jeho instrukcí a používání překladače.


Přesuny dat a zápis konstant

Přesuny dat v paměti procesoru provádí instrukce:

       MOV     <oper1>,<oper2>

Začneme od nejjednoduššího příkladu, a to je zápis konstanty do registru.

       MOV   A,#12

Po provedení této instrukce se naplní obsah registru A číslem 12. Zde je nutno upozornit na znak '#', který je uveden před číslem 12. Tento znak je v assembleru 8051 velmi důležitý, odlišuje totiž zápis konstanty od zápisu adresy neboli tzv.přímé hodnoty, což je buď SFR registr nebo oblast vnitřní RAM. Špatné použití znaku '#' (ať už jeho vynechání či nadbytečnost) má za následek vznik těžko zjistitelných chyb a způsobuje záhadné chování programu.

 Příklad:
               MOV     R0,#10          ;zápis 10 do registru R0
               .
               .
               MOV     A,#0            ;zápis 0 do A
               .
               .
               MOV     A,0             ;zápis 10 do A !!!!

Ve druhém případě použití registru A (třetí instrukce programu) nedošlo k jeho vynulování, ale k přesunu přímé hodnoty z adresy 0 do registru A. Protože na adrese 0 ve vnitřní paměti RAM leží registr R0, dojde k přepisu jeho hodnoty do registru A.

Instrukce MOV má má široké spektrum parametrů a je možno ji použít ve všech adresovacích módech.

               MOV     R0,#20H         ;počáteční adresa ukládání
               MOV     B,#8            ;počet průchodů smyčkou
       CYKL:   MOV     @R0,P1          ;načtení portu P1
               INC     R0              ;zvyš ukazatel
               DJNZ    B,CYKL          ;proveď celkem 8x

Uvedený příklad ilustruje použití instrukce MOV při nepřímém adresování. Program 8x sejme hodnotu portu P1 a uloží ji postupně na adresy 20H až 27H do vnitřní paměti RAM.

Modifikace tohoto příkladu pro uložení hodnot do vnější paměti dat vypadá následovně:

               MOV     R0,#20H         ;počáteční adresa ukládání
               MOV     B,#8            ;počet průchodů smyčkou
       CYKL:   MOV     A,P1            ;načtení portu P1
               MOVX    @R0,A           ;přesun do vnější paměti
               INC     R0              ;zvyš ukazatel
               DJNZ    B,CYKL          ;proveď celkem 8x

Pro adresování vnější paměti dat je nutno použít instrukci MOVX (Move External - přesun z vnější paměti).

Všechny uvedené příklady zatím přesouvaly data v paměti údajů (ať už vnitřní, či vnější). Pro přesun dat z paměti programu slouží instrukce MOVC. Následující příklad ukazuje použití MOVC (Move Constant - přesun z pevné paměti) při přenosu bloku dat (např.tabulky) z paměti programu do paměti dat:

               ORG     0

               MOV     DPTR,#TAB       ;zápis adresy tabulky
               MOV     R7,#TAB_END     ;zápis délky tabulky do R7
               MOV     R0,#20H         ;tabulka se bude ukládat
                                       ;od adresy 20H ve vnitřní RAM
       CYKL:   MOV     A,#0            ;nulování A
               MOVC    A,@A+DPTR       ;přesun jednoho prvku tab.
               MOV     @R0,A           ;do A a odtud do RAM
               INC     R0              ;zvyš ukazatel do RAM
               INC     DPTR            ;zvyš ukazatel do ROM
               DJNZ    R7,CYKL         ;opakuj přes celou délku
                                       ;tabulky

               ORG     300H            ;umístění tabulky
       TAB:    DB      1,2,3,4,5       ;jednotlivé prvky tabulky
               DB      6,7,8,9,10      ;
       TAB_END EQU     $-TAB           ;výpočet délky tabulky
                                       ;udělá překladač


V programu je použita instrukce MOV DPTR,#TAB, která do registru DPTR načte adresu tabulky. Instrukce s těmito parametry pracuje jako jediná se šestnácti bity, všechny ostatní parametry instrukce MOV pracují s osmibitovými údaji.

Poznámka:

Poslední řádek příkladu ukazuje jeden ze způsobů použití pseudoinstrukce EQU pro výpočet délky tabulky. Kdybychom místo posledního řádku uvedli:

       TAB_END EQU     10

program by pracoval stejně, ale při jakékoli změně délky tabulky bychom tento údaj museli neustále počítat a měnit (což může zvláště u delších tabulek vést k chybám). Postupem uvedeným v příkladě přenecháme starost o výpočet délky tabulky překladači.

Pro přesun údajů je možno použít také instrukci výměna:

    XCH A,<parametr>

která vymění obsah registru A s druhým parametrem. Takže sekvenci instrukcí:

<source lang="asm">              ;výměna obsahu A, R0
               MOV     B,A
               MOV     A,R0
               MOV     R0,B


je možno nahradit jedinou instrukcí:

               XCH     A,R0



Používání zásobníku

Zásobník je část paměti v oblasti vnitřní RAM. Je definován svým počátkem a může ležet kdekoli ve vnitřní oblasti RAM. V oblasti SFR registrů je definován ukazatel zásobníku - registr SP. Tento registr ukazuje vždy na vrchol zásobníku. Při ukládání dat na zásobník se nejprve hodnota SP registru zvýší o 1 - zásobník roste směrem do oblasti vyšších adres vnitřní RAM - a potom se data uloží tam, kam ukazuje SP. Při vybírání dat ze zásobníku je postup opačný - nejprve se uloží data, adresovaná SP a potom se SP sníží o 1.

Nastavení ukazatele zásobníku a starost o to, aby nedošlo ke kolizi zásobníku s ostatními údaji v RAM je zcela na zodpovědnosti programátora. Vždy je nutno pro zásobník vyhradit dostatečně velkou část paměti tak, aby mohl pojmout návratovou adresu i toho nejvíce vnořeného podprogramu. U systému s přerušením je nutno počítat s tím, že přerušena může být libovolná část programu, takže kapacita zásobníku musí být navržena s rezervou i pro tento případ.

Po resetu mikropočítače se hodnota SP nastaví na 7.

Zásobník je využíván pro odkládání návratových adres při volání podprogramů nebo při přerušení (viz bod 7).

Pro ukládání a vybírání dat ze zásobníku slouží instrukce:

       •  ulož                        PUSH    <parametr>
       •  vyjmi                       POP     <parametr>

Nejtypičtějším použitím zásobníku je přechodné ukládání mezivýsledků pro pozdější zpracování nebo pro úschovu pracovních registrů při vstupu do procedury.

Příklad:       ACALL   PROC    ;volání procedury
               .
               .

         PROC: PUSH    PSW     ;úschova PSW
               PUSH    ACC     ;úschova A
               PUSH    B       ;úschova B
               .
               ;nyní mohu v proceduře používat registry A,B
               ;před návratem z procedury je jejich obsah obnoven
               .
               .
               POP     B       ;obnovení B
               POP     ACC     ;obnovení A
               POP     PSW     ;obnovení PSW
               RET

Logické operace, posuvy a rotace

Logické operace

V instrukčním souboru 8051 jsou následující instrukce pro logické operace:

       •  logické nasobení                ANL   <oper1>,<oper2>
       •  logické sečítání                ORL   <oper1>,<oper2>
       •  log.exkluzivní součet           XRL   <oper1>,<oper2>

Uvedené instrukce provádějí příslušnou logickou operaci mezi dvěma operandy <oper1>,<oper2> po jednotlivých bitech a výsledek se uloží do prvního operandu <oper1>.

Typickým příkladem použití instrukce ANL je vynulování jednotlivých bitů operandu. Používá se v těch případech, kdy potřebuji část operandu vynulovat.

Příklady:

                      ;nulování bitu 0..3 registru B
                      ANL     B,#11110000B
                      ;nulování bitů 0,2,4,6 registru A
                      ANL     B,#10101010B
                      ;nulování celého registru B
                      ANL     B,#0

Této operaci se říká maskování - operand se logicky vynásobí s tzv.maskou - v našem příkladě 11110000B,10101010B nebo 0. Ty bity prvního operandu, které se vynásobí s nulou v příslušném bitu druhého operandu (masce), jsou vynulovány (vymaskovány).

Příklad:

V registru R0 máme dvě BCD číslice. Úkolem programu bude tyto dvě číslice uložit do registrů R1 a R2, každou zvlášť. Tedy například:

               R0=15H  —————>  R1=01H, R2=05H

Poznámka: BCD tvar číslic je způsob uchovávání číslic ve tvaru, kdy v jednom bytu jsou umístěny dvě číslice (v našem příkladě číslice 1 a 5.

              MOV     A,R0           ;přesun do A
              ANL     A,#0F0H        ;nulování spodní části byte
              MOV     R1,A           ;uschovej
              MOV     A,R0           ;znovu přesun
              ANL     A,#0FH         ;nulování horní části byte
              SWAP    A              ;zaměň horní a dolní část
              MOV     R2,A           ;registru A a ulož


Instrukce SWAP A provede záměnu horní a dolní části registru A. Je-li obsah registru A před provedením instrukce SWAP např. 56H, po provedení této instrukce se obsah registru A změní na 65H.


Instrukce ORL provádí logický součet (po bitech operace OR) dvou operandů. Časté použití této instrukce je pro nastavení příslušných bitů daného operandu do 1.

Příklad:

              ;nastavení bitu 0 registru A do jedničky
              ORL     A,#1
              ;nastavení bitů 5,6,7 do jedničky
              ORL     A,#11100000B

Poznámka: Pro nastavení, nulování a negaci jednotlivých bitů existují speciální instrukce, které jsou popsány v následující kapitole.

Instrukce XRL provádí výhradní logický součet operandů (po bitech operace XOR) a výsledek ukládá do prvého operandu. Praktické využití této instrukce je v negaci vybraných bitů daného operandu.

Příklad: Přečtěte z portu P0 hodnotu, spodní čtyři bity vynulujte, horní čtyři bity invertujte (negujte) a výsledek vyšlete na port P1.

              MOV     A,P0            ;načtení z portu P0 do A
              ANL     A,#0F0H         ;nulování spodních bitů
              XRL     A,#0F0H         ;invertování horních bitů
              MOV     P1,A            ;vyslání na port P1


Posuvy a rotace:

Pro rotace jsou v instrukčním souboru 8051 instrukce:

       •  rotace doleva                       RL      A
       •  rotace doleva přes C                RLC     A
       •  rotace doprava                      RR      A
       •  rotace doprava přes C               RRC     A

Posouvat (rotovat) operand je možno pouze v akumulátoru. Rotace mají široké použití v aritmetických programech (viz. kap.6). Ukážeme si další typické použití - vysílání tzv. pochodující nuly na port. Pochodující nula znamená, že na jeden port za sebou postupně vyšleme hodnoty:

                              11111110
                              11111101
                              11111011
                              11110111
                              11101111
                              11011111
                              10111111
                              01111111

Jak je vidět z tabulky, hodnoty, které se vysílají na port se liší pouze v pozici nuly - nula pochoduje přes celý port. Tato technika se používá při obsluze klávesnice - viz část II, zadání 19.

              MOV     R0,#8           ;počítadlo cyklů
              MOV     A,#11111110B    ;první hodnota do A
      CYKL:   MOV     P1,A            ;vyslání na port
              RL      A               ;rotace akumulátoru doleva
              DJNZ    R0,CYKL         ;celkem 8x

Další příklad ukazuje způsob rotace 16-ti bitového operandu doleva. K této rotaci se používají instrukce s přenosem do C bitu. Uvedený posuv se nazývá logický posuv a spočívá v tom, že bity D0-D14 se posunou o jedno místo doleva, do bitu D0 se přesune bit C a bit D15 se přesune do C.

Příklad:

              ;logický posuv 16-ti bitového čísla, uloženého
              ;v R0 a R1
              MOV     A,R0    ;načtení dolních 8 bitů do A
              RLC     A       ;rotace doleva ms přenosem do C
              XCH     A,R1    ;uschovej a načti horních 8 bitů
              RLC     A       ;rotuj horních 8 bitů
              XCH     A,R1    ;ulož horních 8 bitů
              MOV     R0,A    ;ulož spodních 8 bitů


Booleovský procesor

Architektrura 8051 umožňuje pracovat přímo s jednotlivými bity. Instrukčním soubor obsahuje instrukce, které umožňují přímou adresaci jednotlivých bitů (ať už ve vnitřní RAM nebo v oblasti SFR registrů). Jsou to instrukce:

       •  přesun                              MOV     C,<bit>
       •  komplementaci                       CPL     <bit>
       •  nulování                            CLR     <bit>
       •  nastavování                         SETB    <bit>
       •  logický součet                      ORL     C,<bit>
       •  logický součet s neg.bitem          ORL     C,/<bit>
       •  logický součin                      ANL     C,<bit>
       •  logický součin s neg.bitem          ANL     C,/<bit>

Dále pak je možno stav kteréhokoli bitu testovat na hodnotu true nebo false instrukcemi podmíněných skoků:

       •  skok,je-li bit  roven jedné         JB      <adr>
       •  skok, je-li bit roven nule          JNB     <adr>
       •  skok,  je-li  bit  roven  jedné     JBC     <adr>
          s následným nulováním bitu

Uvedené instrukce představují v praxi velmi silný nástroj pro řešení konkrétních aplikací.

Příklad: Pokud je na vývodu T0 log.0, je na vývodu T1 stejná hodnota, jako je na vývodu P1.0. Pokud je na vývodu T0 log.1, hodnota na výstupu T1 se mění periodicky (střídání 0 a 1).

      TEST:   JB      T0,KOMPL        ;test na stav vývodu T0
              MOV     C,P1.0          ;je nulový - načti P1.0
              MOV     T1,C            ;a přenes na T1
              SJMP    TEST            ;návrat na začátek
      KOMPL:  CPL     T1              ;komplementace vývodu T1
              SJMP    TEST            ;návrat na začátek

Příklad: Synchronizace na přicházející signál. Úkolem je zjistit okamžik sestupné hrany (přechod z jedničky do nuly) na vývoru P2.5. Celý problém řeší zápis jedinné instrukce:

                      JB      P2.5,$

nebo

              ZDE:    JB      P2.5,ZDE

Oba zápisy jsou ekvivalentní, význam znaku $ byl vysvětlen v kapitole 1. Činnost procesoru při provádění této instrukce je jednoduchá - pokud je na vývodu P2.5 logická jednička, provádí se skok na tutéž instrukci, tedy čeká se na okamžik, kdy dojde ke změně z log.1 na log.0. Podobně se dá realizovat čekání na vzestupnou hranu, celý puls, atd..

Příklad: Další z možnosti využití instrukcí pracujících s bity je ve vyhodnocení booleovského výrazu. Představme si, že máme vyhodnotit booleovskou funkci:

              Q= K . (L + not M) + (not N . O) + R


         K                    •————•
         —————————————————————• &  |
                              |    •—————•
                              |    |     |
         L    •————•     •————•    |     |
         —————• 1  |     |    •————•     |
              |    |     |               |
     not M    |    •—————•               |
         —————•    |                     |  •—————•
              •————•                     •——•     |
                                            |  1  |
     not N    •————•             •——————————•     •————  Q
         —————• &  |     POM     |          |     |
              |    •—————————————•      •———•     |
         O    |    |                    |   •—————•
         —————•    |                    |
              •————•                    |
         R                              |
         ———————————————————————————————•

Řešení:

      K       BIT     0       ;definice jednotlivých
      L       BIT     1       ;proměnných v oblasti bitově
      M       BIT     2       ;adresovatelné RAM
      N       BIT     3
      O       BIT     4
      R       BIT     5
      Q       BIT     6
      POM     BIT     7
              MOV     C,O     ;výpočet (not N.O)
              ANL     C,/N
              MOV     POM,C   ;úschova mezivýsledku do POM
              MOV     C,L     ;výpočet (not M + L)
              ORL     C,/M
              ANL     C,K     ;and K
              ORL     C,POM   ;or POM
              ORL     C,R     ;or R
              MOV     Q,C     ;výsledek ulož do Q
              END             ;konec programu


Větvení programů, cykly

VĚTVENÍ PROGRAMŮ

S některými instrukcemi pro větvení programu jsme se seznámili v předchozí kapitole. Mezi instrukce skoků patří dále:

       •  skok, je-li C=1                JC   <adr>
       •  skok, je-li C=0                JNC  <adr>
       •  skok, je-li A=1                JZ   <adr>
       •  skok, je-li A=0                JNZ  <adr>

Všechny uvedené instrukce (JB,JNB,JBC,JC,JNC,JZ,JNZ) jsou tzv. podmíněné skoky, kdy skok na zadanou adresu je podmíněn splněním určité podmínky - (bit nastave/vynulován, akumulátor je nulový...). Jejich další společnou vlastností je, že cíl skoku může ležet pouze v rozsahu <-128,+127> bajtů.

Další skupinou instrukcí, které umožňují větvení programu, jsou tzv.nepodmíněné instrukce:

      •  nepodmíněný skok v rozsahu <-128,+127>     SJMP <adr>
      •  nepodmíněný skok v rozsahu 2 kB            AJMP <adr>
      •  nepodmíněný skok v celém adr. rozsahu      LJMP <adr>
      •  nepodmíněný skok v celém adr. rozsahu      JMP  @A+DPTR

Význam a použití prvních třech instrukcí pro nepodmíněný skok je zřejmý - liší se pouze v rozsahu, ve kterém může ležet adresa skoku. Povšimněme si blíže instrukce JMP @A+DPTR. Tato instrukce provede skok na cílovou adresu, která se vypočítá jako součet obsahu akumulátoru a DPTR. To je podstatný rozdíl oproti předchozím typům instrukcí, kdy cílovou adresu skoku jsme museli znát již při překladu instrukce do strojového kódu, kdežto adresa skoku u instrukce JMP @A+DPTR se vypočítává až za běhu programu!

Příklad:

Máme provést rozskok na jednu z osmi adres programu v závislosti na obsahu akumulátoru, který může nabývat hodnot z intervalu 1..8. Např. je-li A=2, skočí se na podprogram DRUHA.

              MOV     DPTR,#TAB       ;adresa rozskokové tab.
              DEC     A               ;korekce na počátek tab.
              RL      A               ;násobení A dvěma (prvky
              JMP     @A+DPTR         ;v tabulce jsou 2-bytové)


      TAB:    AJMP    PRVNI
              AJMP    DRUHA
              .
              .
              AJMP    SEDMA


VYTVÁŘENÍ CYKLŮ

Při programování cyklů se používají v podstatě tři postupy, známe z vyšších programovacích jazyků: repeat - until, while a for.

      ————————————————————————————————————————————
      repeat
       .
       .
       .
      until   <podmínka>
      ————————————————————————————————————————————
      while <podmínka> do
       .
       .
       .
      end
      ————————————————————————————————————————————
      for <počet cyklů> do
       .
       .
       .
      end
      ————————————————————————————————————————————


A. U cyklu repeat-until se podmínka pro ukončení cyklu testuje vždy až na konci cyklu - vždy tedy dojde k tomu, že program projde cyklem alespoň jednou.

Příklad:

      REPEAT:
              .
              .
              ;tělo cyklu
              .
              .
              JC      REPEAT          ;until <podmínka>

Pokud je příznak C nastaven, program přejde na návěští REPEAT a celý cyklus se opakuje znovu.

B. Cyklus typu while vyhodnocuje podmínku pro vstup do cyklu na jeho počátku - může se tedy stát, že program cyklem neprojde ani jednou.


      WHILE:  JNC      KONEC
              .
              .
              ;tělo cyklu
              .
              .
              SJMP    WHILE
      KONEC:

Pokud příznak C není nastaven, provede se skok na návěští KONEC a program se do cyklu nedostane.

C. Cyklus for je tzv. tvrdý počítaný cyklus. Na rozdíl od předchozích typů je počet průchodů cyklu předem znám. Pro tento účel je přímo předeslána instrukce:

    • odečti a skoč, není-li výsledek nula    DJNZ  <oper>,<adr>


Příklad:

Jedno z možných použití tohoto cyklu je při vytváření časových smyček. Např zpoždění 100 µs při krystalu 6MHz:

      ;zpozdeni 100 mikrosecund (6 MHz)
      DELAY:  MOV        R0,#24       ;naplň počítadlo cyklů
              DJNZ       R0,$         ;skáče sám na sebe  (24x)
              NOP


Poznámka: Pro výpočet časových smyček je vždy nutmo znát frekvenci krystalu, se kterouprocesor pracuje a potom délku každé instrukce v cyklech. Pro náš případ:

              MOV     R0,#23          ;       2 µs
              DJNZ    R0,$            ; 24*4=96 µs
              NOP                     ;       2 µs
                                         —————————
                                           100 µs

K vytváření cyklů a větvení programu slouží také instrukce:

    • porovnej a skoč                 CJNE <oper1>,<oper2>,<adr>
      při nerovnosti


Tato instrukce nejprve porovná oba operandy a pokud se nerovnají, provede relativní skok na zadanou adresu, jinak program pokračuje další instrukcí. Tato instrukce je v celém repertoáru instrukcí 8051 jedinná, která porovnává dva operandy a nastavuje příznak C beze změny hodnot operandů !!!!

Příklad:

Pro vytvoření cyklu repeat-until:

              MOV     B,#5            ;konečná hodnota
              MOV     A,#0            ;počáteční hodnota
      REPEAT: INC     A
              CJNE    A,B,REPEAT      ;porovnej A a B, pokud se
                                      ;liší, skoč na REPEAT

Pro vytvoření cyklu for:

              MOV     R0,#0
      FOR:    .
              ;tělo cyklu for
              .
              INC     R0
              CJNE    R0,#10,FOR      ;dokud je R0 menší než 10,
                                      ;opakuj cyklus


Binární a dekadická aritmetika

Pro jednoduché aritmetické úlohy, tj.sčítání odčítání, násobení a dělení má 8051 tyto instrukce:

      •  sčítání                             ADD     A,<operand>
      •  sčítání s přičtením příznaku C      ADDC    A,<operand>
      •  odečítání                           SUBB    A,<operand>
      •  zvyš obsah o jedničku               INC     <operand>
      •  sniž obsah o jedničku               DEC     <operand>
      •  násobení                            MUL     AB
      •  dělení                              DIV     AB
      •  desítková úprava A                  DA      A

Instrukce sčítání ADD přičte k obsahu akumulátoru zadaný operand. Instrukce ADDC přičte navíc k výsledku ještě obsah C.

Příklady: Sečtěte dvě 16-ti bitová čísla a výsledek uložte na místo prvního z nich.

              OPER1   EQU     20H
                      EQU     21H
              OPER2   EQU     22H
              MOV     A,OPER1
              ADD     A,OPER2
              MOV     VYSL,A



Používání podprogramů

Pro používání podprogramů jsou v instrukčním souboru instrukce:

      •  volání podprogramu v rozsahu 2 kb      ACALL   <adr>
      •  volání podpr. v celém adres.rozsahu    LCALL   <adr>
      •  návrat z podprogramu                   RET
      •  návrat z obsluhy přerušení             RETI

Instrukce ACALL a LCALL při své aktivaci zvýší ukazatel zásobníku SP o 2, uloží adresu následující instrukce na zásobník a předá řízení programu na cílovou adresu.

Instrukce RET předá řízení na adresu, uloženou na vrcholu zásobníku a poté odečte od SP dvojku - dochází tedy ke snížení zásobníku o jednu úroveň.

Činnost instrukce RETI z hlediska předání řízení programu na adresu uloženou na zásobníku je totožná s RET, ale navíc RETI povoluje přerušení - používá se tedy při návratu z obsluhy přerušení.

Příklad: Pp vyhodnocení přerušení od sériové linky se řízení předá na adresu 23H. Jeden z možných způsobů obsluhy přerušení může vypadat např. takto:


      ORG     23H
      LJMP    SER_INT         ;skok na obsluhu přerušení
      .
      .
      .
      ORG     500H            ;adresa obsluhy přerušení
                              ;může být n alibovolném místě
                              ;zde např. 500H

SER_INT:

      PUSH    ACC
      PUSH    B               ;ůschova geristrů
      PUSH    PSW
      .
      .                       ;obsluha přerušení
      .
      POP     PSW
      POP     B               ;obnovení registrů
      POP     ACC
      RETI                    ;návrat z přerušení, povolení
                              ;dalších přerušení


Zdroj informací: Příručka EasySoft --JA 27. 4. 2010, 14:33 (UTC)