Łączna liczba wyświetleń

wtorek, 23 lutego 2016

1. Kurs Asembler x86: reprezentacja liczb, liczby binarne i szesnastkowe, kodowanie, wyrażenia logiczne.

Autor Nauka Programowania  |  w x86  01:23


Programowanie w języku Asembler x86

Podstawowe informacje na temat języka assembly


W tym rozdziale przedstawię podstawowe informacje dotyczące języka assembly, systemów liczbowych, konwersji między nimi i podstawowych operacji logicznych.

Język assembly jest niskopoziomowym językiem programowania komputerów i mikroprocesorów wykazującym duże powiązanie między samym językiem a kodem maszynowym danej architektury. Każdy język assembly jest ściśle związany z daną architekturą, dla porównania większość języków wysokiego poziomu działa na wielu architekturach, lecz wymagają wcześniej kompilacji lub są interpretowane.

Jednym z najczęściej używanych języków assembly jest assembly x86. 

Język assembly używa mnemoników do odwzorowania każdej instrukcji maszynowej. Każda instrukcja składa się, z co najmniej jednego operandu. Przykładami operandów są etykiety, symbole i wyrażenia.

Program, który zamienia kod zapisany w języku assembly na kod maszynowy to assembler. Oblicza on również wyrażenia stałe zapisane w kodzie programu i zamienia wszystkie elementy, będące symbolem np. nazwa funkcji na jednoznaczny adres w pamięci danego elementu. Jest to jedna z kluczowych funkcji assemblera, ponieważ w ten sposób oszczędza programiście żmudnej pracy ręcznego obliczania adresów. Teoretycznie kod będący wynikiem działania assemblera powinien być identyczny, co do kodu źródłowego zapisanego w języku assembly, lecz zapisany w formie maszynowej, natomiast niektóre assemblery wprowadzają pewne optymalizacje, przez co mogą wystąpić drobne różnice.

Programy komputerowe zazwyczaj składają się z kilku części lub modułów, programem, który łączy pliki obiektowe (zawierające kod maszynowy) w plik wykonywalny jest linker.

Reprezentacja liczb naturalnych


Komputer do reprezentacji danych używa bitów, czyli jednostek danych mogących przyjmować wartość 0 lub 1. Najmniejszą dostępną jednostką danych, na której można operować jest bajt, czyli zbiór 8 bitów. Bity w bajcie numerujemy od prawej do lewej strony. Bit po lewej stronie ma największą wartość i nazywamy go (MSB) most significant bit (najbardziej znaczący bit), natomiast po prawej ma najmniejszą wartość i nazywamy go least significant bit (LSB, najmniej znaczący bit).

MSB






LSB

1
1
1
0
0
1
1
0
Wartość
7
6
5
4
3
2
1
0
Numer bitu


W powyższym zapisie wykorzystujemy jedynie dwie wartości, 1 i 0, dlatego taki system zapisu nazywa się system dwójkowy. Podstawa każdego systemu wynika z ilości wartości, którą można przy pomocy jego zapisać, więc dla systemu dwójkowego podstawa wynosi 2. W informatyce popularne są również systemy ósemkowe i szesnastkowe, natomiast ten wykorzystywany przez nas, na co dzień jest system dziesiętnym.

Poniższa tabela pokazuje wartości używane w wyżej opisanych systemach.

System
Podstawa
Możliwe wartości
Dwójkowy (binarny)
2
0, 1
Ósemkowy (oktalny)
8
0, 1, 2, 3, 4, 5, 6, 7
Dziesiątkowy (decymalny)
10
0, 1, 2, 3, 4, 5, 6, 7, 8, 9
Szesnastkowy (heksadecymalny)
16
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F


W przypadku systemu szesnastkowego, nie ma możliwości zapisu wyłączenie w cyfrach, z racji tego, że cyfr mamy 10, a ów system potrzebuje 16 symboli do zapisu, dlatego wykorzystuje się 10 cyfr oraz 6 pierwszych liter alfabetu łacińskiego.

Zamiana z systemu dziesiątkowego na system dwójkowy jest prosta, wykorzystuje się do tego dzielenie przez dwa z resztą.

Dzielenie
Wynik
Reszta
74 : 2
37
0
37 : 2
18
1
18
9
0
9
4
1
4
2
0
2
1
0
1
0
1



W celu odczytania wartości binarnej, należy odczytać kolejne wartości reszty zacząwszy od dołu. W tym przypadku będzie to 1001010. Szybkie sprawdzenie w kalkulatorze systemowym, czy nasze obliczenia są poprawne.

Jak widać wynik jest poprawny.

A jak wygląda zamiana w odwrotną stronę, czyli z systemu dwójkowego na system dziesiętny. Każda pozycja jedynki lub zera odpowieda kolejnej potędze liczby dwa. Przedstawia to poniższa tabela.

2n
Wartość
20
1
21
2
22
4
23
8
24
16
25
32
26
64
27
128



W celu zamienia wcześniej obliczonej liczby binarnej należy pomnożyć poszczególne zera i jedynki przed odpowiadające im potęgi liczby dwa i zapisać jako sume składników.

1001010(2) = 1 * 26 + 0 *25 + 0 * 24  + 1 * 23 + 0 *22 + 1 + 21 * 0 + 20 = 1 * 64 + 0 * 32 + 0 * 16 + 1 * 8 + 0 * 4 + 1 * 2 + 0 * 1 = 64 + 8 + 2 = 74(10)

Warto zwrócić uwagę, że najmniej znaczący bit (LSB – ang. Least significant bit) w prosty sposób informuje o parzystości liczby. Gdy wynosi 1 liczba jest nieparzysta, a gdy 0 liczba jest parzysta.

Dodawanie i odejmowanie liczb binarnych


Dodawanie binarne przeprowadza się w takim sam sposób jak dodawanie pisemne na liczbach dziesiętnych.

Należy zapamiętać następujące własności:

0 + 0 = 0
0 + 1 = 1
1 + 0 = 1
1 + 1 = 10



Tak wygląda dodanie 1 bajtowych (zapisane są jako 8 bitów) liczb 32 i 50.

0
0
1
0
0
0
0
0
0
0
1
1
0
0
1
0
0
1
0
1
0
0
1
0

1010010(2) = 82(10)

Odejmowanie binarne przeprowadza się w analogiczny sposób do dodawania.

Należy zapamiętać następujące własności:

0 – 0 = 0
0 – 1 = 1
1 – 0 = 1
1 – 1 = 0
                                                                                                                                                           

Tak wygląda odjęcie liczb 50 i 32.

0
0
1
1
0
0
1
0
0
0
1
0
0
0
0
0
0
0
0
1
0
0
1
0

10010(2)  = 18(10)

Zamiana liczb dziesiętnych na liczby szesnastkowe i na odwrót


W celu zamiany liczby dziesiętnej na liczbę szesnastkowę dzielimy tę liczbę przez 16 z resztą.

Dzielenie
Wynik
Reszta
3485 / 16
217
13
217 / 16
13
9
13 / 16
0
13
0





Należy teraz odczytać od resztę z dzielenia. Liczby większe i równe 10 zamieniamy na odpowiadające im litery. W tym przypadku wynik to D9D.

W celu zamiany liczby szesnastkowej na liczbę dziesiętną należy postąpić tak samo jak w przypadku zamiany liczby binarnej na liczbę dziesiątną, tylko że kolejne miejsca cyfr odpowiadają kolejnym potęgom liczby 16.

D9D(16) = 13 * 162 + 9 * 161 + 13 * 160 = 3328 + 144 + 13 = 3485(10)

Zamiana liczb binarnych na liczby szesnastkowe i na odwrót


Można zauważyć, że podstawie liczby szesnastkowej, czyli liczbie 16 odpowiada liczba 24 co oznacza, że na 4 bitach można zapisać 16 kombinacji – cyfr. Pozwala to na szybką zamiane liczby binarnej na liczbę szesnastkową. Wystarczy taką liczbę zapisać jako grupy czterech bitów i każdą z tych grup zamienić na jedną z szesnastu cyfr szesnastkowych.

Poniżej prezentuję zamianę liczby 532421 zapisaną w systemie dziesiętnym na system szesnastkowy.

Grupa 4 bitowa
1000
0001
1111
1100
0101
Wartość dziesiętna
8
1
15
12
5
Wartość szesnastkowa
8
1
F
C
5


W celu zamiany liczby szesnastkowej na binarną należy postąpić w odwrotny sposób, każdą cyfrę szesnastkową zamienić na grupę czterech bitów.

Reprezentacja liczb całkowitych


Liczby całkowite to liczby naturalne, liczby przeciwne do liczb naturalnych oraz zero. Ten rodzaj liczb wymusił na pionierach informatyki wymyślenie metody zapisu liczb ujemnych. W tej części kursu przedstawię kody, których używa się do zapisu takich liczb.

Kod znak-moduł


W tym kodzie najstarszy bit zarezerwowany jest do określenia znaku liczby. Jeżeli jest to jedynka, liczba jest niedodatnia, jeżeli zero liczba jest nieujemna.

Przedstawia to poniższa tabela

Bit znaku
Bity modułu
Liczba
0
000
0
0
001
1
0
010
2
0
011
3
0
100
4
0
101
5
0
110
6
0
111
7
1
000
0
1
001
-1
1
010
-2
1
011
-3
1
100
-4
1
101
-5
1
110
-6
1
111
-7


Kod uzupełnień do jedności


W kodzie uzupełnień do jedności liczby dodatnie zapisywane są tak jak liczby w naturalnym kodzie dwójkowym z dodatkowym bitem znaku, który wynosi 0, natomiast liczby ujemne są negacją tych liczb.

Tabela kodu

U1
Liczba
0000
0
0001
1
0010
2
0011
3
0100
4
0101
5
0110
6
0111
7
1111
0
1110
-1
1101
-2
1100
-3
1011
-4
1010
-5
1001
-6
1000
-7

Kod uzupełnień do dwóch


Jest to najpopularniejszy kod reprezentacji liczb całkowitych w systemach cyfrowych. Operacje dodawania i odejmowania są wykowane tak samo jak dla liczb binarnych bez znaku co daje mu znaczącą przewagę nad innymi kodami, gdyż nie trzeba implementować oddzielnych jednostek procesora odpowiedzialnych za operacje na tych liczbach.

Wartość wag pozycji w zapisie U2

Waga
-2n-1
2n-2
2n-3
22
21
20
Cyfra
Bn-1
Bn-2
Bn-3
B2
B1
B0


Tabela kodu

U2
Obliczenia
Liczba
0000
0
0
0001
20
1
0010
21
2
0011
21 + 20
3
0100
22
4
0101
22 + 20
5
0110
22 + 21
6
0111
22 + 21 + 20
7
1000
-(23)
-8
1001
-(23) + 20
-7
1010
-(23) + 21
-6
1011
-(23) + 21 + 20
-5
1100
-(23) + 22
-4
1101
-(23) + 22 + 20
-3
1110
-(23) + 22 + 21
-2
1111
-(23) + 22 + 21 + 20
-1


W celu szybkiej zamiany liczby na przeciwną należy zanegować ją i dodać do niej jeden.

3 na -3

3
0011
Zapisujemy liczbę
Negacja
1100
Negujemy liczbę
-3
1101
Do zanegowanej liczby dodajemy 1


-7 na 7

-7
1001
Zapisujemy liczbę
Negacja
0110
Negujemy liczbę
7
0111
Do zanegowanej liczby dodajemy 1

Rozmiar liczb całkowitych


W systemach architektury x86 podstawową jednostką informacji jest jeden bajt, wielkość składająca się z 8 bitów. Z tej podstawowej jednostki tworzy się większe bedące jej wielokrotnościami

Byte
8
















Word
16















Doubleword
32




Quadword
64

Double quadword
128


Reprezentacja znaków


Komputery przechowują dane binarne zakodowane w odpowiedni sposób, odpowiednia interpretacja tych danych i wykorzystanych kodów pozwala na reprezentacje znaków (liczb, liter itp).

ASCII


Jednym z pierwszych kodów powstałych w tym celu jest kod ASCII (ang. American Standard for Information Interchange). Jest to siedmiobitowy kod przyporządkowujący liczby z zakresu 0-127 literom alfabetu angielskiego, znakom przestankowym, sybmol oraz poleceniom sterującym.

Kod ASCII dzieli się na 95 znaków drukowalnych oraz 33 znaki sterujące.


Kodowanie znaków ANSI


Amerykańska instytucja ANSI ustalające normy techniczne obowiązująca w USA stworzyła kod wykorzystujący 8 bitów do kodowania zbioru znaków. Standard ten znany jest również jako ANSEL i obejmuje 128 znaków kodu ASCII oraz 63 znaki dodatkowe. 14 lutego 2013 roku został administracyjne wycofany przez ANSI.

Standard Unicode


Potrzeba reprezentacji dużej liczby znaków znajdujących się w wielu alfabetach spowodowała stworzenie standardu Unicode.

Standard Unicode obejmuje przydział przestrzeni numeracyjnej poszczególnym grupom znaków oraz sposoby bajtowego kodowania znaków. Jest kilka metod kodowania, oznaczanych skrótowcami UCS (Universal Character Set) i UTF (Unicode Transformation Format). Do najważniejszych należą:
  • UTF-32/UCS-4
  • UTF-16
  • UTF-8

Ciąg znaków ASCII


Sekwencja jednego lub większej ilości znaków nazywana jest ciągiem znaków. Ciąg ASCII reprezentowany jest w pamięci jako ciąg bajtów zawierających kody ASCII. Ciągowi „ABC123” odpowiadają następujące po sobie wartości zapisane w pamięci komputera

A
B
C
1
2
3
41h
42h
43h
31h
32h
33h


Języki programowania C i C++ korzystają z ciągów znaków zakończonych bitem zerowym (ang. Null-terminated string).

Wyrażenia logiczne


George Bool w XIX w. opracował algebraiczne ujęcie logiki matematycznej, które w informatyce przekładają się na operacje na wartościach 1 i 0 (prawda i fałsz). W tym kursie nie będę zagłębiał się rozlegle w ten dział matematyki. W dzisiejszych czasach jego dogłębna znajomość potrzebna jest przy projektowaniu urządzeń cyfrowych. Podczas programowania wystarczy znać tylko kilka podstawowych operacji.

Poniższa tabela przedstawia wszystkie podstawowe wyrażenia logiczne, ich nazwę oraz operatory.

Nazwa
Operator
Działanie
Negacja (NOT)
¬, ~, ‘
¬P
Koniunkcja (Iloczyn logiczny, AND)
P ∧ Q
Alternatywa (Suma logiczna, OR)
∨, +
P ∨ Q
Alternatywa wykluczająca (suma modulo 2, XOR)
⊕, 
P ⊕ Q
Implikacja (równoważnośc, XNOR)
 →
P  → q

Tablice prawdy poszczególnych funkcji


Negacja

P
¬P
1
0
0
1


Iloczyn logiczny
P
Q
P ∧ Q
0
0
0
0
1
0
1
0
0
1
1
1


Suma logiczna
P
Q
P ∨ Q
0
0
0
0
1
1
1
0
1
1
1
1


Suma modulo 2
P
Q
P Q
0
0
0
0
1
1
1
0
1
1
1
0


Równoważność
P
Q
P ↔ Q
0
0
1
0
1
0
1
0
0
1
1
1


1 komentarz:

  1. Języki programowania C i C++ korzystają z ciągów znaków zakończonych bitem zerowym (ang. Null-terminated string).

    Bajtem zerowym

    OdpowiedzUsuń

Proudly Powered by Blogger.