kurs asemblera.doc

(136 KB) Pobierz
Jeśli znasz pascala to wiesz, że wstawki w assemblerze możesz umieszczać np

Kurs Asemblera

 

Jeśli znasz Pascala to wiesz, że wstawki w asemblerze możesz umieszczać np. w pod procedurach programu. Jeśli jednak zamierzasz pracować w samym tylko asemblerze, to nawet w sieci dostępny jest cały wachlarz kompilatorów tego języka. Jeśli chodzi o popularność, to najpopularniejszy jest Turbo Assembler firmy Borland - zamieszczone w kursie przykłady są pisane właśnie z myślą o tym kompilatorze.
Oczywiście różnic w samym asemblerze nie ma - dotyczą natomiast struktury programu, deklaracji poszczególnych segmentów itd. Pakiet Taśm służy do kompilacji kodu w asemblerze na postać programu wykonywalnego, ale do stworzenia samego programu w asemblerze może posłużyć każdy edytor umożliwiający zapis pliku w postaci czystego tekstu - np. edytor z pakietu Norton Commander.

Podstawową wadą TASM jest jednak fakt, że jest to pakiet komercyjny tzn. nie jest udostępniany w sieci za darmo, pozostaje więc zakup profesjonalnego pakietu, lub używanie programów kompatybilnych. Jeśli nie posiadasz TASM, to polecam twojej uwadze udostępniony w konwencji public domain, kompilator ARROWSOFT ASSEMBLER.

Jeśli chodzi o ścisłość, to do programowania w asemblerze wystarczyłby program debug.exe w pakiecie MS-DOS, ale ponieważ jest dość toporny w użyciu - nikt go praktycznie na poważnie nie stosuje.

Cały kurs asemblera był dotychczas przygotowywany pod kątem Turbo Assemblera, programu, który uchodzi za najlepszy i najpopularniejszy kompilator ASM. Już w kilka dni po opublikowaniu pierwszych wersji kursu na sieci, zaczęło padać pytanie "skąd wziąć TASM?" - faktem jest bowiem, że pakiet ten jako komercyjny nie został udostępniony "do wzięcia" w internecie. Podjęte przeze mnie poszukiwania programów kompatybilnych nie przynosiły przez dłuższy czas efektów, aż do momentu odkrycia assemblera ARROWSOFT, który spełniał praktycznie wszystkie założone kryteria w zadowalającym zakresie:

1.     Może być używany bezpłatnie (freeware lub public domain),

2.      2. Jest kompatybilny z TASM przynajmniej na tyle, by umożliwić bezproblemową kompilację programów na podstawowym poziomie,

3.      Jest możliwie mały tzn. nadaje się do ciągnięcia z inetu. Arrowsoft jest udostępniony w konwencji public domain (istnieje też wersja komercyjna o większych możliwościach), kompiluje programy najwyżej dla 286 (nie udostępnia instrukcji/rejestrów 386 i lepszych procesorów, lecz z tym uniedogodnieniem początkujący programiści nie powinni mieć problemów zwłaszcza jeśli zależy im by oprogramowanie chodziło na wszystkich procesorach), umożliwia kompilację programów, których kod źródłowy nie jest dłuższy niż 64kb, nie rozróżnia dyrektyw typu ".model", ".code" etc. - jest bowiem kompatybilny z MASM 3.0.

Arrowsoft assembler można pobrać np. z Poznańskiego mirrora Simtela, pod adresem: ftp://ftp.man.poznan.pl/pub/simtelnet/msdos/asmutl/valarrow.zip, długość pliku to około 100kb. Pakiet zawiera skróconą dokumentację asemblera jak i sam asembler, linker kompatybilny z masm link, edytor +źródło tego edytora w asemblerze oraz klon programu EXE2BIN również z kodem źródłowym.

 

"Dlaczego kompilować?"

Jak się przekonasz w niedługiej przyszłości, asembler nie do końca jest językiem procesora. Oczywiście prawdą jest to, co pisałem wcześniej, ale nie jest to ścisłe.

Otóż - procesor operuje na instrukcjach zapisanych cyfrowo, podczas gdy asembler jest tekstowym odpowiednikiem tych instrukcji - np:

mov ax,ffffh - instrukcja asemblera - językiem procesora zapisana jest jako seria bajtów - B8h FFh FFh.
(Musisz przyznać, że prościej nauczyć się symboli niż dziwnego systemu numeracji...)

To właśnie proces kompilacji zamienia plik z instrukcjami assemblera na plik z serią bajtów dla procesora - takim właśnie plikiem jest program typu .com.

"Turbo Assembler" 

Do skompilowania programu w asemblerze na postać pliku wykonywalnego (.com) potrzebne są zasadniczo dwa programy z pakietu - tasm.exe i tlink.exe. Tasm.exe kompiluje program w asemblerze (.asm) na postać obiektu (.obj), który później musi zostać skompilowany na właściwy program .com, lub .exe przy pomocy programu tlink.exe. Załóżmy, że mamy plik program.asm i chcemy wygenerować program .com. W tym celu należy najpierw uruchomić program tasm.exe:

TASM.EXE PROGRAM.ASM

W wyniku działania programu tasm otrzymujemy plik program..obj, który następnie należy przepuścić przez program tlink:

TLINK.EXE PROGRAM.OBJ /T

OK, i teraz mamy już plik program .com, który możemy uruchomić.

Dlaczego .com a nie .exe?

W niniejszym kursie nauczymy się tworzyć wyłącznie programy .com - różnic jeśli chodzi o sam język nie ma, ale programy .com mają prostszą strukturę (jeśli chodzi np. o linię poleceń) a poza tym są krótsze niż exe'ki, gdyż nie zawierają kilkusetbajtowego nagłówka charakterystycznego dla plików .exe.
Parametr /T w tlinku powoduje właśnie wygenerowanie programu typu .com.

"Systemy liczbowe"

Systemy dwójkowy i szesnastkowy są to najzwyklejsze w świecie systemy liczenia - tak samo jak dobrze wszystkim znany i powszechnie używany system dziesiętny. Aby najprościej zobrazować sposób działania systemów dwójkowego i szesnastkowego, powinniśmy zacząć od przyjrzenia się systemowi dziesiętnemu.

Weźmy na przykład liczbę 123 - w systemie dziesiętnym. Patrząc od prawej strony - pierwsza cyfra określa ilość jedności, następna - ilość dziesiątek i wreszcie trzecia od prawej - ilość setek. Liczba 123 jest więc równa 1*100+2*10+3*1. Matematycznie jest to 1*10^2+2*10^1+3*10^0. Podobnie sytuacja ma się z układem dwójkowym - tyle, że zamiast dziesięciu cyfr (0 .. 9) mamy tu do dyspozycji tylko dwie - 0 i 1.

Pierwsza cyfra od prawej strony określa nam więc ilość jedności, następna - ilość dwójek, następna - ilość czwórek, ósemek, szesnastek itd.

Jak więc zamienić na układ dwójkowy liczbę 67?
67 =1*64+0*32+0*16+0*8+0*4+1*2+1*1 =1000011

W zapisie matematycznym jest to:
1*2^0+1*2^1+0*2^2+0*2^3+0*2^4+0*2^5+1*2^6.

Wreszcie trzecim układem, którym się zajmiemy, jest układ szesnastkowy (hexadecymalny, stąd symbol "H" dodawany do liczb w tym układzie). Pierwszy problem polega na tym, że potrzebujemy 16 cyfr, aby móc zapisywać liczby w tym układzie - podczas, gdy istnieje tylko 10 cyfr. Aby poradzić sobie z tym problemem, za cyfry 10-16 przyjęto litery A do F, zestaw cyfr w układzie szesnastkowym ma więc postać: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F. Cała reszta jest analogiczna jak w układzie dziesiętnym i dwójkowym, tj. pierwsza od prawej cyfra określa ilość jedności, następna - szesnastek, następna - dwieście pięćdziesiątek szóstek itd. Liczba 123D (decymalna - dziesiętna - zapisana w układzie dziesiętnym) - ma więc w układzie szesnastkowym postać:

7*16+11*1 =7B - ściślej - 7BH - litera H na końcu informuje, że liczba jest zapisana właśnie w układzie szesnastkowym.

Ostatnia kwestia dotyczy powszechności układu szesnastkowego w programowaniu. Jak wiesz, najczęściej spotykaną jednostką informacji jest bajt (bit jest mniej praktyczny i rzadziej używany) - czyli 8 bitów. Na ośmiu bitach, czyli w jednym bajcie - można zapisać 256 liczb (0 .. 255) - 2^8. To właśnie układ szesnastkowy umożliwia zapisanie tych liczb na dwóch znakach - 16*2=256 - 0d=00H, 255D=FFH.

"Procesor"

Procesor - zwany często CPU (od angielskiego "central processing unit") - jest głównym urządzeniem wchodzącym w skład systemu komputera.

To właśnie procesor wykonuje każdy program, odpowiada za komunikację z urządzeniami we/wy i transfery pamięci - słowem bez procesora komputer nie mógłby wogóle funkcjonować będąc tylko nieskoordynowanym splotem przewodów z różnych urządzeń.

Niniejszy kurs objaśni po krótce język, którym możemy rozkazywać procesorowi, terminologię, którą często spotyka się w dyskusjach i wreszcie poda kilka przykładów na spożytkowanie nabytej wiedzy. 

"Porty"

Okazuje się, że tak. To właśnie i wyłącznie przez porty procesor komunikuje się ze wszystkimi urządzeniami - od drukarki, ekranu i klawiatury poprzez modem, cd-rom i kartę muzyczną - na pamięci cmos i wbudowanym zegarze czasu rzeczywistego (RTC) skończywszy.

Porty są adresowane 16-bitowo, tzn. każdy port ma swój unikalny dwubajtowy adres w systemie - np. com1 - 03F8H, lpt1 - 0378H, klawiatura - 0060H do 0064H ITD.

 

 

"Rejestry i flagi"


Rejestry są kilkubajtową pamięcią wbudowaną do procesora, do której procesor ma najszybszy dostęp.
Większość rejestrów może być dowolnie modyfikowana przez programistę tak, że mogą one pełnić rolę podobną do zmiennych w Pascalu.

 

Rejestry to:

  1. AX - accumulator register - rejestr akumulatora
  2. BX - base register - rejestr bazowy
  3. CX - counter register - rejestr licznika
  4. DX - data register - rejestr danych

 

Wszystkie powyższe rejestry są 16-bitowe - czyli dwubajtowe. Do każdego z nich jest dostęp przez podrejestry - np. dla AX istnieją rejestry AH i AL (AH=higher - wyższy bajt ax, AL=lower - niższy bajt AX).
Analogicznie - bx posiada BH i BL, CX - CH i CL, DX - DH i DL.
Oczywiście rejestry *H i *L są ośmiobitowe - jednobajtowe.

Procesor zawiera także rejestry segmentów.

Rejestry segmentów to:

  1. CS - Code Segment - segment kodu - określa segment pamięci, w którym znajduje się wykonywany program.
  2. DS - data segment - segment danych - określa segment pamięci, w którym znajdują się dane aktualnie wykonywanego programu.
  3. SS -stack segment - segment stosu - segment, w którym znajduje się stos.
  4. ES - extra segment - używany do operacji na łańcuchach pamięci.

W przypadku tych rejestrów nie istnieją już podrejestry z ich starszym i młodszym bajtem. Jakkolwiek wszystkie te segmenty mogą być modyfikowane, to jednak modyfikacji wartości CS, DS i SS dokonuje się w zaawansowanym programowaniu - dla niedoświadczonych programistów takie modyfikacje mogą oznaczać tylko kłopoty.Trzecią grupę rejestrów stanowią pointers - wskaźniki.

 

 

Wykaz wskaźników:

  1. IP - instruction pointer - wskaźnik wykonywanej instrukcji a ściślej - jej przesunięcia w segmencie CS
  2. SP - stack pointer - wskaźnik dna stosu - ściślej - jego przesunięcia w segmencie SS.
  3. BP - base pointer - wskaźnik bazowy.

Podobnie jak w przypadku segmentów, wartości rejestrów *P - za wyjątkiem BP - są modyfikowane wyłącznie przez procesor, lub przez program na wysokim szczeblu zaawansowania.

Przedostatnią grupę stanowią indeksy, do których należą:

1.      SI - source index - index źródłowy

  1. DI - destination index - index docelowy

Indeksy używane są do transferu bloków pamięci, ale mogą być bez konsekwencji używane również do innych celów.

Ostatnim rejestrem jest rejestr flag procesora. Jest to rejestr 16 bitowy a każda flaga zajmuje 1 bit - może więc być aktywna, lub nieaktywna. Flagi to:

AF, CF, DF, IF, OF, PF, SF, TF, ZF.

Najogólniej mówiąc flagi określają aktualny stan procesora, mają też jednak istotne znaczenie przy instrukcjach skoku warunkowego, którymi zajmiemy się później.

"Stos"

Stos jest obszarem pamięci używanym do "tymczasowego" przechowywania danych istotnych dla programu.
Jak powiedzieliśmy w sekcji "Rejestry i flagi" rejestry są obszarem kilku(nasto)bajtowym i nie nadają się raczej do przechowywania danych - raczej do ich obróbki.
Funkcję magazynu pełni właśnie stos. 

Jak działa stos?

Wyobraź sobie kilka kartek - umownie ponumerowanych od 1 do 10 - symbolizujących dane. Jeśli na stole położysz kartkę o numerze 1, to masz już jedną kartkę "na stosie". Jeśli na kartce 1 położysz kartkę 2, to wówczas masz na stosie już dwie kartki. Dołóż jeszcze kartki 3 i 4 - 4 kartki na stosie. Zauważ jednak, że w danym momencie masz dostęp tylko do kartki 4 - tylko ją możesz w danym momencie zdjąć ze stosu. Kartki 1 2 i 3 są dla ciebie aktualnie niedostępne - aby móc zobaczyć co jest na nich napisane musisz zdjąć kartkę 4, 3 i 2. Wniosek 1: ze stosu dane mogą być pobierane w odwrotnej kolejności, niż zostały na nim umieszczone. Wniosek 2: W danym momencie masz dostęp do ostatnio umieszczonego na stosie elementu - bez możliwości natychmiastowego dostępu do elementów poprzednich.

Asembler - do obsługi stosu - posiada dwie analogiczne instrukcję - na_stos (push) i ze_stosu (pop), ale o tym powiemy w dalszej części tego kursu. 

"Pamięci"

Pamięć jest chyba drugą co do ważności składową systemu komputera.
To właśnie tutaj znajduje się stos, wykonywany program i jego dane, system operacyjny ze sterownikami - słowem wszystko, co aktualnie jest potrzebne do pracy komputera.

Istnieją 3 rodzaje pamięci: RAM, ROM i pamięć zewnętrzna.

·         Pamięć zewnętrzna- nie należy do pamięci właściwej: jest to nośnik, na którym można dokonać zapisu np. dyskietka, dysk twardy...

·         Pamięć ROM (read only memory - tylko do odczytu) do układ elektroniczny zawierający wszystkie procedury podstawowe komputera - np. biosu. Pamięć ta - jak wskazuje nazwa - nie może być modyfikowana przez użytkownika.

·         Pamięć RAM (random access memory - pamięć o dostępie swobodnym) jest pamięcią, która zawiera wszystkie składowe, o których pisałem na początku tj. program z danymi, system operacyjny i wszystkie sterowniki. Pamięć ta może być dowolnie modyfikowana przez użytkownika, ale jej wadą jest, że wszystkie zapisane informacje są z niej bezpowrotnie tracone w momencie odcięcia dopływu prądu.

Aby jednoznacznie określić (zaadresować) jakieś miejsce w pamięci, używa się dwóch dwubajtowych wartości - segment i przesunięcie (offset). Przesunięcie jest liczbą określającą odległość miejsca w pamięci od początku segmentu - np. czwarty bajt pamięci znajduje się w segmencie 0 na przesunięciu 4.

Jak już pisałem - wartości segment i offset są dwubajtowe, czyli maksymalna długość każdego segmentu wynosi 65536 bajtów (64KB).

Ważna uwaga

Wbrew temu, co mogłoby się wydawać, nie prawdą jest, że w pamięci można zaadresować 65536 segmentów *65536 bajtów czyli w sumie ponad 4 miliardy bajtów. Okazuje się bowiem, że początki segmentów w pamięci są przesunięte następny względem obecnego o 16 bajtów - tzn. zerowy bajt segmentu x jest jednocześnie szesnastym bajtem segmentu x-1 i trzydziestym drugim segmentu x-2...

Dzięki takiej organizacji minimalizowane są dziury między programami alokowanymi w pamięci, ale sprawia to jednocześnie, że długość adresowalnego w ten sposób obszaru wynosi 16*65536 czyli 1MB.

" Przerwania procesora "

Przerwanie (ang. interrupt) - jest sygnałem dla procesora mówiącym mu, że ma czasowo przerwać aktualnie wykonywany proces i zająć się czymś innym. Bez przerwań procesor musiałby stale kontrolować wszystkie urządzenia zewnętrzne typu klawiatura, podczas gdy z przerwaniami procesor może wykonywać jakiś program i nadal ma możliwość reagować na wydarzenie zewnętrzne (np. naciśnięcie klawisza) od razu po jego wystąpieniu.

Procesor ma też instrukcję, która umożliwia wyłączenie przerwań - jeśli aktualnie wykonywany proces nie może być zakłócany. Istnieją jednak pewne specyficzne sytuacje, podczas których konieczna jest reakcja procesora - np. awaria sprzętu, pamięci czy odcięcie dopływu prądu - nie ważne jak istotny jest wykonywany aktualnie proces.
Do poinformowania procesora o tych ekstremalnych wypadkach służą tzw. non maskable interrupts (NMI - przerwania niemaskowalne).

Procesor posiada 256 różnych przerwań - można je podzielić na dwie grupy:

  1. Hardware interrupts - przerwania sprzętowe - omówione powyżej przerwania wywoływane przez inne części sprzętu jak np. klawiatura, zegar etc.
  2. Software interrupts - przerwania programowe - przerwania, które są wywoływane przez program - np. przerwanie 21h - przerwanie dos'a - zajmiemy się tym później. Również w przypadku przerwań programowych, po napotkaniu odwołania do przerwania (instrukcja INT) - procesor przerywa wykonywanie aktualnego programu i "przeskakuje" do procedury wywołanego przerwania.

No właśnie: Każde z przerwań - bez względu na to, czy jest to przerwanie programowe czy sprzętowe - posiada procedurę obsługi - procedurę, która jest wykonywana w momencie wystąpienia danego przerwania - w przypadku przerwania klawiatury będzie to odczytanie znaku i jego zapis do bufora klawiatury.
Procedury obsługi danego przerwania można oczywiście zmienić tak, żeby procesor po wystąpieniu przerwania wykonywał zamiast standardowej obsługi - np. część naszego programu. Na tym właśnie polega pisanie programów rezydentnych tzw. tsr'ów.

Po wykonaniu procedury obsługi danego przerwania procesor powraca do programu, który wykonywał przed wystąpieniem sygnału przerwania i wykonuje ten program.

Ważniejsze przerwania to:

1. Sprzętowe:

08H - zegar - przerwanie wykonywane 18.2 razy na sekundę;
09H - klawiatura - przerwanie wykonywane, gdy został naciśnięty lub puszczony jakikolwiek klawisz.
70H - RTC - zegar czasu rzeczywistego.
(Są to oczywiście tylko te przerwania, które nas - początkujących assemblerowców - będą interesować jako te, które się przydają:)).

2. Przerwania programowe:

05H - bios - print screen - przerwanie uaktywniane, gdy naciśnięty zostaje klawisz
10H - bios obsługa karty graficznej
16H - bios - obsługa klawiatury
20H - dos - zakończenie programu
21H - dos - główne przerwanie funkcji/procedur

 

Już na wstępie radziłbym zaopatrzyć się w mniej lub bardziej dokładny spis przerwań z opisem ich funkcji - jest to niezbędne, gdyż nie sposób zapamiętać kilkuset różnych użytecznych funkcji.

W tym kursie można znaleźć kilka tabel przerwań, które znajdują się w dziale "Tabele, wykazy...", w rozdziale "Tabele przerwań".

Kwestię przerwań a dokładniej - ich praktycznego użycia - zgłębimy nieco później - podczas omawiania instrukcji int.

"Struktura programu"

O.K. temat ten jest rzeczą, którą najlepiej szybko przeczytać i mieć ją już z głowy:) - nigdy nie wnikałem w tajniki dyrektyw, których trzeba użyć - ten akurat punkt programu wykonuję automatycznie i bez zastanowienia.
No to tak: To, że program .asm można pisać w każdym edytorze tekstowym pod warunkiem, że produkuje on plik w czystym ascii - to już zapewne wiecie.

Następną rzeczą są właśnie te dyrektywy - mój standardowy program wygląda zawsze tak:

.model tiny
.code
org 100h
start:
...
end start

Uwierzcie mi na słowo, że dotychczas takie podejście świetnie zdawało egzamin - i tak najważniejsza jest częśc między "start" i "end start" czyli to, co powyżej jest puste - to właśnie tam utkwi główny program.

Jeśli jednak chcecie mieć ogólne pojęcie o sprawie to już po krótce wyjaśniam:

.model tiny -

oznacza model pamięci, w którym zarówno kod (cs) jak i dane (ds) programu muszą się zmieścić w 64k - to jest jedna z cech programów typu .com.

.code -

oznacza code segment - segment kodu programu - właśnie tu się zaczyna.

org 100h -

program będzie się zaczynać od CS:0100H - to jest kolejna cecha programów typu .com.

Start: -

etykieta "początek" - możnaby ją z grubsza porównać do pascalowskiego begin...

 

Innym "szablonem", który również zda egzamin, jest szablon następujący: CSEG SEGMENT ;Ustalenie symbolu ASSUME CS:CSEG, DS:CSEG, ES:CSEG;przyporządkowanie wartości CS, DS i ES do symbolu CSEG ORG 100H Start: ... CSEG ENDS END START Ten sposób polecałbym może nawet bardziej niż poprzedni (polecam go od niedawna), gdyż umożliwi on kompilację programu bez błędów zarówno pod TASM, ARROWSOFT i prawdopodobnie pod MASM a poza tym jest chyba nawet prostszy niż schemat poprzedni.

"Mov"

Mov jest jedną z częściej używanych a może nawet najczęściej używaną instrukcją, toteż poznamy ją już na samym początku.

Nazwa instrukcji (mov - move - przesuń) jest dość myląca, gdyż mov służy do kopiowania wartości między pamięcią a rejestrami, lub między rejestrami wyłącznie.
W praktyce oznacza to często, że mov używa się do nadawania rejestrom określonych wartości np. ustawienie rejestru AX na wartość 09H (AX=09H).

Składnia:
MOV cel, źródło

Zarówno źródło jak i cel mogą być rejestrem, lub komórką pamięci, ale równocześnie tylko jeden z operatorów może być pamięcią tzn. nie możemy przesyłać danych bezpośrednio na linii pamięć-pamięć, trzeba to zrobić za pomocą dwóch transferów - pamięć-rejestr i rejestr-pamięć.
Bez trudu natomiast można kopiować wartość jednego rejestru do innego np.:

Mov AX,BX

Mam nadzieję, że jest to zrozumiałe, zanim więc zakończę - dwie anomalie mov, które trzeba sobie dobrze zapamiętać:

  1. Mov kopiuje a nie przenosi wartości, czyli po wykonaniu polecenia wartość "źródło" nie zostaje zmieniona ani wyzerowana.
  2. W składni mov najpierw podajemy cel, później źródło - nie jest to pomyłka, aczkolwiek do pomyłek niekiedy prowadzi, gdyż jest niezgodne z ludzkim tokiem rozumowania:).

Jeszcze przykładowy program do ściągnięcia i przemyślenia - zawiera przykłady wszystkich trzech omówionych powyżej transferów.

"Stos - instrukcje push, pop, pusha, popa, pushf i popf"

Stos omówiliśmy już wcześniej - już wówczas sygnalizowałem istnienie dwóch instrukcji, które służą do obsługi stosu - PUSH i POP. Push - powoduje wrzucenie wartości lub zawartości rejestru na stos a POP - pobranie wartości ze stosu i wrzucenie jej do rejestru.

Składnia:
PUSH REJ16
PUSH WAR16
POP REJ16

"REJ16" i "WAR16" - oznaczają odpowiednio rejestr i wartość 16-bitową - tzn. nie można wrzucać ani pobierać ze stosu wartości 8-bitowych np. AL, AH...; inaczej: instrukcje PUSH i POP operują jedynie na słowach czyli danych 2-bajtowych.

Instrukcja PUSHA i POPA powodują wrzucenie na stos wszystkich rejestrów - nie trzeba wywoływać kilkakrotnie instrukcji PUSH i POP - wpływa to więc na skrócenie programu.
Niestety - instrukcje te nie są dostępne we wszystkich wersjach procesora, czyli ich używanie w programie wpływa na kompatybilność....

Zgłoś jeśli naruszono regulamin