Každý program musí byť schopný presunov dát a to medzi registrami, registrami a pamäťou,
registrami a vstupmi/výstupmi. Pri tejto operácii si musíme vždy uvedomiť,
koľkobitové číslo presúvame. Počet bitov je väčšinou špecifikovaný menom
použitého registra (osembitové - AH, AL, BH, BL, . . ., šestnásťbitové
- AX, BX, BP, DI, ES, DS . . .). V prípade, že používame len pamäť, špecifikuje
počet bitov pre operáciu označenie:
-
BYTE PTR
označenie pamäťového. miesta - špecifikuje slabiku
-
WORD PTR
označení pamäťového miesta - špecifikuje slovo
Presuny
registre - register, register - pamäť
Všetky presuny tohoto
typu prevedieme univerzálnou inštrukciou:
-
MOV cieľ,
zdroj - do cieľa presuň zo zdroja (register - register, register - pamäť,
register - hodnota, pamäť - hodnota, seg. register - register, seg. register
- pamäť)
Použitie tejto inštrukcie
demonštruje príklad:
#include <stdio.h>
unsigned char slabika; // v pamäti rezervuj 8 bitov a označ ich slabika
unsigned int slovo; // v pamäti rezervuj 16 bitov a označ ich slovo
void main() {
asm {
MOV AL, 10 // do registra AL dosaď 8 bitov, hodnotu 10
MOV slabika, AL // do pamäti na miesto ozn. slabika dosaď obsah AL
MOV BX, 10 // do registra BX (16 bitový) dosaď 10
MOV slovo, BX // do pamäti na miesto ozn. slovo dosaď 16 bitov bx
}
printf("%d %d", slabika, slovo);
}
Tento program
má po preklade na miestach premenných v bloku asm označenie pamäťového
miesta, ktoré pre ne bolo vyčlenené. Miesto pre premenné je vždy v segmentu
globálnych premenných. Segmentová adresa tohoto bloku je vždy umiestnená
v registri DS. To, že DS ukazuje na segment dát programu, môže viesť k
chybe, ktorá spočíva v jeho zmene a následnom čítaní z globálnych premenných.
Takže pozor! Po zmene registru DS je práca s globálnymi premennými nemožná,
pretože sme si k nim odrezali cestu. Do segmentových registrov nejde dosadiť
hodnota priamo. Tú najrýchlejšie dosadíme tak, že ju vložíme do niektorého
univerzálneho registra a z neho potom do segmentového registra (napríklad
MOV AX,adresa; MOV ES,AX).
Metódy
adresovania
Miesto (offset)
v pamäti označuje vždy určitá hodnota zapísaná v hranatých zátvorkách.
Inštrukcia
MOVBYTE PTR ES:[0x100F], 10 znamená: na adresu
slabiky offset 100F (0x označuje použití hexadecimálnej sústavy) v segmente
určenom adresou v ES, dosaď hodnotu 10. Ak segment nešpecifikujeme označením
a dvojbodkou, vzťahuje sa adresa k segmentu v DS. V praxi by táto metóda
obmedzovala programátora v rozlete. Preto ASM86 umožňuje i ďalšie metódy
adresovania. Ale po poriadku . . .
-
Priame adresovanie
MOV AH,
ES:[0x1A40] - do registru AH predaj 8 bitov z adresy určenej ES a číslom
Túto metódu použijeme,
ak dopredu vieme adresu hľadaného miesta v pamäti. Na pomoc v C++ sú operátory:
-
OFFSET premenná
- vracia offsetovú adresu premennej
-
SEG premenná
- vracia segmentovú adresu premennej (pre globálne premenné vracia vždy
obsah DS)
Ich použitie
umožní zistiť adresu deklarovaných premenných.
unsigned char premenna;
void main() {
asm MOV BYTE PTR [offset premenna], 10
}
Segmentová adresa
sa v tomto príklade nemusí určiť. Je v DS, a ten sa nemusí uvádzať. Prekladač
C++ túto metódu používa i pre naše globálne premenné. Pri preklade je totiž
každej premennej pridelené miesto v pamäti s pevnou offsetovou adresou
(takže zápis OFFSET premenná nesie práve túto adresu). Špecifikácia,
či sa jedná o slabiku, alebo slovo, je nutná, pretože inak by procesor
nevedel, či má číslom obsadiť jednu, alebo dve slabiky.
-
Nepriame adresovanie
MOV AH,
ES:[BX] - do registru AH predaj obsah pam. miesta špecifikovaného adresou
v BX
Pozor! Do registru
AH je uložený obsah v pamäte na adrese v BX, ne obsah registra BX. Offsetová
časť adresy je uložená v niektorom z adresových registrov BX, BP, SP, SI,
DI. Vzhľadom k tomu, že obsah týchto registrov môžeme meniť, použijeme
túto metódu v prípade pohybu po pamäti.
unsigned char premenna;
void main() {
asm {
MOV BX, offset premenna // do BX dosaď adresu premennej
MOV BYTE PTR [BX], 10 // na jej adresu dosaď hodnotu 10
}
}
-
Bázové adresovanie
MOV AH,
[BX + adresa] - k registru BX pričítaj konštantu adresa, výsledná hodnota
je adresou odkiaľ sa má načítať do registra AH
Bázová adresa
sa tvorí s pomocou obsahu jedného z bázových registrov BP, BX. Výraz v
zátvorke sa vyhodnotí, pritom označenie registrov zastupuje ich obsahy.
Tento druh adresy používame pri zisťovaní hodnôt parametrov určených pre
podprogramy (prípadne k prístupu k lokálnym premenným).
-
Indexové adresovanie
MOV AH,
ES:[adresa + SI] <=> (je zhodné) MOV AH, adresa[SI] - register
SI sčítaj s konštantou adresa, výsledok je hodnota adresy offsetu do pamäti
Tento spôsob
adresovania je obdobou predchádzajúcej tvorby adresy. Používa sa však pri
práci s blokmi v pamäte. Tu sú k dispozícii indexové registre SI, DI.
unsigned char pole[10];
void main() {
asm {
MOV SI, 0 // nuluj register SI
MOV BYTE PTR [SI + offset pole], 10 // adr. pole sčítaj s SI a dosaď 10
}
}
Program dosadí na prvé miesto pole hodnotu. Pretože register SI môžeme zvyšovať, budeme
týmto spôsobom realizovať pohyb v poli.
-
Kombinovaná adresa báza + index
MOV AH,
[BX + SI] <=> MOV AH, [BX][SI] - obsahy registrov BX a SI sčítaj,
výsledok je hodnota offsetu odkiaľ sa má čítať
Kombinovaná adresa
umožňuje pracovať s adresou, ktorá sa skladá zo súčtu dvoch registrov (jedného
bázového BX, BP a jedného indexového SI, DI).
unsigned char pole[10];
void main() {
asm {
MOV BX, offset pole // do registra BX dosaď adresu poľa
MOV SI, 0 // do registra SI dosaď 0
MOV BYTE PTR [BX][SI], 10 // na prvý prvok v poli ulož 10
}
}
-
Kombinovaná adresa priama + báza + index
MOV AH,
[adresa + BX + SI] <=> MOV AH, adresa[BX][SI] - sčítaj registre
BX, SI a pričítaj hodnotu adresa, výsledok je hodnota offsetu
Toto adresovanie
použijeme napríklad pri práci s hlavičkovými súbory. Bázový register nastavíme
na počiatok bloku pamäti vyčleneného na uloženie súboru. Indexový register
vynulujeme. Konštantná hodnota (adresa) môže byť rovná dĺžke hlavičky.
Zvyšovaním hodnoty v indexovom registri sa pohybujeme v dátach hlavičkového
súboru. Ďalšie možné použitie tohto adresovania je pri pohybe v dvojrozmerných
poliach. Hodnoty v oboch registroch sú indexy poľa. Konštantná adresa je
adresou počiatku poľa.
Prefix preskočenia
V assembleri mikroprocesoru
8086 sa objavuje i nový výraz. Prefix znamená určitú špecifikáciu pre nasledujúcu
inštrukciu. Zatiaľ sme si ukázali, ako zmeniť špecifikáciu segmentového
registra adresy s pomocou jeho označenia a dvojbodky. Ďalším spôsobom je
použitie prefix zmeny segmentu: SEGDS, SEGES, SEGCS,
SEGSS.
Tieto označenia sú prefixami preskočenia (zmeny segmentu) pre jednotlivé
segmentové registre. Napríklad:
MOV AX, ES:[BX] je rovnaké, ako
by sme použili SEGES MOV AX, [BX] (i keď zápis je rôzny,
kód programu bude po preklade rovnaký).
Práca
so zásobníkom
Zásobník je časť
v pamäte počítača vyhradená na odkladanie dát. Je organizovaná tak, že
dáta, ktoré sú uložené naposledy, vyberáme ako prvé. Na vrchol zásobníku
ukazujú adresy uložené v registroch SS a SP (prípadne BP). Pridávaním dát
do zásobníka sa offset v SP automaticky znižuje o dve (a naopak). Musíme
si teda uvedomiť, že do zásobníka môžeme odkladať len šestnásťbitové dáta.
Pre prácu so zásobníkom slúžia inštrukcie:
-
PUSH zdroj - do zásobníka ulož obsah zdroja (register, pamäť, [286] hodnota)
-
POP ciel - zo zásobníka dosaď do cieľa (register, pamäť)
-
PUSHA - [286] do zásobníka ulož postupne registre AX, CX, DX, BX, SP, BP, SI, DI
-
POPA - [286] zo zásobníka dosaď späť registre uložené inštrukciou PUSHA
-
PUSHF - do
zásobníka ulož obsah registra F v šestnásťbitovom tvare
-
POPF - hodnotou zo zásobníka obsaď register F
unsigned int premenna;
void main() {
premenna=10; // do pamäte na adresu premenné dosaď 10
asm {
MOV AX, premenna // obsah premennej dosaď do registra AX
MOV BX, 0xBBBB // do registra BX dosaď číslo
PUSH AX // ulož obsah AX
PUSH BX // ulož obsah BX
MOV AX, 0xAAAA // prepíš obsah AX
MOV BX, 0xCCCC // prepíš obsah BX
POP BX // obnov obsah BX
POP AX // obnov obsah AX
MOV premenna, AX // vráť obsah AX do premennej
}
}
Tento program
naznačuje postup ukladania a vyberania dát do a zo zásobníka. V zásobníku
sú uložené i lokálne premenné procedúr a funkcií. Sú tu i parametre, ktorými
je podprogram volaný. (Preto lokálne premenné NEMAJÚ segmentovú adresu
v DS.) Občas potrebuje programátor uložiť register príznakov F, aby ho
neskôr mohol obnoviť do pôvodného stavu. K tomu požívame inštrukciu PUSHF
(pre uloženie) a POPF (pre obnovenie). Ak vo vkladanom assembleri
chceme meniť niektorý zo "zakázaných" registrov (DS, BP), môžeme si jeho
obsah uložiť do zásobníka. Podmienkou je ale to, že nezmeníme registre
SS, SP. Tým by sme si podrezali vetvu pod sebou. Ďalšie možné použitie
zásobníka je pri práci s časťou pamäti, v ktorej máme pole slov (šestnásťbitových
dát). Nasmerovaním vrcholu zásobníka (SS:SP) na koniec tohoto poľa môžeme
inštrukciami PUSH a POP s týmto poľom pracovať. Pritom sa
bude automaticky zvyšovať a znižovať adresa. Pozor ale, obsahy SS a SP
je nutné zase uschovať, najlepšie do pamäte na miesta premenných. V tom
prípade ale nemôžeme meniť register DS (ES).
Presuny
vstup-výstup - register
Každý sa niekedy
pokúsime zapísať na port a čítať z neho. Je dobré si uvedomiť, že môžeme
zapisovať osem i šestnásť bitov. Každý port, rovnako ako slabika v pamäti,
má svoju adresu. Pri zápise šestnástich bitov zapisujeme teda i na port
s adresou o jednu vyššiu. Prácu s portami prevedieme inštrukciami:
-
OUT adresa portu, zdroj - pre zápis na port (AL, AX-> port)
-
IN cieľ, adresa portu - pre čítanie z portu (port-> AL, AX)
Dáta sa čítajú, alebo
zapisujú z (do) registra AL (osembitový prístup), AX (šestnásťbitový prístup).
Adresu portu špecifikuje buď priamo adresa (IN AL, 0x0F) pri adrese
osembitovej (spodných 256 portov), alebo register DX, v ktorom je šestnásťbitová
adresa (MOV DX, 0xF10; OUT DX, AL).
Ďalšie
presuny
Medzi presuny dát
patrí aj:
-
XCHG cieľ, zdroj - vzájomná výmena hodnôt zdroja a cieľa (pamäť - register, register - register)
-
LAHF - do registra AH dosaď nižšiu slabiku registra príznakov F
-
SAHF - z registra AH dosaď do nižšej slabiky registra príznakov F
-
XLAT - do AL dosaď obsah slabiky v pamäti s adresou v DS:[BX + AL] (práca s tabuľkou)
a niektoré reťazcové inštrukcie o ktorých bude reč neskôr.