A ZH-kban gyakran előfordult hibák




  1. 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.)

  2. 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!

  3. 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.

  4. Regiszterpár használata operandusként

    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
         
  5. Érvénytelen adat betöltése szegmensregiszterbe

    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.

  6. A 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.

  7. Endianizmus mellőzése

    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.

  8. 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.

  9. AL, AH, AX vagy DX használata osztóként

    Egy-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.

  10. Illegális címzési mód használata

    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.

  11. Pascal-szerű tömbindexelés használata

    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.

  12. Konstanst jelölő szimbólum nem megfelelő használata

    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.

  13. 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.

  14. 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.

  15. 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.

  16. 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.

  17. MUL vagy IMUL konstans operandussal

    Az 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.

  18. Több, kevesebb ill. érvénytelen operandus használata

    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.

  19. Rossz ciklusszervezés

    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!

  20. Adatmozgatás általános regiszterbe

    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.

  21. 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.