Professional Documents
Culture Documents
Prvý blok cvičení je navrhnutý tak, aby ste sa oboznámili s fungovaním procesoru
a základnými funkciami jazyka c++.Za úlohy prvého bloku môžete získať až 12 bodov.
Úlohy odovzdávajte priebežne, najneskôr však vo štvrtom týždni semestra. Po tomto
termíne už nebude možné odovzdať žiadnu z týchto úloh!
Nemusíte riešiť všetky, vyberte si tie, ktoré vás zaujmú tak, aby ste získali potrebný
počet 12 bodov. Celkove je tu 8 úloh za 18 bodov. Viac ako 12 nemôžete mať. Na
zápočet potrebujete aspoň 6 bodov.
• viď úloha 11
• viac o inštrukcii CPUID
• error C2443: operandsizeconflict
• Ešte viac o inštrukcii cpuid [niekedy nefunguje]
• Popis inštrukcie cpuid [sandpile.org]
Úloha 16 (2b)
Napíšte program, ktorý zobrazí nasledovné dva vstupy: číslo 29127 a ASCII kód znaku
'F', v dvojkovej, desiatkovej a šestnástkovej sústave.
Tip: Použite čo najmenší počet premenných a využite možnosti pretypovania
a formátovacieho reťazca funkcie printf.
Zdroj:
Úloha 17 (2b)
Napíšte program, ktorý dokáže zapísať zadené číslo (stačí rozsah 1-100) rímskymi
číslicami.
Zdroj:
Úloha 18 (4b)
Napíšte program na výpočet kontrolnej sumy (checksum) pre zadaný riadok tzv. IntelHex
súboru.
Definícia: Kontrolný súčet sa vypočíta ako dvojkový doplnok súčtu jednotlivých bajtov
od začiatku riadku až po kontrolný súčet.
Príklad:
Ak zadáte tento reťazec:
:10010000214601360121470136007EFE09D21901XX
Musí vám vyjsť namiesto XX kontrolný súčet 40.
Splnenie úlohy demonštrujte výpočtom kontrolných súm napr. pre tieto reťazce:
:100010000C9445000C9445000C9445000C944500xx
:100020000C9445000C9445000C9445000C944500xx
:100030000C9445000C9445000C9445000C944500xx
:100040000C9445000C9445000C9445000C944500xx
Asme..., Assem...
Čo? Aký assembler?
Assembler je strojový jazyk, čo znamená, že každá inštrukcia priamo hovorí stroju --
procesoru, čo má vykonať. Z toho vyplýva, že tento jazyk je vždy určený pre konkrétny
typ procesora, programy nie sú prenositeľné a závisia vždy na konkrétnej konfigurácii a
architektúre.
Na cvičeniach budeme písať veľmi jednoduché programy, ktoré využívajú len malú časť z
kompletného inštrukčného súboru procesorov rodiny x86. Všetky (okrem CPUID) by mali
fungovať aj na najstaršom procesore 8086 až po najnovšie Pentium 4.
Na nasledujúcom obrázku je nakreslená vnútorná štruktúra procesora 80386. Je to
základ, ktorý možno nájsť aj v jeho nasledovníkoch. Keďže prakticky všetko, čo procesor
robí sa odohráva v registroch, nájdeme ich tu hneď niekoľko.
Registre procesorov x386.
Predovšetkým je tu sada univerzálnych registrov A-D, ktoré vykonávajú prakticky všetku
prácu. Niektoré majú špeciálne určenie, ale vo väčšine inštrukcií môžete použiť ľubovoľný
z nich. V pôvodnom procesore 8086 boli tieto registre 16-bitové (AX, BX, CX a DX),
pričom každý z nich sa dal použiť aj ako 8-bitový (napr. AX = AH + AL). S nástupom 32-
bitových procesorov sa rozšírili aj tieto registre, takže dnes sú všetky 32-bitové, pričom
ale ostala zachovaná možnosť pristúpiť k spodnej polovici obsahu cez pôvodné označenie
(teda napr. 32-bitový register EAX má spodných 16 bitov označených ako AX, ktoré sa
dajú rozdeliť zasa na dve polovice AH a AL).
Okrem univerzálnych registrov sú v procesore dva indexové registre ESI a EDI, ktoré sa
používajú napríklad pri počítadláach. Ďalej tri ukazovacie registre EBP, ESP a EIP (EBP je
bázový pointer, ktorý sa používa pri výpočte adresy, ESP je zásobníkový - stack pointer,
ktorý ukazuje na vrchol zásobníka a EIP je inštrukčný, ktorý ukazuje na práve
vykonávanú inštrukciu).
Pri práci s pamäťou sa intenzívne využíva ďalšia skupina, tzv. segmentových registrov
(SS - stack segment, CS - code segment, DS - data segment, ES - extra segment, FS a
GS).
Významné postavenie má tzv. príznakový register (FLAGS), v ktorom má každý bit
špeciálny význam. Napríklad nultý, najnižší bit CF (Carry Flag) sa automaticky nastaví do
log. 1, ak pri aritmetickej operácii nastal prenos do vyššieho rádu. Jednotlivé bity tohoto
registra je možné testovať a vetviť tak program na základe rozličných podmienok.
Podľa funkcie
• Presuny
• MOV
• Aritmeticko-logické
• ADD
• SUB
• INC
• DEC
• Posuny
• SHL
• SHR
• Skoky
• CMP
• JG
• JE
• JZ
• JMP
• Špeciálne
• CPUID
• NOP
MOV Move
MOV ciel,zdroj ; ciel <- zdroj
Operandy:
reg, reg
reg, mem
mem, reg
reg, imm
mem, imm
Príznaky:
O D I T S Z A P C
Popis: Inštrukcia presunie obsah z jedného miesta na druhé. Najčastejšie sa vyskytujúca
inštrukcia. Procesor vlastne stále len niečo niekam presúva.
Príklad:
ADD Addition
ADD ciel,zdroj ; ciel = ciel + zdroj
Operandy:
reg, reg
reg, mem
mem, reg
reg, imm
mem, imm
Príznaky:
O D I T S Z A P C
+ - - - + + + + +
Popis: Inštrukcia spočíta oba operandy a výsledok uloží do prvého z nich.
Príklad:
SUB Substraction
SUB ciel,zdroj ; ciel = ciel - zdroj
Operandy:
reg, reg
reg, mem
mem, reg
reg, imm
mem, imm
Príznaky:
O D I T S Z A P C
+ + + + + +
Popis: Inštrukcia odpočíta druhý operand od prvého a výsledok uloží do prvého z nich.
Príklad:
INC Increment by 1
INC zdroj ; zdroj = zdroj + 1
Operandy:
reg
mem
Príznaky:
O D I T S Z A P C
+ + + + +
Popis: Inštrukcia zväčší obsah operandu o jednotku. Je to rýchlejšie ako ADD zdroj,1.
Príklad:
DEC Decrement by 1
DEC zdroj ; zdroj = zdroj - 1
Operandy:
reg
mem
Príznaky:
O D I T S Z A P C
+ + + + +
Popis: Inštrukcia zmenší obsah operandu o jednotku. Je to rýchlejšie ako SUB zdroj,1.
Príklad:
SHL ShiftLogicalLeft
SHL zdroj,pocet ; zdroj = zdroj<<pocet
Operandy:
reg,CL
mem,CL
Príznaky:
O D I T S Z A P C
+ + +
Popis: Inštrukcia posunie register, alebo obsah pamäti o pocet miest doľava. Zľava Zprava sa doplní
nula a najvyšší bit "prepadne" do príznakového bitu CF (carryflag). Pozn.: pocet sa uloží v registri CL.
Príklad:
Operandy:
reg,CL
mem,CL
Príznaky:
O D I T S Z A P C
+ + +
Popis: Inštrukcia posunie register, alebo obsah pamäti o pocet miest doprava. Na najvyšší bit príde
nula a najnižší bit "prepadne" do príznakového bitu CF (carryflag). Pozn.: pocet sa uloží v registri CL.
Príklad:
CMP Compare
CMP ciel,zdroj ; ciel - zdroj
Operandy:
reg, reg
reg, mem
mem, reg
reg, imm
mem, imm
Príznaky:
O D I T S Z A P C
+ + + + + +
Popis: Inštrukcia odpočíta druhý operand od prvého ale výsledok neuloží nikam, iba nastaví príznaky
podobne ako inštrukcia SUB. Používa sa pri rozhodovaní, obvykle nasleduje inštrukcia podmieneného
skoku.
Príklad:
Operandy:
lab
Príznaky:
O D I T S Z A P C
Popis: Inštrukcia vykoná skok na návestie ak je výsledok predošlej operácie nula. Presnejšie
povedané, ak ZF je nastavený skok sa vykoná. Nemusí to byť výsledok bezprostredne predošlej
inštrukcie. Obvykle sa používa v kombinácii s inštrukciou CMP. Vtedy to znamená, že oba jej operandy
boli rovnaké. Preto sa táto inštrukcia používa aj ako JE. Vygenerovaný kód je však rovnaký.
Skok samotný procesor zrealizuje veľmi jednoducho -- presunie novú adresu do registra IP
(instruction pointer).
Príklad:
Operandy:
lab
Príznaky:
O D I T S Z A P C
Popis: Inštrukcia vykoná skok na návestie ak výsledok niektorej predošlej operácie nastaví príznaky SF
a OF na rovnakú hodnotu a nie je to nula. Obvykle sa používa v kombinácii s inštrukciou CMP. Vtedy to
znamená, že prvý jej operand je väčší (preto JG). To ale môžeme interpretovať aj tak, že nie je menší
alebo rovný a preto sa táto inštrukcia používa aj ako JNLE. Vygenerovaný kód je však rovnaký.
Príklad:
JMP Jump
JMP ciel ; goto ciel
Operandy:
ciel
Príznaky:
O D I T S Z A P C
Skok samotný procesor zrealizuje veľmi jednoducho -- presunie novú adresu do registra IP
(instruction pointer).
Príklad:
Operandy:
Príznaky:
O D I T S Z A P C
Popis: Inštrukcia uloží do registrov xxx, xxx a xxx 12 znakový reťazec, jednoznačne identifikujúci
výrobcu procesora.
Príklad:
NOP No Operation
NOP ; nerobimnic
Operandy:
žiadne
Príznaky:
O D I T S Z A P C
- - - - - - - - -
Popis: Inštrukcia, ktorá nerobí nič. Používa sa ako malé oneskorenie, doladenie časových slučiek a
pod.
Príklad:
OUT 44, AX ; na port 44 zapisem obsah registra AX
NOP ; chvilupockam, lebo viem, ze je pomaly
OUT 44, BX ; na port 44 zapisemdruhecislo z registra BX
Vzorovýpríklad
/*
***************************************************************************
***** */
/*
*/
/* Architekturapocitacov - Uloha 2.1
*/
/*
*/
/* Program inkrementuje v assembleripremennu
*/
/*
*/
/* Autor: Richard Balogh <balogh@elf.stuba.sk>
*/
/* Historia:
*/
/* 2.3.2006 zakladna verzia funkcna
*/
/* 6.3.2006 verzia pre oba kompilatory
*/
/* Prenositelnost:
*/
/* Zalezi na syntaxi prekladaca!
*/
/*
***************************************************************************
***** */
#include<stdio.h>
intmain(intargc,char*argv[])
{
iCislo=27;
iVysledok=0;
printf("\nCislo: %d Vysledok: %d",iCislo,iVysledok);
#endif
Vysvetlivky k vzorovému
príkladu
Aby sme sa nemuseli príliš detailne zaoberať štruktúrou programu v assembleri, t.j. čo
všetko okrem nášho algoritmu musí obsahovať, aby sa vôbec dal spustiť v danom
operačnom systéme, budeme používať tzv. vložený (embedded) assembler. To znamená,
že to bude len akýsi fragment kódu vložený do štandartného C-programu.
DEV-C++ (gcc)
Kompilátor gcc má trocha odlišnú syntax pre vložený assembler. Je to vlastne
funkcia asm ("retazec") , kde samotné inštrukcie musíme písať ako textový reťazec.
Pretože takýto blok sa priamo podsunie prekladaču assembleru, musí vložený kúsok
vyzerať presne rovnako ako zdrojový kód pre assembler. To znamená, že každá
inštrukcia musí končiť \n. Nepríjemná komplikácia je, že gcc používa inú syntax (tzv.
AT&T), dosť odlišnú od Intel syntaxe. Napríklad operandy zdroj, cieľ sa uvádzajú v
opačnom poradí. Našťastie sa to dá prenaučiť príkazom .intel_syntaxnoprefix.
Premenné z C-programu, môžete používať priamo, len musíte pridať znak podtržítko pred
názov premennej. Premenné z C-programu, ktoré chcete používať v assembleri však
musia byť zadefinované ako globálne.
Podobne ako vo Visual C však musíte mať na pamäti, že do registrov môžete presúvať
len premenné zodpovedajúcej veľkosti. Nemôžete teda napr. do registra EAX presunúť
premennú typu double. Výsledok práce programu odovzdáte C-programu tiež jednoducho
cez premenné.
Príklad:
asm(".intel_syntaxnoprefix \n" // Prepneme z AT&T syntaxe na na Intel
CPUID
Firma Intel po uvedení MMX inštrukcií zaviedla aj jednu novú inštrukciu (cpuid), ktorá
umožnila rozpoznať svoje nové procesory a využívať tak inštrukcie pre ne. Tým sa
umožnila optimalizácia kódu.
Táto jednoduchá asemblerovská inštrukcia umožňuje programátorovi získať mnoho
informácií: výrobcu CPU (napr. Intel, AMD, Cyrix,...), aké "extra" vlastnosti podporuje
(napr. MMX, FPU, 3DNow,...) a ďalšie.
Inštrukcia je k dispozícii od procesorov Pentium vyššie.
Inštrukcia nemá žiadne parametre, pred volaním sa nastaví požadovaná funkcia
zapísaním vhodného čísla do EAX.
Funkcia 0 vracia VendorString, reťazec 12 znakov identifikujúci výrobcu procesora. Prvé
štyri znaky sú v EBX, ďalšie v EDX a posledné štyri v ECX. V registri eax sa vráti
najvyyššia funkcia, ktorú ešte môžeme použiť. (12 chars = 3 x 4 bytes = 3 x 4 x 8 bits =
3 x 32 bits)
Ďalšie funkcie, ktoré dostaneme zapísaním prísušného čísla do EAX pred volaním funkcie
sú:
Funkcia 1 vracia Signature
Funkcia 2 vracia Config
Funkcia 3 SerialNumber
Vyššie čísla sú rezervované.
Podrobnosti nájdete na tejto stránke [niekedy nefunguje], prípadne pozrite
tento popis inštrukcie cpuid [sandpile.org] a samozrejme v manuáloch k
jednotlivým procesorom.
Error C2443: operandsizeconflict
Význnam: inštrukcia vyžaduje, aby oba operandy boli rovnakej veľkosti. Musíte jeden z
operandov zmeniť tak, aby oba mali rovnakú veľkosť.
Táto chyba môže znamenať aj to, že kompilátor NEVIE akú veľkosť máte na mysli, musíte
mu dať pomôcku ktorou presne špecifikujete veľkosť premennej.
Príklad:
charstr[4];
intmain()
{
__asmmovstr, EAX // chyba
__asmmov BYTE PTR str, AL // OK, presunie do str jeden byte
__asmmov WORD PTR str, AX // OK, presunie do str 2 byte
__asmmov DWORD PTR str, EAX // OK, presunie do str 4 byte
}