LEKCJA16.TXT

(18 KB) Pobierz
LEKCJA 16 - ASEMBLER TASM i BASM.  
________________________________________________________________ 
W trakcie tej lekcji:  
* dowiesz si� , jak ��czy� C++ z assemblerem  
* poznasz wewn�trzne formaty danych  
________________________________________________________________ 
 
WEWN�TRZNY FORMAT DANYCH I WSPӣPRACA Z ASSEMBLEREM.  
 
W zale�no�ci od wybranej wersji kompilatora C++ zasady  
wsp�pracy z asemblerem mog� si� troch� r�ni�. Generalnie,  
kompilatory wsp�pracuj� z tzw. asemblerami in-line (np. BASM),  
lub asemblerami zewn�trznymi (stand alone assembler np. MASM,  
TASM). Wstawki w programie napisane w assemblerze powinny zosta� 
 
poprzedzone s�owem asm (BORLAND/Turbo C++), b�d� _asm (Microsoft 
 
C++). Przy kompilacji nale�y zatem stosownie do wybranego  
kompilatora przestrzega� specyficznych zasad wsp�pracy. Np. dla 
 
BORLAND/Turbo C++ mo�na stosowa� do kompilacji BCC.EXE/TCC.EXE  
przy zachowaniu warunku, �e TASM.EXE jest dost�pny na dysku w  
bie��cym katalogu.  
 
Typowymi sposobami wykorzystania assemblera z poziomu C++ s�:  
 
* umieszczenie ci�gu instrukcji assemblera bezpo�rednio w  
  �r�d�owym tek�cie programu napisanym w j�zyku C/C++,  
* do��czeniu do programu zewn�trznych modu��w (np. funkcji)  
  napisanych w assemblerze. 
 
W C++ w tek�cie �r�d�owym programu blok napisany w asemblerze  
powinien zosta� poprzedzony s�owem kluczowym asm (lub _asm):  
 
# pragma inline  
 
void main()   
{   
        asm mov dl, 81  
        asm mov ah, 2   
        asm int 33   
}   
  
Program b�dzie drukowa� na ekranie liter� "Q" (ASCII 81).  
 
JAK POS�UGIWA� SI� DANYMI W ASEMBLERZE. 
 
Napiszemy w asemblerze program drukuj�cy na ekranie napis "tekst 
 
- test". Rozpczynamy od zadeklarowania �a�cucha znak�w: 
 
void main()  
{  
   char *NAPIS = "tekst - test$";     /* $ - ozn. koniec */ 
 
Umie�cili�my w pami�ci �a�cuch, b�d�cy w istocie tablic�  
sk�adaj�c� si� z element�w typu char. Wska�nik do �a�cucha mo�e  
zosta� zast�piony nazw�-identyfikatorem tablicy. Zwr�� uwag�, �e 
 
po �a�cuchu znakowym dodali�my znak '$'. Dzi�ki temu mo�emy  
skorzysta� z DOS'owskiej funkcji nr 9 (string-printing DOS  
service 9). Mo�emy utworzy� kod w asemblerze:  
 
asm mov dx, NAPIS  
asm mov ah, 9  
asm int 33  
 
Ca�y program b�dzie wygl�da� tak:  
 
[P054.CPP]  
 
# pragma inline  
void main()  
{  
  char *NAPIS = "\n tekst - test $";  
  
  asm {  
       MOV DX, NAPIS  
       MOV AH, 9  
       INT 33 
      }  
} 
  
Zmienna NAPIS jest pointerem i wskazuje adres w pami�ci, od  
kt�rego rozpoczyna si� �a�cuch znak�w. Mo�emy przes�a� zmienn�  
NAPIS bezpo�rednio do rejestru i przekaza� wprost przerywaniu  
Int 33. Program assemblerowski (tu: TASM) m�g�by wygl�da� np.  
tak:  
 
[P055.ASM]  
 
      .MODEL SMALL      ;To zwylke robi TCC 
      .STACK 100H       ;TCC dodaje standardowo 4K 
      .DATA  
NAPIS DB     'tekst - test','$'  
      .CODE  
START:        
       MOV AX, @DATA  
       MOV DS, AX       ;Ustawienie segmentu danych 
ASM:  
       MOV DX, OFFSET NAPIS  
       MOV AH, 9  
       INT 21H          ;Drukowanie 
KONIEC:  
       MOV AH, 4CH  
       INT 21H          ;Zako�czenie programu 
       END START  
 
Inne typy danych mo�emy stosowa� podobnie. Wygodn� taktyk� jest  
deklarowanie danych w tej cz�ci programu, kt�ra zosta�a  
napisana w C++, aby inne fragmenty programu mog�y si� do tych  
danych odwo�ywa�. Mo�emy we wstawce asemblerowskiej odwo�ywa�  
si� do tych danych w taki spos�b, jakgdyby zosta�y zadeklarowane 
 
przy u�yciu dyrektyw DB, b�d� DW.  
 
WEWN�TRZNE FORMATY DANYCH W C++.  
 
LICZBY CA�KOWITE typ�w char, short int i long int.  
 
Liczba ca�kowita typu short int stanowi 16-bitowe s�owo i mo�e  
zosta� zastosowana np. w taki spos�b:  
 
[P056.CPP]  
 
#pragma inline 
void main()  
{  
  char *napis = "\nRazem warzyw:  $";  
  int marchewki = 2, pietruszki = 5;  
  asm {  
        MOV     DX, napis  
        MOV     AH, 9  
        INT     33  
        MOV     DX, marchewki  
        ADD     DX, pietruszki  
        ADD     DX, '0'  
        MOV     AH, 2  
        INT     33  
      }  
}  
 
Zdefiniowali�my dwie liczby ca�kowite i �a�cuch znak�w - napis.  
Poniewa� obie zmienne (�a�cuch znk�w jest sta��) maj� d�ugo��  
jednego s�owa maszynowego, to efekt jest taki sam, jakgdyby  
zmienne zosta�y zadeklarowane przy pomocy dyrektywy asemblera DW 
 
(define word). Mo�emy pobra� warto�� zmiennej marchewki do  
rejestru instrukcj�  
 
MOV DX, marchewki         ;marchewki -> DX 
 
W rejestrze DX dokonujemy dodawania obu zmiennych i wyprowadzamy 
 
na ekran sum�, pos�uguj�c si� funkcj� 2 przerywania DOS 33  
(21H).  
 
W wyniku dzia�ania tego programu otrzymamy na ekranie napis:  
 
Razem warzyw:  7 
 
Jeczsze jeden szczeg� techniczny. Poniewa� stosowana funkcja  
DOS pracuje w trybie znakowym i wydrukuje nam znak o kodzie  
ASCII przechowywanym w rejestrze, potrzebna jest manipulacja:  
 
ADD DX, '0'   ;Dodaj kod ASCII "zera" do rejestru  
 
Mo�esz sam sprawdzi�, �e po przekroczeniu warto�ci 9 przez sum�  
wszystko si� troch� skomplikuje (kod ASCII zera - 48). Z r�wnym  
skutkiem mo�naby zastosowa� rozkaz  
 
ADD DX, 48 
 
Je�li prawid�owo dobierzemy format danych, fragment programu  
napisany w asemblerze mo�e korzysta� z danych dok�adnie tak  
samo, jak ka�dy inny fragment programu napisany w C/C++. Mo�emy  
zastosowa� dane o jednobajtowej d�ugo�ci (je�li drugi, pusty  
bajt nie jest nam potrzebny). Zwr�� uwag�, �e pos�ugujemy si� w  
tym przypadku tylko "po��wk�" rejestru DL (L - Low - m�odszy).  
 
[P057.CPP] 
 
#pragma inline 
void main()  
{  
    const char *napis = "\nRazem warzyw:  $";  
    char marchewki = 2, pietruszki = 5;  
    asm {  
        MOV     DX, napis  
        MOV     AH, 9  
        INT     33  
        MOV     DL, marchewki 
        ADD     DL, pietruszki 
        ADD     DL, '0'  
        MOV     AH, 2  
        INT     33  
      } 
}  
 
W tej wersji zadeklarowali�my zmienne marchewki i pietruszki  
jako zmienne typu char, co jest r�wnoznaczne zadeklarowaniu ich  
przy pomocy dyrektywy DB.  
 
Zajmijmy si� teraz maszynow� reprezentacj� liczb typu unsigned  
long int (d�ugie ca�kowite bez znaku). Ze wzgl�du na specyfik�  
zapisu danych do pami�ci przez mikroprocesory rodziny Intel  
80x86 d�ugie liczby ca�kowite (podw�jne s�owo - double word) np. 
 
12345678(hex) s� przechowywane w pami�ci w odwr�conym szyku.  
Zamieniony miejscami zostaje starszy bajt z m�odszym jak r�wnie� 
 
starsze s�owo z m�odszym s�owem. Liczba 12345678(hex) zostanie  
zapisana w pami�ci komputera IBM PC jako 78 56 34 12.  
 
Gdy inicjujemy w programie zmienn�   
 
long int x = 2;  
 
zostaje ona umieszczona w pami�ci tak:   02 00 00 00 (hex). 
M�odsze s�owo (02 00) jest umieszczone jako pierwsze. To w�a�nie 
 
s�owo zawiera interesuj�c� nas informacj�, mo�emy wczyta� to  
s�owo do rejestru rozkazem  
 
MOV DX, X  
 
Je�li b�dzie nam potrzebna druga po��wka zmiennej - starsze  
s�owo (umieszczone w pami�ci jako nast�pne), mo�emy zastosowa�  
pointer (czyli poda� adres nast�pnego s�owa pami�ci).  
 
[P058.CPP]  
 
# pragma inline 
void main()  
{  
    unsigned long marchewki = 2, pietruszki = 5; 
    const char *napis = "\nRazem warzyw:  $";  
    asm  
      {  
        MOV     DX, napis  
        MOV     AH, 9  
        INT     33  
        MOV     DX, marchewki 
        ADD     DX, pietruszki 
        ADD     DX, '0'  
        MOV     AH, 2  
        INT     33  
      }  
}  
 
W przypadku liczb ca�kowitych ujemnych C++ stosuje zapis w  
kodzie komplementarnym. Aby m�c manipulowa� takimi danymi ka�dy  
szanuj�cy si� komputer powinien mie� mo�liwo�� stosowania liczb  
ujemnych.  
 
Najstarszy bit w s�owie, b�d� bajcie (pierwszy z lewej) mo�e  
spe�nia� rol� bitu znakowego. O tym, czy liczba jest ze znakiem, 
 
czy te� bez decyduje wy��cznie to, czy zwracamy uwag� na ten  
bit. W liczbach bez znaku, oboj�tnie, czy o d�ugo�ci s�owa, czy  
bajtu, ten bit r�wnie� jest (i by� tam zawsze!), ale  
traktowali�my go, jako najstarszy bit nie przydaj�c mu poza tym  
�adnego szczeg�lnego znaczenia. Aby liczba sta�a si� liczb� ze  
znakiem - to my musimy zacz�� j� traktowa� jako liczb� ze  
znakiem, czyli zacz�� zwraca� uwag� na ten pierwszy bit.  
Pierwszy, najstarszy bit liczby ustawiony do stanu 1 b�dzie  
oznacza�, �e liczba jest ujemna - je�li zechcemy j� potraktowa�  
jako liczb� ze znakiem.  
 
Filozofia post�powania z liczbami ujemnymi opiera si� na  
banalnym fakcie: 
 
  (-1) + 1 = 0  
 
Tw�j PC "rozumuje" tak: -1 to taka liczba, kt�ra po dodaniu 1  
stanie si� 0. Czy mo�na jednak�e wyobrazi� sobie np.  
jednobajtow� liczb� dw�jkow�, kt�ra po dodaniu 1 da nam w  
rezultacie 0 ? Wydawa�oby si�, �e w dowolnym przypadku wynik  
powinien by� conajmniej r�wny 1.  
 
A jednak. Je�li ograniczymy swoje rozwa�ania do o�miu bit�w  
jednego bajtu, mo�e wyst�pi� taka, absurdalna tylko z pozoru  
sytuacja. Je�li np. dodamy 255 + 1 (dw�jkowo 255 = 11111111):  
 
              1111 1111    hex    FF    dec    255 
                 +    1          + 1          +  1 
            ___________         _____        _____ 
            1 0000 0000          100           256 
  
 
otrzymamy 1 0000 0000 (hex 100). Dla Twojego PC oznacza to, �e w 
 
o�miobitowym rejestrze pozostanie 0000 0000 , czyli po prostu 0. 
 
Nast�pi natomiast przeniesienie (carry) do dziewi�tego (nie  
zawsze istniej�cego sprz�towo bitu). 
 
Wyst�pienie przeniesienia powoduje ustawienie flagi CARRY w  
rejestrze FLAGS. Je�li zignorujemy flag� i b�dziemy bra� pod  
uwag� tylko te osiem bit�w w rejestrze, oka�e si�, �e  
otrzymali�my wynik 0000 0000. Kr�tko m�wi�c FF = (-1), poniewa�  
FF + 1 = 0.  
 
Aby odwr�ci�...
Zgłoś jeśli naruszono regulamin