Stała
symboliczna tworzona jest poprzez powiązanie identyfikatora z liczbą lub
tekstem. Taka stała nie zajmuje miejsca w pamięci, wystąpienia jej
identyfikatora w programie zamieniane są na jej wartość przez tłumaczeniem
programu na kod maszynowy. W związku z tym wartość takiej stałej nie może
zostać zmieniona w trakcie działania programu.
Przypisanie
Przypisanie
wiąże ze sobą nazwę i wyrażenie liczbowe za pomocą znaku =. Kiedy program jest
tłumaczony na kod maszynowy, wszystkie wystąpienia danego symbolu zamieniane są
na jego wartość.
Kod
programu:
.386
.model flat, stdcall
.stack 4096
include kernel32.inc
includelib kernel32.lib
.data
.code
main PROC
LICZBA = 1000
mov eax, liczba
invoke ExitProcess, eax ;tutaj ustaw breakpoint
main ENDP
END main
|
Podczas
tłumaczenia programu, dany kod:
mov eax, liczba
|
Zostanie
przetłumaczony na:
mov eax, 1000
|
Wynik
działania programu
Do
czego może się to przydać? Takie działanie jest przydatne, gdy w kodzie
programu używamy jednej wartości wiele razy. Gdy zajdzie potrzeba jej zmiany,
nie musimy ręcznie zmieniać jej wystąpienia na każdej linijce, wystarczy
zmienić jej wartość przypisaną do stałej.
Stała
taka może ulegać redefinicji. Przechodząc przez, każdą instrukcję programu
klawiszem F10 można zobaczyć jak wartość rejestru EAX ulega zmianie.
.386
.model flat, stdcall
.stack 4096
include kernel32.inc
includelib kernel32.lib
.data
.code
main PROC
mov eax, 0
LICZBA = 10
mov al, liczba
LICZBA = 20
mov al, liczba
LICZBA = 30
mov al, liczba
invoke ExitProcess, eax
main ENDP
END main
|
Wskaźnik aktualnej lokalizacji $
Korzystając
z tablic zazwyczaj chcielibyśmy znać jej rozmiar. Można go zadeklarować w
sposób bezpośredni.
Tablica
czterech bajtów będzie zajmowała 4 bajty w pamięci.
.data
tablicaByte BYTE 10, 20, 30, 40
tablicaByteRozmiar = 4
|
Ale,
żeby nie liczyć rozmiaru tablicy, można użyć do tego celu wskaźnika aktualnej
lokalizacji $.
Kod
|
Wynik
|
.386
.model flat, stdcall
.stack 4096
include kernel32.inc
includelib kernel32.lib
.data
tablica BYTE 10, 20, 30, 40
rozmiar = ($ - tablica)
.code
main PROC
mov eax, rozmiar
invoke ExitProcess, eax
main ENDP
END main
|
EAX
= 4
|
Nazwa
tablica jest adresem pierwszego elementu tej tablicy. Pod stałą rozmiar
znajduje się adres tego miejsca w kodzie – adres tablica.
Załóżmy,
że adresacja wygląda w następujący sposób
Adres
w bajtach
|
Zawartość
pamięci
|
1000
|
10
|
1001
|
20
|
1002
|
30
|
1003
|
40
|
1004
|
???
|
Adres
1004 oznaczony jest jako ??? ponieważ, nie wiemy co się znajduje pod tym
adresem, nasza tablica sięga tylko do adresu 1003. Natomiast podczas
tłumaczenia programu asembler wie, gdzie skończyła się tablica i gdzie się
zaczeła. Stała rozmiar znajduje się tuż po tablicy więc, gdyby była to zmienna
otrzymałaby adres 1004 i asembler o tym wie, więc może podstawić ten adres pod
znak $. Więc w tym wyrażniu:
rozmiar = ($ - tablica)
|
Podstawi
następujące wartości
rozmiar = (1004 - 1000)
|
I
w stałej rozmiar zapisze wartość 4 czyli ilość elementów w tablicy.
W
ten sposób można liczyć każdy rozmiar, należy tylko wziąć pod uwagę jaki
rozmiar ma pojedynczy element. Dane typu WORD, zajmują 16 bitów czyli 2 bajty,
dlatego żeby uzyskać ilość elementów należy otrzymaną liczbę (czyli ilość
bajtów) podzielić przez 2.
Kod
|
Wynik
|
.386
.model flat, stdcall
.stack 4096
include kernel32.inc
includelib kernel32.lib
.data
tablica WORD 1000h, 2000h, 3000h, 4000h
rozmiar = ($ - tablica) / 2
.code
main PROC
mov eax, rozmiar
invoke ExitProcess, eax
main ENDP
END main
|
EAX = 4
|
Kolejny
przykład, każdy znak zajmuje jeden bajt.
Kod
|
Wynik
|
.386
.model flat, stdcall
.stack 4096
include kernel32.inc
includelib kernel32.lib
.data
zdanie BYTE "Ala ma kota"
BYTE " bo siertka ma
rysia"
rozmiar = ($ - zdanie)
.code
main PROC
mov eax, rozmiar
invoke ExitProcess, eax
main ENDP
END main
|
EAX
= 31
|
Dyrektywa EQU
Dyrektywa
EQU wiąże pewną nazwę z jakimś wyrażeniem
nazwa EQU wyrazenie
nazwa EQU symbol
nazwa EQU <tekst>
|
W
pierwszym przypadku wyrażenie musi być poprawnym wyrażeniem liczbowym. W drugim
przypadku symbol to jeden z istniejących już symbol zdefiniowanych przez = lub
przez EQU. W trzecim przypadku między <> może wystąpić dowolny tekst.
Przykłady
macierz1 EQU 10 * 10
macierz2 EQU <10 * 10>
.data
M1 WORD macierz1
M2 WORD macierz2
|
Macierz1
ma przypisaną od razu wartość 100 ponieważ 10 * 10 jest to wyrażenie. Podczas
tworzenia zmiennej M1 pod symbol macierz1 zostanie podstawiona wartość 100.
Macierz2 ma przypisany ciąg znaków 10 * 10. Podczas tworzenia zmiennej M1 pod
symbol macierz2 zostanie podstawiony ciąg znaków 10 * 10, który w tym przypadku
też jest wyrażeniem. Asembler zobaczy, że jest tam 10 * 10 i dopiero wstawi
wartość tego wyrażenia czyli 100.
M1 WORD 100
M2 WORD 10 * 10
|
W
przeciwieństwie do symboli zdefiniowanych przez = symbole zdefiniowane przez
EQU nie mogą ulec redefinicji.
Dyrektywa TEXTEQU
Ta
dyrektywa tworzy element zwany makrem tekstowy. Są różne formaty takiego makra.
Pierwszy przypisuje tekst, drugi przypisuje zawartość wcześniej zdefiniowanego
makra, a trzeci przypisuje wyrażenie liczbowe.
nazwa TEXTEQU <tekst>
nazwa TEXTEQU makrotekstowe
nazwa TEXTEQU %wyrazanie
|
Przykład
użycia:
zapytanie TEXTEQU <"Czy chcesz
kontynuowac (T/N)?",0>
.data
tekst1 BYTE zapytanie
|
fv
OdpowiedzUsuń