LEA
utasítás nem memóriaoperandussal
A LEA
utasítás céloperandusa csak egy 16
bites általános regiszter (tehát AX
,
BX
, CX
, DX
, SI
,
DI
, SP
vagy BP
) lehet,
míg forrásként kötelezően valamilyen,
a memóriában elhelyezkedő operandust kell megadni.
Ezért bármilyen ettől eltérő
használatú változat, így pl. a LEA
SI,SI
is illegális. (Mellékesen ennek az alaknak
nem is lenne értelme.)
IMUL
utasítás kettő vagy több
operandussal
A 8086-os/8088-as processzorokon az IMUL
utasítás csak 1 operandust kaphat, amely operandus
memóriahivatkozás vagy általános regiszter
lehet. Ha az operandus mérete 8 bit, akkor AL
-t
szorozza meg az operandussal, és a szorzat AX
-be
kerül, míg 16 bites operandus esetén AX
-et
szorozza meg az operandussal, a szorzatot pedig DX:AX
-be teszi.
A 80186-os/80188-as processzoroktól kezdve az IMUL
már 3 operandust is kaphat: az elsőnek egy 16 bites
általános regiszternek kell lennie, a második
szokás szerint egy 16 bites ált. regiszter vagy
memóriahiv. lehet, míg harmadik operandusként egy 8
vagy 16 bites konstanst adhatunk meg. Az IMUL BX,[SI],3
utasítás pl. a DS:SI
címen levő
szót megszorozza a +3-mal, majd az eredmény alsó
szavát BX
-be rakja.
A 80386-os processzortól kezdve az utasítás 2
operandussal is használható. Az első op. 16 vagy 32
bites ált. reg., míg a második op. 16 vagy 32 bites
ált. reg. vagy memóriahiv. lehet. Az IMUL BX,CX
utasítás hatására pl. BX
-be a
BX*CX
szorzat alsó szava kerül.
Egyszóval tehát, hacsak nem kifejezetten az újabb generációs processzorokon futó programot írunk szándékosan, ne használjunk 1-nél több operandust ezzel az utasítással!
MUL
utasítás egynél több
operandussal
Az IMUL
-lal ellentétben a MUL
utasítás kizárólag 1 operandust
kaphat, amely
operandus vagy ált. regiszter vagy memóriahivatkozás
lehet. Ezért pl. a MUL BX,N
utasítás
érvénytelen lesz.
A jegyzetben és sok szakkönyvben is elterjedten
használt DX:AX
jelölés arra utal, hogy egy
32 bites szám (esetleg memóriacím) alsó szava
AX
-ben, míg felső szava DX
-ben
található. Azaz ezt a két regisztert mi most nem
függetlennek tekintjük, hanem egyetlen értéket
tároló egységként kezeljük. Ez a
jelölés azonban csak az embereknek, a programozónak
szól, és felhasználható pl. az algoritmus
pszeudokódban való leírásánál.
Az Assembly nyelvű forrásprogramban viszont nem
szerepelhet
ilyen rövidítés operandusként. Így pl. a
MOV [SI],(DX:AX)
utasítás illegális, de
felváltható pl. a következő sorokkal:
MOV WORD PTR [SI],AX
MOV WORD PTR [SI+2],DX
A 8086-os/8088-as processzoron összesen 4 szegmensregiszter van:
CS
, DS
, ES
és
SS
. Mindegyik szegmensregiszter 16 bites, és
nevükből adódóan a memóriaoperandusok
szegmenscímét (pontosabban annak a felső 16
bitjét) tárolják. Fontos megjegyezni,
hogy nem lehet mindegyik ilyen regiszterbe bármilyen
értéket betölteni.
A CS
regiszter tartalma kizárólag a
JMP
, CALL
, RET
, INT
,
INTO
és IRET
vezérlésátadó utasításokkal
változtatható meg.
A DS
és ES
regiszterekbe 3 módon
tölthetünk valamit. Egyik lehetőség, hogy a
POP DS
vagy a POP ES
utasításokat
használjuk. Szintén jók erre a célra az
LDS
és LES
utasítások is.
Végül pedig a MOV DS,?
vagy a MOV
ES,?
utasítások alkalmazása jelentheti a
megoldást.
Az SS
regiszter tartalmát vagy a POP SS
,
vagy a MOV SS,?
utasításokkal
változtathatjuk meg.
Bár szerencsére csak 1 dolgozatban szerepelt, de
azért csak megemlítjük a következő
nagyon durva hibát: MOV DS,DX:AX
.
LOOP
utasítás szakszerűtlen
használata
A LOOP
utasítás a CX
regisztert
használja számlálóként, így ha
csak CL
-t töltjük fel, CH
-t nem,
úgy nem megfelelő eredményt kapunk.
A másik fontos dolog, amit illik megjegyezni, az, hogy ha
CX
=0, akkor a LOOP
nem hagyja abba
tevékenységét, hanem, miután
CX
-et csökkentette, még 65535-ször
végre fogja hajtani a ciklusmagot.
Néhány dolgozatban előfordult, hogy egy több bájtból álló érték mozgatásakor nem megfelelő címre rakták az egyes bájtokat. Nem szabad elfelejteni, hogy az alacsonyabb helyiértékű bájtok alacsonyabb, míg a magasabb helyiértékűek magasabb címen helyezkednek el a memóriában.
AL
, AH
, AX
vagy DX
használata
szorzóként
Azzal nincs semmi gond, ha az említett regiszterek valamelyikét használjuk
a MUL
vagy IMUL
operandusaként. Arra
azonban figyelni kell, hogy mivel a szorzat AX
-ben ill. DX:AX
-ben
keletkezik, AX
és esetleg DX
korábbi tartalma is
megsemmisülhet. Így pl. a MUL AX
utasítás az AX
-ben levő előjeltelen
érték négyzetét számolja ki, az eredményt pedig DX:AX
-ben tárolja el.
AL
, AH
, AX
vagy DX
használata osztókéntEgy-két dolgozatban előfordult az alábbi utasítások valamelyike:
DIV AL
DIV AH
DIV AX
DIV DX
Mind a 4 utasítás ugyanazon ok miatt rossz: az osztóként megadott regiszter része az osztandónak. Ennek igen ritkán lehet valóban értelme, ezért ne használjuk ezeket az alakokat.
Nagyon sok dolgozatban találkoztunk különféle
"egzotikus" címzési móddal. Hogy csak
néhány példát említsek, a
következők mind érvénytelenek:
[SI+DI]
, [BX-DI]
, [CX+DX]
,
[(SI+CX)*4]
, [SP+2]
, [SI*N+DI+BX]
,
[BL]
, [BH]
, [EVEKTOR[1]+SI]
,
[EMATRIX+(AX+CX*N)*4]
, 2*[MATRIX+(AX*N+CX)*4]
.
A szabályos címzési módok a
következő alakot ölthetik: [konst.kif.]
,
[BX]
, [BX+konst.kif.]
, [BP]
,
[BP+konst.kif.]
, [SI]
,
[SI+konst.kif.]
, [DI]
,
[DI+konst.kif.]
, [BX+SI]
,
[BX+SI+konst.kif.]
, [BX+DI]
,
[BX+DI+konst.kif.]
, [BP+SI]
,
[BP+SI+konst.kif.]
, [BP+DI]
és
[BP+DI+konst.kif.]
, ahol konst.kif.
a konstans
kifejezést (tehát a csak számokat, kontans
szimbólumokat és műveleti jeleket tartalmazó
jelöléseket) rövidíti.
A memória elérésekor az offszetcímet a
címzési móddal adjuk meg. Ehhez
kizárólag csak a BX
,
BP
,
SI
és DI
regiszterek tartalma
használható fel. Mint látható, vagy csak egy
indexregiszter (SI
vagy DI
), vagy csak egy
bázisregiszter (BX
vagy BP
), vagy egy
index és egy bázisregiszter fordulhat elő ezekben
egyszerre. Kivételt képez a közvetlen
címzés, mivel ilyenkor egyetlen érték, egy 16
bites konstans kifejezés adja meg az offszetet.
Az előbb említett regiszterek nem szerepelhetnek
negatív előjellel, illetve nem szorozhatók meg
semmilyen értékkel, még +1-gyel sem!
Negatív előjelet csak a konstans kifejezés
kaphat. Ezért pl. az [SI-BX]
helytelen
címzésmód, a [DI-2*5]
viszont
helyes.
Bár SP
-t használja a PUSH
és POP
utasítás, ezt a regisztert
mégsem alkalmazhatjuk a memória
megcímzésére. Használjuk helyette mondjuk a
BP
-t.
Csak 1 dolgozatban szerepelt a következő furcsa, ámde
nagyon súlyos hiba. Az illető az eredményt a
MOV EMATRIX[BL,BH],DX:AX
utasítással
próbálta meg eltárolni az EMATRIX
mátrixba. Ebben az utasításban
"mindössze" 3 hiba van. Egyrészt a
forrásoperandus helyén egy regiszterpár szerepel, ami
illegális a korábban már említett okok miatt.
Másrészt a céloperandus egy olyan
memóriahivatkozás, ami 2 okból is rossz.
Szintén korábban volt szó arról, hogy a
memória elérésére nem alkalmazhatók
pl. a BL
és BH
regiszterek.
Ezenkívül nem tudjuk, mit akarna jelenteni a
[BL,BH]
jelölés, de ha az a Pascal nyelvben
megszokott tömbindexelés lenne, akkor főleg elég
csúnya a hiba.
Mindegyik ZH feladatsorban szerepelt valamilyen konstans
szimbólum, ilyen voltak pl. N és L is. Ha ennek a
szimbólumnak az értékét akarjuk valamely
változóba vagy regiszterbe tölteni, akkor ezt a
MOV ?,szimbólum
módon tegyük meg, ne pedig
a, néhány dolgozatban szereplő, következő
hibás alakkal: MOV ?,[szimbólum]
. Ez
utóbbi alak ugyanis második operandusként egy
memóriahivatkozást ír elő,
méghozzá olyan közvetlen címzési
módot, ahol az offszetcím értéke
szimbólum
lesz.
BP
-t tartalmazó címzési mód
használata
Ha olyan címzési módot alkalmazunk, ami
BP
-t tartalmazza, vegyük figyelembe, hogy ilyenkor a
megszokott DS
szegmensregiszter helyett SS
-t
használja a processzor, hacsak másképpen nem
rendelkezünk.
CWD
használata IMUL
előtt
Előjeles osztás előtt szokás alkalmazni a
CWD
utasítást akkor, ha az osztandó csak
16 bites, ezért azt mi előjelesen kiterjesztve 32
bitessé alakítjuk át. Szorzás esetén
viszont abszolút felesleges ezt megtennünk, sőt
kifejezetten káros is pl. akkor, ha mondjuk DX
-et
szoroznánk meg AX
-szel, mivel ilyenkor
az egyik szorzótényező (DX
) értéke már a szorzás előtt elveszhet.
MOV
kettő memóriaoperandussal
A MOV
utasításnak egyszerre csak egy operandusa
lehet memóriahivatkozás. Ha memóriából
memóriába szeretnénk mozgatni adatokat, akkor ezt
két lépésben tegyük meg, valamely regiszter vagy
a verem bevonásával.
ADD
vagy egyéb kétoperandusú
utasítás két memóriaoperandussal
Az ilyen utasításokra ugyanaz vonatkozik, mint az
előző pontban említett MOV
-ra.
Egyedüli kivételnek a MOVS
és
CMPS
sztringkezelő utasítások
számítanak, viszont az összes többi
kétoperandusú utasításnak egyszerre csak egy
memóriaoperandusa lehet! Ezért pl. az ADD
A[BX],B[BX]
utasítás illegális.
MUL
vagy IMUL
konstans operandussalAz említett két utasítás nem kaphat konstans értéket operandusként. Ha tehát egy konstanssal akarunk valamit megszorozni, vagy rakjuk azt az értéket egy változóba vagy regiszterbe, és használjuk azt operandusként, vagy pedig használjuk a léptető utasításokat, esetleg az összeadó-kivonó utasításokat a feladat elvégzésére.
A következő utasítások mind illegálisak valami miatt:
CBW ?,AX
SHL DX
MOV SI,VEKTOR+AX
INC CX,4
LEA [BX+4]
A CBW
nem kaphat egyetlen operandust sem.
Az SHL
mindig kettő operandussal kell hogy
szerepeljen, ebből a második vagy az 1-es
érték, vagy a CL
regiszter lehet.
A MOV
utasítás második operandusa egy
érvénytelen kifejezés. Ha ez egy
memóriahivatkozás akart volna lenni, akkor sem
szerepelhetne benne az AX
regiszter.
Az INC
egyoperandusú utasítás,
és ez az operandus vagy ált. reg., vagy
memóriahivatkozás lehet csak. Mellesleg az INC
1-gyel növeli operandusát, 4-gyel növelésre
pedig mondjuk az ADD
utasítást
célszerű használni.
A LEA
utasításról korábban
már volt szó, szintaxisának
leírását lásd ott.
A következő sorok hibás módon szervezik meg a ciklust:
...
@Ciklus:
...
DEC CX
JCXZ @Vege
LOOP @Ciklus
@Vege:
...
Ez a megoldás duplán rossz. Először is
CX
tartalma minden iteráció után 2-vel
csökken (először a DEC
, majd a
LOOP
miatt), ez tehát eleve elrontja a
számláló értékét.
Másodszor pedig felesleges a JCXZ ...
ellenőrzés, mivel azt a LOOP
is elvégzi.
Ilyet tehát ne csináljunk!
A korrektség kedvéért még megjegyezzük,
hogy ha csak elírás miatt szerepel LOOP
,
s helyette JMP
-t írunk, akkor már
helyes megoldáshoz jutunk!
Néhány dolgozatban egy furcsasággal találkoztunk. Sokan a következő módon töltöttek egy értéket valamilyen ált. regiszterbe:
XOR BX,BX
MOV BX,?
Nem tudni, mi szükség volt az illető regiszter kitörlésére az adatbetöltés előtt, mindenesetre az első sorra semmi szükség az ilyen esetekben.
INTO
utasítás operandussal
Az INTO
utasítás nem kaphat semmilyen
operandust sem. Ha az OF
=1 feltétel esetén a
saját hibakezelőnket akarjuk meghívni, akkor azt pl.
a JNO
-CALL
utasításpárral
tegyük meg inkább.
Módosítva: 2003. október 22.