Ľudmila Jánošíková

Programovanie v jazyku symbolických adries

pre 32-bitové procesory Intel
Obsah Index
Spôsoby adresovania

Keď hovoríme o spôsoboch adresovania, máme na mysli rôzne spôsoby, ako sa v inštrukciách odvolávame na operandy v registroch alebo v pamäti.

Implicitné adresovanie

Pri tomto spôsobe adresovania operand v zápise inštrukcie nevidíme. Ako príklad môžeme uviesť inštrukciu mul bl, ktorá vynásobí obsah registra AL obsahom registra BL a výsledok uloží do registra AX. V inštrukcii mul bl sú uvedené dve implicitné adresy: adresa 1. operandu (register AL) a adresa výsledku (register AX). Žiadna z týchto adries nie je explicitne zapísaná v inštrukcii. Sú dané typom inštrukcie a schopnosťami procesora. Všetky ďalej uvádzané typy adries sú adresy explicitné.

Explicitné adresovanie

Priamy operand (adresa 0. rádu)

Hodnota operandu je zapísaná priamo v inštrukcii, nejde vlastne ani o adresu (preto adresa 0. rádu). Napr. v inštrukcii mov al,3 je číslo 3 priamym operandom.

Priama adresa (adresa 1. rádu)

Priama adresa sa používa najčastejšie. Je to v inštrukcii zapísané označenie registra alebo číslo pamäťového miesta, s ktorého obsahom inštrukcia pracuje. Príkladom môže byť inštrukcia add dx,[Alfa], ktorá sčíta obsah registra DX s obsahom premennej Alfa a výsledok uloží do DX. Obidva operandy inštrukcie sú priamou adresou. Meno premennej interpretuje prekladač ako offset v dátovom segmente. Hranaté zátvorky uzatvárajúce meno premennej nie sú povinné, majú len zdôrazniť, že pracujeme s hodnotou premennej, a nie s jej adresou.

Nepriama adresa (adresa 2. rádu)

Pri nepriamom adresovaní je v inštrukcii zapísaná adresa pamäťového miesta, na ktorom nájdeme adresu operandu (na tomto mieste nájdeme priamu adresu). Nepriame adresovanie v takejto podobe sa používa zriedka (napr. v inštrukciách nepriameho skoku).

Za zvláštny prípad nepriameho adresovania môžeme považovať nepriame adresovanie pomocou registra. V tomto prípade je priama adresa uložená v niektorom registri procesora a inštrukcia obsahuje adresu (meno) registra. Priamu adresu môžeme získať aj sčítaním obsahov dvoch registrov, prípadne k nim môžeme ešte pripočítať konštantu - tzv. posunutie (displacement). Priamu adresu môžeme vypočítať pomocou

  • bázového registra,
  • indexového registra,
  • bázového registra a posunutia,
  • indexového registra a posunutia,
  • bázového a indexového registra,
  • bázového registra, indexového registra a posunutia.

V chránenom režime sa ktorýkoľvek 32-bitový univerzálny register môže použiť ako bázový register alebo (s výnimkou registra ESP) ako indexový register. Segment operandu sa určuje podľa bázového registra:

  • EAX, EBX, ECX, EDX, ESI, EDI - operand leží v dátovom segmente,
  • EBP, ESP - operand leží v zásobníkovom segmente.
Napr. inštrukcia mov al,[ebx] skopíruje do registra AL obsah bajtu na adrese, ktorá je v registri EBX. Adresou sa myslí offset v dátovom segmente. Nepriamu adresu môžeme vytvoriť aj tak, že v inštrukcii uvedieme bázový register a posunutie. Napr. príkaz mov al,[ebx+2] skopíruje do registra AL obsah bajtu, ktorého offset v dátovom segmente vypočítame tak, že k obsahu registra EBX pripočítame 2. Posunutím môže byť aj premenná. V prípade premennej sa k obsahu bázového registra pripočíta adresa premennej.

Ako sme už uviedli, môžeme používať aj kombináciu bázových a indexových registrov, napr. mov al,[ebx+edi]. Obsahy bázového a indexového registra sa sčítajú a dostaneme offset, na ktorom budeme hľadať pravý operand. Segment sa určí podľa použitého bázového registra, t.j. v tomto prípade by sa použil dátový segment. V príkaze mov al,[ebp+esi+4] sa odkazujeme do zásobníkového segmentu. Tento príklad zároveň ukazuje, že k bázovému a indexovému registru môžeme pripočítať aj posunutie.

Nepriame registrové adresovanie sa s výhodou používa pri spracovaní polí dát. Do bázového registra sa ukladá adresa začiatku poľa (báza poľa alebo smerník na pole), zatiaľ čo indexový register slúži na prechádzanie poľom. Ukážme si príklad na použitie bázových a indexových registrov.

Úloha

Vypíšte na obrazovku znakový reťazec uložený v premennej Retaz, ktorý je ukončený nulou.

Riešenie

Každý znak reťazca je v pamäti uložený v jednom bajte v kóde ASCII. Reťazec je uložený zľava doprava, t.j. prvý znak má najnižšiu adresu. Meno premennej interpretuje prekladač ako adresu, na ktorej reťazec začína. Na obr. 7 je znázornené uloženie reťazca „No nazdar!“. Kvôli lepšej čitateľnosti sú v pamäťových bunkách zapísané znaky a nie ich ASCII kódy.


Retaz : 'N'
Retaz + 1: 'o'
Retaz + 2: ' '
Retaz + 3: 'n'
... ...
Retaz + 9: '!'
Retaz + 10: 0

Obr. 7. Uloženie reťazca v pamäti

Z cvičných dôvodov budeme reťazec vypisovať po jednotlivých znakoch. Nasledujúci program rieši našu úlohu. Na výpis znaku použijeme procedúru WriteChar z knižnice Irvine32.lib.

TITLE MASM VypisRetazca(main.asm)
INCLUDE Irvine32.inc
.data
Retaz DB "No nazdar!",0Dh,0Ah,0; deklarácia premennej
.code
main PROC
mov edx,offset Retaz; ulož do edx adresu 1. znaku reťazca
Vypis:

mov edi,0; prvý znak má index 0
VypisZnak:

mov al,[edx+edi]; ulož do al znak na offsete edx+edi

cmp al,0; porovnaj al s nulou

je Koniec; ak sú rovnaké, choď na návestie Koniec

call WriteChar; vypíš znak, ktorého ASCII kód je v al

inc edi; zvýš index o 1 - prejdi na ďalší znak

jmp VypisZnak
Koniec:
exit
main ENDP
END main

Bez nepriameho adresovania by sme cyklus na výpis reťazca po znakoch ani nevedeli zostaviť. Pri výpise jedného reťazca by sme vystačili s indexovým registrom. Príkaz na skopírovanie znaku s indexom EDI do registra AL by potom bol mov al,[Retaz+edi]. Ak by sme ale potrebovali vypísať viacej reťazcov, mohli by sme časť programu medzi návestiami Vypis a Koniec zapísať ako procedúru. Pred jej vyvolaním z hlavného programu by sme uložili adresu požadovaného reťazca do registra EDX.

Nepriame registrové adresovanie je podporované aj pravidlami kódovania inštrukcií. Pokiaľ inštrukcia obsahuje len bázový a/alebo indexový register (bez posunutia), potrebujeme na jej zakódovanie o 4 bajty menej, ako keby sme v inštrukcii uviedli priamu adresu.

Indexový register sa môže násobiť číslom 2, 4 alebo 8, čo umožňuje zrýchliť prístup k položkám polí typu word, dword, resp. qword (pozri kapitolu Premenné a návestia). Príklad:

mov eax,[DwordTable+ebx*4]

Rovnaký register môže dokonca byť použitý dvakrát: ako bázový aj ako indexový register. Táto možnosť sa dá využiť aj v 16-bitovom režime v niektorých inštrukciách, ktoré nepristupujú do pamäti, napr. inštrukcia

lea eax,[eax+eax*4]

vynásobí obsah registra EAX piatimi.

Pretože ľubovoľný univerzálny register môže byť bázou aj indexom, otázka je, za čo ho bude prekladač považovať a ktorý segment sa pri výpočte adresy použije. Pravidlá sú nasledujúce: Ak sa v odkaze vyskytujú dva registre, len jeden z nich sa môže násobiť, a ten sa potom považuje za indexový register; druhý register bude bázový. Ak nie je použité násobenie, ľavý register bude bázový, pravý indexový. Ak je použitý len jeden register, považuje sa za bázový bez ohľadu na to, či ho násobíme konštantou alebo nie. 

Hore

Načo je vám jazyk symbolických adries?

Architektúra moderných procesorov

Registre

Spôsoby adresovania

Premenné a návestia

Symbolické konštanty

Inštrukčný súbor

Segmentové direktívy

Moduly

Služby operačného systému MS-DOS

Služby operačného systému Windows 95/98/NT/XP

Systémová úroveň vstupu a výstupu

Prostriedky pre prípravu programu

Register príznakov | Premenné a návestia
Vydala Žilinská univerzita v Žiline, 2000. ISBN 80-7100-723-4.
Otázky a pripomienky môžete poslať autorke.
Naposledy upravené 28.9.2015.