poniedziałek, 28 kwietnia 2014

Systemy liczbowe

W kolumnowym obrazie systemu liczbowego podstawą systemu jest mnożnik, przez który należy pomnożyć wartość znajdująca się w kolumnie z prawej strony. W naszym systemie o podstawie 10 wartość każdej kolejnej kolumny jest dziesięciokrotnie większa od wartości kolumny z prawej strony. Każdy system liczbowy ma zestaw własnych symboli cyfrowych. Liczba tych symboli równa jest wartości podstawy systemu.  W żadnym systemie cyfrowym liczba równa podstawie systemu nie może być wyrażona jedną cyfrą. Każda kolumna ma wartość określoną przez liczbę stanowiącą podstawę systemu podniesioną do potęgi równej porządkowemu numerowi kolumny minus jeden. Wartość w pierwszej kolumnie otrzymujemy podnosząc podstawę systemu do potęgi 1 - 1 (kolumna jednostkowa ponieważ każda liczba podniesiona do potęgi zerowej ma wartość 1). Druga kolumna ma wartość podstawy systemu podniesioną do potęgi 2 - 1 (kolumna ma wartość wielkości podstawy systemu) itd. Cyfra znajdująca się w każdej kolumnie wskazuje ile razy wartość danej kolumny jest zawarta w całej liczbie. Np. jeżeli, biorąc pod uwagę system ósemkowy, w kolumnie czwartej znajduje się wartość 6 to jest to informacja, że wartość kolumny jest zawarta sześciokrotnie w wartości ósemkowej całej liczby, a jej wartość do 8 do potęgi 4 - 1 czyli 512 (512 jest zawarta sześciokrotnie w całej liczbie).

Potęgi liczby 16:
  • 16^2 = 256,
  • 16^3 = 4096,
  • 16^4 = 65536,
  • 16^5 = 1048576,
  • 16^6 = 16777216.
Tabela konwersji dwójkowej na ósemkową i odwrotnie:
  • 000 dwójkowo to 0 ósemkowo,
  • 001 dwójkowo to 1 ósemkowo,
  • 010 dwójkowo to 2 ósemkowo,
  • 011 dwójkowo to 3 ósemkowo,
  • 100 dwójkowo to 4 ósemkowo,
  • 101 dwójkowo to 5 ósemkowo,
  • 110 dwójkowo to 6 ósemkowo,
  • 111 dwójkowo to 7 ósemkowo.
Tabela konwersji dwójkowej na szesnastkową:
  • 0000 dwójkowo to 0 szesnastkowo,
  • 0001 dwójkowo to 1 szesnastkowo,
  • 0010 dwójkowo to 2 szesnastkowo,
  • 0011 dwójkowo to 3 szesnastkowo,
  • 0100 dwójkowo to 4 szesnastkowo,
  • 0101 dwójkowo to 5 szesnastkowo,
  • 0110 dwójkowo to 6 szesnastkowo,
  • 0111 dwójkowo to 7 szesnastkowo,
  • 1000 dwójkowo to 8 szesnastkowo,
  • 1001 dwójkowo to 9 szesnastkowo,
  • 1010 dwójkowo to a szesnastkowo,
  • 1011 dwójkowo to b szesnastkowo,
  • 1100 dwójkowo to c szesnastkowo,
  • 1101 dwójkowo to d szesnastkowo,
  • 1110 dwójkowo to e szesnastkowo,
  • 1111 dwójkowo to f szesnastkowo.
Przykłady:
  • system dwójkowy:
    • dwójkowy na dziesiętny: 10101101 dwójkowo to 173 dziesiętnie bo (1 * 2^7) + (0 * 2^6) + (1 * 2^5) + (0 * 2^4) + (1 * 2^3) + (1 * 2^2) + (0 * 2^1) + (1 * 2^0) = 173,
    • dwójkowy na ósemkowy: liczbę dwójkową dzielimy grupy po 3 bity idąc od prawej do lewej (jednak kolejność w jakiej zapisujemy jest taka jak kolejność bitów w liczbie dwójkowej), a brakujące bity uzupełniamy zerami i odczytujemy z tabeli wartości odpowiadające, przykładowo: liczba dwójkowa 10101101 ma 3 grupy tj. 010, 101 i 101 co odpowiada liczbie ósemkowej 255, 
    • dwójkowy na szesnastkowy: liczbę dwójkową dzielimy na grupy po 4 bity idąc od prawej do lewej (jednak kolejność w jakiej zapisujemy jest taka jak kolejność bitów w liczbie dwójkowej), a brakujące bity uzupełniamy zerami i odczytujemy z tabeli wartości odpowiadające, przykładowo: 10101101 ma 2 grupy tj. 1101 i 1010 co odpowiada liczbie szesnastkowej da,
  • system ósemkowy:
    • ósemkowy na dwójkowy: każdą cyfrę z liczby ósemkowej zamieniamy według na tabeli na odpowiadającą jej wartość dwójkową i tak liczba ósemkowa 255 to 010101101 dwójkowo,
    • ósemkowy na dziesiętny: 1105 ósemkowo to 581 dziesiętnie bo (1 * 8^3) + (1 * 8^2) + (0 * 8^1) + (5 * 8^0) = 581,
    • ósemkowy na szesnastkowy: zamieniamy na dziesiętny i następnie na szesnastkowy,
  • system dziesiętny:
    • dziesiętny na dwójkowy: 173 % 2 = 1, 86 % 2 = 0, 43 % 2 = 1, 21 % 2 = 1, 10 % 2 = 0, 5 % 2 = 1, 2 % 2 = 0, 1 % 2 = 1, a więc 173 dziesiętnie to 10101101 (od tyłu),
    • dziesiętny na ósemkowy: 100 / 8 = 12 (reszta 4), 12 / 8 = 1 (reszta 4), 1 / 8 = 0 (reszta 1), a więc 100 dziesiętnie to 144 ósemkowo (odczytujemy reszty od tyłu),
    • dziesiętny na szesnastkowy: 39345 / 4096 (największa potęga liczby 16 mniejsza niż liczba, z której przeliczamy) = 9 reszta 2571, 2571 / 256 (kolejna potęga liczby 16) = 10 reszta 11, 11 / 16 = 0, 11 / 1 = 11, a więc 39345 dziesiętnie to 9a0b szesnastkowo,
  • system szesnastkowy:
    • szesnastkowy na dwójkowy: każdą cyfrę z liczby szesnastkowej zamieniamy według tabeli na odpowiadającą jej wartość dwójkową i tak liczba szesnastkowa 9a0b to 1001101000001011 dwójkowo,
    • szesnastkowy na ósemkowy: zamieniamy na dziesiętny i następnie na ósemkowy,
    • szesnastkowy na dziesiętny: 9a0b szesnastkowo to 39435 dziesiętnie bo (9 * 16^3) + (a * 16^2) + (0 * 16^1) + (b * 16^0) = (9 * 4096) + (10 * 256) + (0 * 16) + (11 * 1) = 39545.
Literatura: Duntemann J., Zrozumieć Asembler, Translator S.C., 1994

sobota, 26 kwietnia 2014

Operacje wejścia i wyjścia w C++

Wyświetlanie wartości w innym systemie liczbowym:
#include <iostream>

int main() {
    int number;

    std::cout << "Podaj liczbe calkowita: ";

    std::cin >> number;

    std::cout << "Osemkowo: " << std::oct << number << std::endl;

    std::cout << "Szesnastkowo: " << std::hex << number << std::endl;

    std::cout << "Dziesietnie: " << std::dec << number << std::endl;
}

Dostosowywanie szerokości pól:
#include <iostream>

int main() {
    std::cout.width(20); // Następny łańcuch danych wyświetlony na standardowym wyjściu zostanie umieszczony w polu o szerokości 20 znaków i dosunięty do prawej.

    std::cout << "String.";

    std::cout << "String.\n"; // Domyślnie (dosunięte do lewej krawędzi).
}

Ustawianie sposobu wyświetlania liczb:
#include <iostream>

int main() {
    float number_float = 20.1000;

    std::cout << "Domyslnie: " << number_float << std::endl; // Wyświetla "20.1".

    std::cout.precision(2);

    std::cout << "Po ustawieniu precyzji na wartosc 2 (domyslnie precyzja jest ustawiona na 6): " << number_float << std::endl; // Wyświetla "20".

    std::cout.setf(std::ios_base::showpoint);

    std::cout.precision(6);

    std::cout << "Po ustawieniu flagi \"showpoint\" i prezycji na domyslna wartosc: " << number_float << std::endl; // Wyświetla "20.1000".

    std::cout.unsetf(std::ios_base::showpoint);

    std::cout << "Po usunieciu flagi \"showpoint\" (precyzja nadal na taka wartosc jak poprzednio): " << number_float << std::endl; // Wyświetla "20.1".
}

Plik nagłówkowy iomanip:
#include <iostream>
#include <iomanip>

int main() {
    float number_float = 20.1001;

    std::cout << std::setw(15); // Umieść dane wysłane na standardowe wyjście w polu o szerokości 15 znaków i wyrównaj do prawej.

    std::cout << number_float << std::endl;

    // Aktualnie powyższe wywołanie funkcji setw już nie obowiązuje.

    std::cout << std::setprecision(6); // Precyzja na 6.

    std::cout << number_float << std::endl; // Wyświetla "20.1001".

    std::cout << std::setw(15);

    std::cout << std::setfill('.'); // Wypełnia pustą przestrzeń aktualnego pola określonymi znakami.

    std::cout << number_float << std::endl;
}

Podstawowa instrukcją służącą w C++ do wprowadzania danych jest cin, np.:
cin >> zmienna;
Tak jak C++ traktuje dane wynikowe jako strumień znakowy tak samo strumień wejściowy jest strumieniem znakowym wpływającym do programu. Obiekt cin potrafi konwertować dane wejściowe będące łańcuchem znaków z klawiatury na postać odpowiednią dla zmiennej. Nie można z klawiatury wprowadzić znaku końca łańcucha więc obiekt cin musi mieć inną metodę znajdowania jego końca. Uznaje się, że łańcuch kończy się wraz z napotkaniem białego znaku. W związku z tym cin odczytuje tylko jedno słowo. Po jego odczytaniu automatycznie dodawany jest znak końca łańcucha jeżeli wczytywanie następuje do tablicy znakowej. Obiekt cin może być również wywoływany w postaci "cin.get(zmienna_char);", która wczytuje następny znak nawet jeżeli jest to spacja i przypisuje go zmiennej.

Przykład tego jak widoczne są dane wejściowe przy zastosowaniu cin:
#include <iostream>
#include <string>

int main() {
    std::string input;
    int integer;
    char sign;

    std::cout << "Podaj dane (tablica char, int, char oddzielone spacjami): ";

    std::cin >> input >> integer >> sign;

    // Jeżeli podamy dane wejściowe tak jak nas prosi program to oczywiście jest to poprawne ale jeżeli trzecia dana będzie stringiem to zostanie przeczytany tylko jeden znak, a reszta zostanie w buforze.

    std::cin.clear(); // Ta funkcja zeruje stan strumienia.

    std::cout << "Podaj liczby: ";

    while (std::cin >> integer) {
        std::cout << "Podales: " << integer << std::endl;

        // Jeżeli jednak zamiast liczby całkowitej podamy np. "123Z" to program wczyta do zmiennej wartość 123 i zakończy działanie.
    }
}

Jednoznakowe funkcje wejścia:
#include <iostream>

int main() {
    char character;

    std::cout << "Podaj dane wejsciowe (funkcja get):\n";

    std::cin.get(character); // Ze względu na zastosowanie funkcji get program będzie wyświetlał zarówno znaki białe jak i drukowalne.

    while (character != '\n') {
        std::cout << "Podales: " << character << std::endl;

        std::cout << "Podaj dane wejsciowe (funkcja get):\n";

        std::cin.get(character);
    }

    std::cout << std::endl;

    std::cout << "Podaj dane wejsciowe (obiekt cin) i zakoncz znakiem \"q\":\n";

    std::cin >> character; // Użycie tej instrukcji spowoduje, iż program pominie białe znaki, a wykonywanie poniższej pętli nie zostanie nigdy przerwane jeżeli warunkiem jest przypisanie do zmiennej białego znaku.

    while (character != 'q') {
        std::cout << "Podales: " << character << std::endl;

        std::cout << "Podaj dane wejsciowe (obiekt cin):\n";

        std::cin >> character;
    }

    // Poniższy kod pokazuje można łączyć wywołanie funkcji get i obiektu cin:
    // char character_one, character_two, character_three;
    //
    // std::cin.get(character_one).cin.get(character_two) >> character_three;
}

Łańcuchowe funkcje wejścia:
#include <iostream>

const int limit = 256;

int main() {
    char array[limit];
    char character;

    std::cout << "Wprowadz lancuch znakow (funkcja get): ";

    std::cin.get(array, limit, '\n'); // Ta funkcja pozostawia znak separatora w kolejce wejściowej.

    std::cout << "Oto Twoje dane: " << array << std::endl;

    std::cin.get(character);

    std::cout << "Nastepny znak wejsciowy to: " << character << std::endl;

    std::cout << "Wprowadz lancuch znakow (funkcja getline): ";

    std::cin.getline(array, limit, '\n'); // Ta funkcja nie pozostawia znaku separatora w kolejce wejściowej.

    std::cout << "Oto Twoje dane: " << array << std::endl;

    std::cin.get(character);

    std::cout << "Nastepny znak wejsciowy to: " << character << std::endl;
}

Funkcja getline (klasa istream) wczytuje całe wiersze uznając za koniec danych znak nowego wiersza, a na końcu tablicy dodaje znak końca łańcucha, np.:
cin.getline(tablica, ilość_znaków);

Kolejną funkcją wczytującą całe wiersze jest get (również klasa istream), np.:
cin.get(tablica, ilość_znaków);
Różnicą w stosunku do getline jest to, iż zamiast wczytać i odrzucić znak końca wiersza, zostawia go w kolejce wejściowej. W związku z tym kolejne wywołanie w postaci "cin.get(tablica, ilość_znaków);" sprawi, że zostanie wczytany pusty łańcuch. Rozwiązaniem jest tutaj wywołanie w postaci "cin.get();" poprzedzające chęć pobrania danych z klawiatury, które "zje" znak nowego wiersza. Bezargumentowa wersja get zwraca następny znak pozyskany z wejścia programu, który można przypisać zmiennej (zwrca kod znaku, który można przypisać do typu int). Rozwiązanie problemu może mieć również postać "cin.get(tablica, ilość_znaków).get();" po czym następuje kolejne wywołanie get z argumentami.

Różnicą między getline a get jest to, że get pozwala na zachowanie większej ostrożności. Przykładowo jeżeli użyliśmy get do wczytania tablicy to skąd wiadomo, iż odczytany został cały wiersz czy też przerwano go z powodu przepełnienia tablicy. Jeżeli kolejnym znakiem wejściowym jest znak nowego wiersza to wczytano cały wiersz, a w przeciwnym wypadku nadal mamy dane do odczytania. getline jest prostsza w użyciu jednak w starszych implementacjach może w ogóle nie występować.

Jednym z problemów jakie towarzyszą wywołaniom powyższych funkcji jest to, że w funkcje getline i get pozostawiają niewczytane znaki w kolejce wejściowej jeżeli łańcuch wejściowy jest dłuższy od miejsca na niego przeznaczonego. Poza tym jeżeli get odczyta pusty wiersz to ustawia bit błędu (co nie tyczy się getline), a dalsze wczytywanie danych jest wstrzymane aż do wywołania "cin.clear();". Problem pojawia się też kiedy wczytujemy liczbę za pomocą obiektu cin, a w kolejce wejściowej pozostaje nam znak nowego wiersza. Wyjściem z sytuacji jest oczywiście puste wywołanie "cin.get()" tudzież "(cin >> zmienna).get()".

Wejście i wyjście plikowe:
#include <iostream>
#include <fstream> // Dla klas ofstream (zapis) i ifstream (odczyt).
#include <string>
#include <exception>

int main() {
    std::string filename;

    std::cout << "Podaj sciezke i nazwe nowego pliku: ";

    std::cin >> filename;

    std::ofstream out(filename.c_str(), std::ios_base::out|std::ios_base::app); // Odczyt i dołączanie na końcu pliku.

    // std::ofstream out(filename.c_str()); // W tej postaci wywołania wszystkie dane, które są w podanym pliku zostaną uprzednio wykasowane.

    // Można również powyższa instrukcję zastąpić poniższymi:
    // std::ofstream out;
    //
    // out.open(filename.c_str());

    out << "Pierwsza linijka.\n";

    out << "Druga linijka.\n";

    out.close();

    std::ifstream in(filename.c_str());

    std::cout << "Oto zawartosc pliku:\n";

    char character;

    try {
        while (in.get(character))
            std::cout << character;
    }
    catch (std::ios::failure const & exception) {
        std::cout << "Nie udalo sie przeczytac z pliku poniewaz: " << exception.what() << std::endl;
    }

    // Do sprawdzania możliwości otwarcia pliku można również użyć wywołania "in.is_open()", które zwraca false jeżeli nie udało się otworzyć do odczytu.

    in.close();
}

Stałe trybu otwarcia pliku:
  • "ios_base::in": odczyt,
  • "ios_base::out": zapis,
  • "ios_base::ate": ustaw się na końcu,
  • "ios_base::app": dołącz na koniec pliku,
  • "ios_base::trunc": usuń zawartość pliku,
  • "ios_base::binary": plik binarny.

Źródło: Prata S., Język C++. Szkoła programowania, Wydanie VI, Helion SA, 2012

piątek, 4 kwietnia 2014

Operatory rzutowania w C++

#include <iostream>

using std::cout;

using std::cin;

using std::endl;

class object_one { };

class object_two : public object_one { };

class object_three {
public:
    short variable_1;
    short variable_2;
};

int main() {
    // Operator dynamic_cast. Jego zadaniem jest umożliwienie (lub nie) rzutowania w górę wewnątrz hierarchii klas.

    object_one first_object_one;
    object_two first_object_two;
    object_one * pointer_object_one;
    object_two * pointer_object_two = & first_object_two;

    pointer_object_one = dynamic_cast<object_one *>(pointer_object_two);

    if (pointer_object_one != nullptr)
        cout << "Udalo sie rzutowac za pomoca dynamic_cast.\n";
    else
        cout << "Nie udalo sie rzutowac za pomoca dynamic_cast.\n";

    // Operator const_cast. Służy do rzutowania typu, którego jedynym celem jest zmiana etykiety wartości między "const" a "volatile". Przydomek "volatile" mowi kompilatorowi, że ma być ostrożony w kontaktach z danym obiektem. Słowo to ostrzega, że obiekt może się w jakiś niezauważalny dla kompilatora sposób zmieniać. Kompilator nie powinien sobie upraszczać sprawy tylko za każdym razem zwracać się do przydzielonych temu obiektowi komórek pamięci.

    const object_two * const_pointer_object_two = & first_object_two;

    pointer_object_two = const_cast<object_two *>(const_pointer_object_two); // Wskaźnik "pointer_object_two" może teraz zostać użyty do zmiany wartości obiektu "first_object_two" (usunięcie etykiety "const").

    // "const object_one * const_pointer_object_one = const_cast<const object_one *>(const_pointer_object_two);" - to wyrażenie jest niepoprawne gdyż próbuje zmienić z typu "const object_two *" na "const object_one *".

    // Operator static_cast.

    pointer_object_one = static_cast<object_one *>(& first_object_two); // Rzutowanie w górę.
  
    pointer_object_two = static_cast<object_two *>(& first_object_one); // Rzutowanie w dół.

    if (pointer_object_one != nullptr)
        cout << "Udalo sie rzutowac w gore za pomoca static_cast.\n";
    else
        cout << "Nie udalo sie rzutowac w gore za pomoca static_cast.\n";

    // object_three * pointer_object_three = static_cast<object_three *>(& first_object_two); // Niepoprawne gdyż klasa object_three jest poza hierarchią.

    // Operator reinterpret_cast. Służy do ryzykownych operacji jak np. rzutowanie typu wskaźnikowego na typ całkowity, który jest wystarczająco pojemny aby pomieścić reprezentację wskaźnika. Nie możemy jednak rzutować wskaźnika na mniej pojemny typ liczby całkowitej lub na typ zmiennoprzecinkowy czy też rzutować wskaźnik na funkcję na wskaźnik na dane i odwrotnie.

    long value = 0xa224b118;

    object_three * pointer_object_three = reinterpret_cast<object_three *>(&value);

    cout << "Dwa pierwsze bajty wartosci: " << std::hex << pointer_object_three->variable_1 << endl;
}

Źródło: Prata S., Język C++. Szkoła programowania. Wydanie VI, Helion SA, 2012

wtorek, 1 kwietnia 2014

Architektura procesorów rodziny IA-32 (x86-32) oraz Intel 64 (x86-32e)

Do wymienionej w temacie dużej rodziny procesorów zaliczamy wszystkie procesory rodziny Celeron, Pentium i Core. Współczesne procesory x86-32 począwszy od roku 2003 (niektóre model Pentium 4 oraz wszystkie następne takie jak Core 2 czy Core i7) posiadają dodatkowy 64-bitowy tryb pracy. Architekturę 64-bitową wprowadziła po raz pierwszy AMD pod nazwą x86-64. 64-bitowe rozszerzenie architektury procesorów Intel oznaczane jest jako x86-32e tudzież EM64T. Obecnie oficjalnie jest używana nazwa Intel 64, której nie należy mylić z IA-64, w oparciu o którą zbudowana jest zupełnie inna linia procesorów opracowanych przez firmy Hewlett-Packard oraz Intel o nazwie Itanum.

Każy kolejny procesor firmy Intel z linii x86-32 był tak projektowany aby oprogramowanie działające na starszym modelu mogło być w dalszym ciągu używane.

Jeżeli chodzi o procesory AMD to zbiór instrukcji jednostki stałoprzecinkowej oraz zmiennoprzecinkowej (procesora głównego oraz koprocesora arytmetycznego) jest zgodny z firmą Intel. Podobnie jest z instrukcjami MMX poczynając od procesora AMD K6 2. W kolejnych procesorach K6 firma wprowadziła swoje rozszerzenie technologii MMX o nazwie 3DNow! bazujące na 64-bitowych rejestrach MMX. Można powiedzieć, że była to odpowiedź na SSE Intela. Instrukcje 3DNow! pozwalają wykonywać operacje na dwóch spakowanych wartościach zmiennoprzecinkowych pojedynczej precyzji.  Kolejnym rozszerzeniem 3DNow!, czasem nazywanym 3DNow+, jest DSP. Intelowskie rozszerzenie SSE posiada AMD Athlon 4 i nowsze, a SSE2 pojawiło się w AMD Athlon 64.

Procesor 8086 Intela był pierwszym, w którym rejestry dostępne programowo są 16-bitowe. Z niewielkim uproszczeniem można przyjąć, że procesor o architekturze x86-32 widziany jest przez programistę jako szybki procesor 8086. Można stwierdzić, iż procesor pracuje wtedy trybie adresacji rzeczywistej bądź w trybie 16 bitów (chodź programowo są dostępne rejestry 32-bitowe wprowadzone w procesorze 80386). Instrukcje procesora 8086 stanowią podzbiór zbioru instrukcji np. procesora Intel Core i7. Programy mogą operować na ośmiu podstawowych rejestrach:
  • AX (16 bitów): główny rejestr jednostki stałoprzecinkowej procesora, który służy jako akumulator dla argumentów instrukcji procesora oraz zapamiętania wyników:
    • AH (8 bitów),
    • AL (8 bitów),
  • BX (16 bitów): rejestr bazowy będący wskaźnikiem do danych w pamięci (w segmencie danych):
    • BH (8 bitów),
    • BL (8 bitów),
  • CX (16 bitów): licznik w operacjach na łańcuchach oraz w pętlach programowych:
    • CH (8 bitów),
    • CL (8 bitów),
  • DX (16 bitów): rejestr danych będący rejestrem adresowym układów wejścia i wyjścia:
    • DH (8 bitów),
    • DL (8 bitów),
  • SI (16 bitów): rejestr indeksowy stanowiący wskaźnik do danych (w operacjach na łańcuchach wskaźnik dla łańcucha źródłowego),
  • DI (16 bitów): rejestr indeksowy stanowiący wskaźnik do danych (w operacjach na łańcuchach wskaźnik dla łańcucha przeznaczenia),
  • BP (16 bitów): rejestr bazowy jako wskaźnik do danych w segmencie stosu,
  • SP (16 bitów): wskaźnik szczytu stosu.

Należy wymienić jeszcze dwa inne rejestry procesora 8086:
  • IP (16 bitów): wskaźnik instrukcji wskazujący adres w segmencie kodu, z którego kolejno pobierane będą instrukcje programu do wykonania,,
  • FLAGS (16 bitów): rejestr znaczników:
    • znaczniki stanu:
      • CF (1 bit): znacznik przeniesienia (przyjmuje 1 gdy w wyniku wykonanej operacji nastąpiło przeniesienie z bitu najstarszego na zewnątrz lub też nastąpiła pożyczka z zewnątrz do bitu najstarszego,
      • PF (1 bit): znacznik parzystości (ustawiany na 1 gdy w wyniku realizowanej operacji liczba bitów o wartości 1 w młodszym bajcie wyniku jest parzysta,
      • AF (1 bit): znacznik przeniesienia pomocniczego (przyjmuje wartość 1 gdy nastąpiło przeniesienie z bitu 3 na 4 lub pożyczka z bitu 4 na 3),
      • ZF (1 bit): znacznik zera (przyjmuje wartość 1 gdy wynik operacji jest równy zeru),
      • SF: znacznik znaku (1 gdy najbardziej znaczący bit czyli bit znaku w otrzymywanym wyniku jest równy 1),
      • OF: znacznik przepełnienia (przyjmuje wartość 1 gdy przy realizowaniu określonej operacji wystąpiło przeniesienie na bit znaku lub też z tego bitu pobrana została pożyczka ale nie wystąpiło przeniesienie lub pożyczka z bitu znaku),
    • znaczniki sterujące:
      • TF: znacznik pracy krokowej (stan równy 1 powoduje wprowadzenie procesora w tryb pracy umożliwiający wygenerowanie przerwania i przejście do specjalnych procedur obsługi po każdym wykonanym rozkazie),
      • IF: znacznik zezwolenia na przerwanie (ustawiony w stan 1 powoduje odblokowanie systemu przerwań procesora),
      • DF: znacznik kierunku (jeżeli ma wartość 0 to przetwarzanie łańcuchów odbywa się z inkrementacją adresów).

Rejestry AX, BX, CX, DX, SI, DI, BP oraz SP w czasie wykonania programu mogą zawierać odpowiednio:
  • argumenty dla wykonywanych w programie operacji arytmetycznych i logicznych,
  • argumenty służące do obliczania adresu w pamięci operacyjnej,
  • wskaźniki do pamięci operacyjnej.

W procesorze 80386 w miejsce wymienionych wyżej rejestrów 16-bitowych wprowadzono rejestry 32-bitowe (i tak pozostało do procesorów o architekturze x86-32 pracujących w trybie 32-bitowym):
  • EAX (32 bity),
  • EBX (32 bity),
  • ECX (32 bity),
  • EDX(32 bity),
  • ESI (32 bity),
  • EDI (32 bity ,
  • EBP (32 bity),
  • ESP (32 bity).

Począwszy od procesora 80386 mamy też do dyspozycji 32-bitowy rejestr wskaźników rozkazów EIP, a także 32-bitowy rejestr znaczników EFLAGS. W rejestrze EFLAGS oprócz znanych z procesora 8086 znaczników stanu oraz znaczników sterujących mamy również znaczniki systemowe.

Równolegle do procesora 8086 powstał koprocesor arytmetyczny 8087, a później dla kolejnych procesorów 80286 i 80386 koprocesory 80287 i 80387. Koprocesor arytmetyczny posiada 80-bitowe rejestry oraz listę instrukcji pozwalającą na stosunkowo proste wykonywanie nawet bardzo złożonych operacji matematycznych na liczbach zmiennoprzecinkowych. Począwszy od procesora i486 (80486) koprocesor arytmetyczny włączony został do procesora głównego przy zachowaniu wszystkich swoich funkcji. W procesorach o architekturze x86-32 występuje w postaci tzw. jednostki zmiennoprzecinkowej. Liczby zmiennoprzecinkowe są tam składowane w rejestrach od R0 do R7. Programista nie ma bezpośredniego dostępu do tych rejestrów lecz może się do nich odwoływać poprzez aliasy jakimi są rejestry od ST(0) do ST(7).

Kolejne rejestry zostały udostępnione w procesorze Pentium MMX, który był przejściowym modelem i dlatego mówi się raczej, że procesorze Pentium 2 zostało wprowadzone rozszerzenie MMX. Rozszerzenie to wprowadza osiem 64-bitowych rejestrów oznaczonych od MM0 do MM7.

W procesorze Pentium 3 wprowadzono tym razem osiem rejestrów o rozmiarze 128 bitów, które stanowią podstawowe zasoby rozszerzenia procesora o architekturze x86-32 o nazwie SSE. W procesorze Pentium 4 nie przybyło już żadnych dostępnych programowo rejestrów, a została jedynie rozbudowana lista instrukcji. Rozszerzenie procesora Pentium 4 nazwano zatem SSE2. Rejestry rozszerzeń SSE i SSE2 nazywamy XMM (oznaczane od XMM0 do XMM7). Później Intel wprowadził na rynek wersję procesora Pentium 4 z jądrem o nazwie Prescott, w którym wprowadzono rozszerzenie SSE3. W następnych wersjach procesorów pojawiły się jeszcze rozszerzenia SSE3 i SSE4.

Dane przetwarzane w programie mogą znajdować się w opisanych wyżej rejestrach tudzież w pamięci operacyjnej. Pamięć operacyjna ma organizację bajtową czyli każdy bajt w pamięci ma swój własny fizyczny adres. Podzielona jest ona na części zwane segmentami. Maksymalna wielkość pamięci operacyjnej zależy od wielkości magistrali adresowej. Każdy bajt w pamięci adresować będziemy przez adres logiczny (a nie fizyczny), który składa się z dwóch części:
  • adresu początku segmentu,
  • adresu względem początku segmentu (offsetu).

Do określenia offsetu służą rejestry segmentowe. Procesor 8086 ma ich cztery, a procesor 80386 i kolejne ma ich sześć:
  • CS (16 bitów) wskazuje segment z programem,
  • DS (16 bitów) wskazuje segment z danymi,
  • SS (16 bitów) wskazuje segment stosu,
  • ES (16 bitów) wskazuje dodatkowe segmenty danych,
  • FS (16 bitów) wskazuje dodatkowe segmenty danych,
  • ES (16 bitów) wskazuje dodatkowe segmenty danych.

Jeżeli chodzi o tryb 64-bitowy procesorów to programista otrzymuje:
  • rejestry ogólnego przeznaczenia, na których mapowane są 32-bitowe rejestry ogólnego przeznaczenia:
    • RAX (64 bity),
    • RBX (64 bity),
    • RCX (64 bity),
    • RDX (64 bity),
    • RSI (64 bity),
    • RDI (64 bity),
    • RBP (64 bity),
    • RSP (64 bity),
  • nowe rejestry ogólnego przeznaczenia:
    • R8 (64 bity),
    • R9 (64 bity) ,
    • R10 (64 bity),
    • R11 (64 bity),
    • R12 (64 bity),
    • R13 (64 bity),
    • R14 (64 bity),
    • R15 (64 bity),
  • nowe rejestry ogólnego przeznaczenia mapowane na rejestrach od R8 do R15:
    • R8D (32 bity),
    • R9D (32 bity) ,
    • R10D (32 bity),
    • R11D (32 bity),
    • R12D (32 bity),
    • R13D (32 bity),
    • R14D (32 bity),
    • R15D (32 bity),
  • nowe rejestry ogólnego przeznaczenia mapowane na rejestrach od R8D do R15D:
    • R8W (16 bitów),
    • R9W (16 bitów),
    • R10W (16 bitów),
    • R11W (16 bitów),
    • R12W (16 bitów),
    • R13W (16 bitów)
    • R14W (16 bitów),
    • R15W (16 bitów),
  • nowe rejestry ogólnego przeznaczenia mapowane na rejestrach od R8W do R15W:
    • R8L (8 bitów),
    • R9L (8 bitów),
    • R10L (8 bitów),
    • R11L (8 bitów),
    • R12L (8 bitów),
    • R13L (8 bitów),
    • R14L (8 bitów),
    • R15L (8 bitów),
  • nowe rejestry XMM:
    • XMM8 (128 bitów),
    • XMM9 (128 bitów),
    • XMM10 (128 bitów),
    • XMM11 (128 bitów),
    • XMM12 (128 bitów),
    • XMM13 (128 bitów),
    • XMM14 (128 bitów),
    • XMM15 (128 bitów).

Źródło: Wróbel E., Praktyczny kurs asemblera. Wydanie II, Helion SA, 2011

Microsoft Foundation Classes

Struktura programu opartego na MFC składa się z dwóch pojęć związanych z aplikacją:
  • dokumentu czyli kolekcji danych w aplikacji, z którymi ma styczność użytkownik przy jednoczesnym wyborze tego czy użytkownik będzie korzystał Single Document Interface czy też Multiple Document Interface,
  • widoku, który określa sposób w jaki dane będą wyświetlone w oknie oraz możliwe interakcje użytkownika z nimi (własna klasa widoku jest wyprowadzana z klasy CView).
MFC zawiera mechanizm integrujący dokument z jego widokami oraz okno ramek z aktywnym widokiem, które przechowuje wskaźnik do aktywnego obiektu widoku. Natomiast koordynacją między dokumentem, widokiem i oknem ramek zajmuje się klasa zwana szablonami dokumentów. Aby zdefiniować szablon dokumentów dla aplikacji SDI należy użyć klasy CSingleDocTemplate natomiast do typu MDI służy CMultiDocTemplate.

Aplikacja MFC posiada cztery podstawowe typy ("My" to nazwa naszej aplikacji):
  • CMyApp czyli klasę aplikacji,
  • CMyWnd czyli klasę okna ramki,
  • CMyView czyli klasę widoku, która definiuje w jaki w obszarze klienta okna utworzonego przez obiekt CMyWnd będą wyświetlane dane zawarte w CMyDoc,
  • CMyDoc czyli klasę dokumentu definiującą dokument zawierający dane aplikacji.



Aby stworzyć nowy projekt MFC w środowisku Microsoft Visual Studio 2010 należy:
  • uruchomić nowy projekt,
  • jako szablon wybrać "Visual C++" i "MFC", a następnie "Aplikacja MFC",
  • wprowadzić nazwę projektu i kliknąć "OK",
  • w oknie "MFC Application Wizard" dla aplikacji SDI:
    • wchodzimy w "Application Type",
    • wciskamy pole "Single document",
    • wyłącz opcję "Use Unicode libraries" (jeżeli jest aktywna to aplikacja spodziewa się wejścia w formacie Unicode, pliki są w formacie Unicode i na skutek tego nie można ich odczytać w programach, które oczekują formatu ASCII),
    • określamy styl graficzny naszego programu,
    • określamy w jaki sposób kod bibliotek MFC jest wykorzystywany w programie (aplikacje z łączeniem statycznym są szybsze natomiast te z łączeniem dynamicznym zapewniają lepsze użycie pamięci),
    • wybieramy z menu po lewej stronie "Document Template Properties" i określamy jakie rozszerzenie ma tworzyć nasz program,
    • przechodzimy do "User Interface Features" gdzie określamy takie opcje jak np. domyślna maksymalizacja okna,
    • kolejna sekcja to "Advanced Features" i tutaj uaktywniamy opcje "Printing and print preview" (dodaje do menu "File" takie opcje jak np. "Page Setup", a kreator aplikacji dodaje kod obsługi) i "Context-sensitive Help (HTML)" (podstawowa obsługa pomocy zależnej od kontekstu),
    • klikamy na "Generated Classes" po lewej stronie gdzie możemy sprawdzić jakie klasy zostaną wygenerowane w kodzie aplikacji i tak np. klasy bazowe to:
      • CEditView: umożliwia prostę wielowierszową edycję tekstu, a także funkcje wyszukiwania, zamieniania oraz drukowania,
      • CFormView: dostarcza widok będący formularzem (oknem dialogowym, które może zawierać kontrolki do wyświetlania danych oraz wprowadzania danych przez użytkownika),
      • CHtmlEditView: rozszerza klasę CHtmlView i dodaje możliwość edytowania witryn HTML, 
      • CHtmlView: dostarcza widok, w którym można przeglądać strony WWW oraz lokalne pliki HTML,
      • CListView: umożliwia korzystanie z architektury "dokument i widok" z kontrolkami list,
      • CRichEditView: umożliwia wyświetlanie i edycję RTF,
      • CScrollView: dostarcza widok, który automatycznie dodaje paski przewijania jeżeli wyświetlane dane tego wymagają,
      • CTreeView: umożliwia korzystanie z architektury "dokument i widok" z kontrolkami drzewa,
      • CView: dostarcza podstawowych możliwości przeglądania dokumentu,
      • CChildFrame (dla MDI): dostarcza okno ramowe dla widoku dokumentu, który pojawia się wewnątrz okna aplikacji utworzone przez obiekt typu CMainFrame,
    • jako klasę bazową klasy typu CMyView (gdzie "My" to nazwa projektu) wybierz np. CEditView aby automatycznie uzyskać podstawowe możliwości edycji tekstu,
    • kliknij "Finish",
  • dla aplikacji MDI robimy zmiany tylko w dwóch miejscach:
    • w sekcji "Application Type" pozostawiamy domyślną opcję "Multiple documents",
    • w sekcji "Generated Classes" pozstawiamy klasę CView jako klasę bazową CMyView.
W oknie "Eksplorator rozwiązań" Visual Studio w pliku "ReadMe.txt" na liście rozwiązania znajduje się opis wszystkich plików wchodzących w skład projektu jak następuje:
  • "My.vcxproj": to główny plik projektu zawierający informacje o wersji Visual C++, który wygenerował plik, a także informacje o platformach, konfiguracjach i cechach projektu wybranych w kreatorze aplikacji,
  •  "My.vcxproj.filters": plik filtrów projektu zawierający informacje o związku pomiędzy plikami a filtrami,
  • "My.h": pliki nagłówkowe aplikacji (zawiera deklarację klasy CMyApp),
  • "My.cpp": główny plik źródłowy zawierający klasę CMyApp oraz definicję klasy CAboutDlg,
  • "My.rc": lista wszystkich zasobów Microsoft Windows, które używa program,
  • "My.ico": plik ikon,
  • "My.rc2": zawiera zasoby, które nie są edytowane przez Microsoft Visual C++ (wszystkie nieedytowalne przez edytor zasobów zasoby powinny zostać tutaj umieszczone),
  • "My.reg": przykładowy plik pokazują rodzaj ustawień rejestracji struktury, które zostaną skonfigurowane,
  • "MainFrm.h" i "MainFrm.cpp": zawiera klasę CMainFrame, która wywodzi się z CFrameWnd i kontroluje wszystkie właściwości ramki SDI,
  • "LeftView.h" i "LeftView.cpp": zawiera klasę CLeftView wywodzącą się z CTreeView,
  • "Toolbar.bmp": plik mapy bitowej użyty do stworzenia kafelkowych obrazów dla paska narzędzi (początkowy pasek narzędzi i pasek stanu są konstruowane w klasie CMainFrame),
  • "MyDoc.h" i "MyDoc.cpp": klasa CMyDoc,
  • "MyView.h" i "MyView.cpp": widok dokumentu i klasa CMyView,
  • "StdAfx.h" i "StdAfx.cpp": pliki używane do budowanie prekompilowanych plików nagłówkowych i prekompilowanych plików typów,
  • "Resource.h": standardowy plik zasobu, które definiuje nowe identyfikatory zasobu, 
  • "My.manifest": pliki manifestu aplikacji użyte przez Windows XP do opisu zależności aplikacji od konkretnych wersji zespołów "Side-by-Side".
Definicje klas:
  • CMyApp:
    • makrodefinicja "DECLARE_MESSAGE_APP": definiuje które funkcje klasy będą obsługiwały które komunikaty Windowsa,
  • CMainFrame: 
    • składowe chronione "m_wndStatusBar" i "m_wndToolbar": egzemplarze klas MFC (odpowiednio CStatusBar i CToolBar) tworzące i zarządzające paskiem stanu pojawiającym się na dole okna aplikacji oraz paskiem narzędzi dostarczającym przyciski umożliwiające dostęp do standardowych elementów menu,
  • CMyDoc:
    • makrodefinicja "DECLARE_DYNCREATE": umożliwia dynamiczne tworzenie obiektu klasy poprzez jego syntezę danych odczytanych z pliku,
    • makrodefinicja "DECLARE_MESSAGE_APP": obsługa komunikatów Windowsa przez jej funkcje składowe,
  • CMyView:
    • funkcja GetDocument: zwraca wskaźnik do obiektu dokumentu odpowiadającego widokowi.
Działanie programu:
  • utworzenie zmiennej "theApp" typu CMyApp będącej obiektem aplikacji,
  • wywołanie funkcji "WinMain" dostarczonej przez MFC,
  • uruchomienie za pomocą "WinMain" funkcji "InitInstance", która tworzy szablon dokumentu, główne okno ramowe, dokument oraz widok,
  • wywołanie za pomocą "WinMain" funkcji "Run", która uruchamia główną pętlę komunikatów pobierającą i przesyłającą komunikaty Windowsa.
Powiązanie między konkretnym komunikatem a obsługującą go funkcją w programie dokonywane jest przez mapę komunikatów, która musi mieć każda obsługująca komunikaty Windowsa klasa w programie. Mapa komunikatów jest tworzona automatycznie lub poprzez ClassWizard. W kodzie programu początek mapy komunikatów jest określony makrodefinicją "BEGIN_MESSAGE_MAP", a koniec "END_MESSAGE_MAP".
Kategorie komunikatów:
  • komunikat Windowsa: oprócz komunikatów "WM_COMMAND" są to standardowe komunikaty rozpoczynające się prefiksem "WM_",
  • komunikaty powiadamiające z kontrolek: komunikaty "WM_COMMAND" przesyłane z kontrolek (takich jak lista wyboru) do okna, które utworzyło kontrolkę lub z okna potomnego do macierzystego,
  • komunikaty poleceń: komunikaty "WM_COMMAND" pochodzące z elementów interfejsu użytkownika takich jak elementy menu i przyciski pasków narzędzi.
Rodzaje komunikatów, które mogą powstać dla danego identyfikatora menu:
  • "COMMAND": komunikat wysyłany gdy dany element menu został wybrany,
  • "UPDATE_COMMAND_UI": przesyłany gdy menu powinno zostać uaktualnione zależnie od jego stanu.
Argumentem przesyłanym do funkcji uaktualniającej (komunikat "UPDATE_COMMAND_UI") jest wskaźnik do obiektu klasy CCmdUI. Jest to klasa MFC używana jedynie z procedurami obsługi uaktualnień i posiadająca pięć funkcji składowych:
  • ContinuteRouting: przesyła komunikat do procedury o niższym priorytecie,
  • Enable: włącza lub wyłącza stosowny element interfejsu,
  • SetCheck: ustawia znacznik wyboru dla stosownego elementu interfejsu,
  • SetRadio: włącza lub wyłącza przycisk w grupie przycisków opcji,
  • SetText: ustawia tekst dla stosownego elementu interfejsu.
Gdy chcesz narysować coś na graficznym urządzeniu musisz wykorzystać kontekst urządzenia. Kontekst urządzenia jest strukturą danych definiowaną przez system Windows zawierającą informacje pozwalające Windowsowi tłumaczyć żądanie wyjścia na działania na wykorzystywanym fizycznie urządzeniu. Kontekst urządzenia zapewnia wybór układów współrzędnych zwanych trybami mapowania, które są automatycznie konwertowane do współrzędnych klienta:
  • "MM_TEXT": jednostką logiczną jest jeden piksel urządzenia o dodatnim x od lewej do prawej i dodatnim y od góry do dołu obszaru klienta okna,
  • "MM_LOENGLISH": jednostką logiczną jest 0,01 cala o dodatnim x od lewej do prawej i dodatnim y od góry obszaru klienta okna w górę,
  • "MM_HIENGLISH": jednostką logiczną jest 0,001 cala o kierunkach x i y jak dla "MM_LOENGLISH",
  • "MM_LOMETRIC": jednostką logiczną jest 0,1 milimetra o kierunkach x i y jak dla "MM_LOENGLISH",
  • "MM_HIMETRIC":  ednostką logiczną jest 0,01 milimetra o kierunkach x i y jak dla "MM_LOENGLISH", 
  • "MM_ISOTROPIC": jednostka logiczna jest dowolnej długości jednak takiej samej na osi x i y (kierunki x i y są takie same jakd dla "MM_LOENGLISH"),
  • "MM_ANISOTROPIC": ten tryb jest podobny do "MM_ISOTROPIC" jednak pozwala aby długość jednostki logicznej na osi x była inna niż na osi y,
  • "MM_TWIPS": jednostką logiczną jest TWIP (0,05 punktu czyli 1/72 cala) o takich samych kierunkach x i y jak dla "MM_LOENGLISH".
Aby coś narysować musimy użyć obiektu pióra, który ma typ CPen. Obiekt ten posiada funkcję składową CreatePen, która jest wywoływana z trzeba argumentami. Drugi argument definiuje grubość linii, trzeci określa kolor używany podczas rysowania natomiast ostatni to jedna z następujących stałych:
  • "PS_SOLID": rysuje ciągłą linię,
  • "PS_DASH": rysuje linię przerywaną (ten styl linii jest prawidłowy tylko wtedy gdy pióro ma szerokość 1),
  • "PS_DOT": rysuje linię złożoną z kropek (ten styl linii jest prawidłowy tylko wtedy gdy pióro ma szerokość 1),
  • "PS_DASHDOT": rysuje linię składająca się na przemian z kresek i kropek (ten styl linii jest prawidłowy tylko wtedy gdy pióro ma szerokość 1), ,
  • "PS_DASHDOTDOT": rysuje linię składająca się na przemian z kresek i dwóch kropek  (ten styl linii jest prawidłowy tylko wtedy gdy pióro ma szerokość 1),
  • "PS_NULL": pióro nic nie rysuje,
  • "PS_INSIDEFRAME": pióro rysuje ciągłą linię jednak w przeciwieństwie do "PS_SOLID" punkty określające linię leżą na krawędzi pióra, a nie w jego środku więc rysowany obiekt nigdy nie wystaje poza opisujący prostokąt.
Innym sposobem rysowania jest pędzel, w ramach którego definiujemy obiekt klasy CBrush posiadający konstruktor jednoargumentowy i dwuargumentowy. W pierwszy przypadku podajemy tylko kolor pędzla natomiast przy drugim podajemy typ kreskowania:
  • "HS_HORIZONTAL": kreskowanie poziome,
  • "HS_VERTICAL": kreskowanie pionowe,
  • "HS_BDIAGONAL": kreskowanie w dół od lewej do prawej pod kątem 45 stopni,
  • "HS_FDIAGONAL": kreskowanie w górę od lewej do prawej pod kątem 45 stopni,
  • "HS_CROSS": kreskowanie poziome i pionowe,
  • "HS_DIAGCROSS": kreskowanie pod kątek 45 stopni.
Gdy rysujemy jakiś kształt robimy to w konkretnym widoku naszego dokumentu. Sprowadza się to do tego, że klasa widoku jest oczywistym miejscem dla procedur obsługi komunikatów myszy, które najważniejsze zostały wyszczególnione poniżej:
  • "WM_LBUTTONDOWN": komunikat powstaje przy naciśnięciu lewego przycisku myszy,
  • "WM_LBUTTONUP": komunikat powstaje przy zwolnieniu lewego przycisku myszy,
  • "WM_MOUSEMOVE": komunikat powstaje przy przemieszczaniu myszy.
Do procedury obsługi komunikatu przesyłane są dwa argumenty:
  • liczba znaczników stanu wskazujących czy dane klawisze są wciśnięte:
    • "MK_CONTROL": klawisz "Ctrl",
    • "MK_LBUTTON": lewy przycisk myszy,
    • "MK_MBUTTON": środkowy przycisk myszy,
    • "MK_RBUTTON": prawy przycisk myszy,
    • "MK_SHIFT": klawisz "Shift",
  • obiekt CPoint definiujący pozycję kursora w momencie naciśnięcia lewego przycisku myszy.
Funkcja SetROP2 ustawia tryb rysowania dla wszystkich kolejnych operacji wyjścia w kontekście urządzenia powiązanych z obiektem typu CDC. Tryb rysowania określa jak połączyć kolor pióra użytego do rysowania z kolorem tła aby ustalić kolor wyświetlanego obiektu. Tryb rysowania określamy jednym argumentem funkcji o jednej z następujących wartości:
  • "R2_BLACK": wszystko jest rysowane na czarno,
  • "R2_WHITE": wszystko jest rysowane na biało,
  • "R2_NOP": operacje rysowania nie mają efektu,
  • "R2_NOT": rysowanie przebiega zawsze w kolorze przeciwnym do wyświetlanego już na ekranie co zapewnia, że wyjście będzie zawsze widoczne,
  • "R2_COPYPEN": rysowanie w kolorze pióra (domyślny),
  • "R2_NOTCOPYPEN": rysowanie w kolorze przeciwnym do koloru pióra,
  • "R2_MERGEPENNOT": rysowanie w kolorze powstałym z alternatywy bitowej koloru pióra i koloru przeciwnego do koloru tła,
  • "R2_MASKPENNOT":  rysowanie w kolorze powstałym z koniunkcji bitowej koloru pióra i koloru przeciwnego do koloru tła,
  • "R2_MERGENOTPEN": rysowanie w kolorze powstałym z alternatywy bitowej koloru tła i koloru przeciwnego do koloru pióra,
  • "R2_MASKNOTPEN": rysowanie w kolorze powstałym z koniunkcji bitowej koloru tła i koloru przeciwnego do koloru tła,
  • "R2_MERGEPEN": rysowanie w kolorze powstałym z alternatywy bitowej koloru tła i koloru pióra,
  • "R2_NOTMERGEPEN": rysuje w kolorze przeciwnym do "R2_MERGEPEN",
  • "R2_MASKPEN": rysowanie w kolorze powstałym z koniunkcji bitowej koloru tła i koloru pióra,
  • "R2_NOTMASKPEN": rysowanie w kolorze przeciwnym do koloru "R2_MASKPEN",
  • "R2_XORPEN": rysowanie w kolorze powstałym z bitowej alternatywy wykluczającej koloru pióra i koloru tła,
  • "R2_NOTXORPEN": rysowanie w kolorze przeciwnym do koloru "R2_XORPEN".
Podczas pracy często musimy obsługiwać kolekcje danych nie znając z góry liczby elementów ani nawet ich typu. MFC dostarcza grupę klas kolekcji do obsługi takiego problemu. Kolekcja jest zbiorem dowolnej liczby danych zorganizowanych w określony sposób. MFC obsługuje trzy rodzaje kolekcji różniące się sposobem organizacji danych. Sposób organizacji kolekcji nazywa się jej kształtem. Istnieją trzy typy organizacji lub kształtu:
  • tablica (klasa szablonu CArray): uporządkowany układ elementów, z którego każdy element może zostać pobrany z wykorzystaniem indeksu będącego liczbą całkowitą,
  • lista (klasa szablonu CList): kolekcja uporządkowanych danych gdzie z każdym elementem powiązane są dwa wskaźniki, które wskazują na poprzedni i następny element na liście,
  • mapa (klasa szablonu CMap): nieuporządkowana kolekcja danych gdzie każdemu elementowi przyporządkowany jest klucz używany do odzyskania elementu z mapy.
Szablony klas kolekcji wskaźników z kontrolą typów przechowują wskaźniki do obiektów, a nie obiekty jako takie. Służy do tego klasa szablonu CTypedPtrList z następującymi funkcjami składowymi:
  • GetHead: zwraca wskaźnik do początku listy,
  • GetTails: zwraca wskaźnik do stopki listy,
  • RemoveHead: usuwa pierwszy wskaźnik z listy,
  • RemoveTail: usuwa ostatni wskaźnik z listy,
  • GetNext: zwraca wskaźnik znajdujący się na pozycji określonej zmienną typu POSITION, która jest przesyłana jako referencja (wartość zmiennej jest aktualizowana aby wskazywała kolejny element listy),
  • GetPrev: zwraca wskaźnik znajdujący się na pozycji określonej zmienną typu POSITION, która jest przesyłana jako referencja (wartość zmiennej jest aktualizowana aby wskazywała poprzedni element listy),
  • GetAt: zwraca wskaźnik przechowywany na pozycji wskazywanej przez zmienną typu POSITION, która jest przesyłana jako argument,
  • AddHead: dodaje na początku listy wskaźnik przesyłany jako argument,
  • AddTail: dodaje na końcu listy wskaźnik przesyłany jako argument,
  • RemoveAll: usuwa wszystkie elementy z listy,
  • GetHeadPosition: zwraca pozycję elementu z początku listy,
  • GetTailPosition: zwraca pozycję elementu z końca listy,
  • RemoveAt: usuwa wskaźnik z pozycji listy,
  • InsertBefore: wstawia nowy wskaźnik określony przez drugi argument przed pozycją określoną przez pierwszy argument,
  • InsertAfter:  wstawia nowy wskaźnik określony przez drugi argument za pozycją określoną przez pierwszy argument, 
  • Find: wyszukuje na liście wskaźnik, który jest taki sam jak wskaźnik określony przez argument,
  • FindIndex: zwraca pozycję wskaźnika na liście określoną przesłanym jako argument indeksem będącym liczbą całkowitą,
  • GetCount: zwraca liczbę elementów na liście,
  • IsEmpty: zwraca prawdę jeżeli na liście nie ma żadnych elementów, a w przeciwnym razie fałsz.
Każdy widok dokumentu działa niezależnie od innych i nie ma między nimi komunikacji. Aby każdy widok, który doda element do dokumentu informował o tym pozostałe widoki tak aby i one mogły podjąć odpowiednie działania należy użyć funkcji UpdateAllViews z klasy dokumentu. Jej prototyp wygląda następująco: "void UpdateAllViews(CView * pSender, LPARAM lHint = OL, CObject * pHint = NULL);" gdzie:
  • "pSender" to wskaźnik do bieżącego widoku (powstrzymuje on wywołanie funkcji składowej OnUpdate w widoku),
  • "lHint" jest zmienną typu LAPARAM, który ma 32 bity w systemie Windows i może zostać użyty do przesłania informacji o obszarze, który ma zostać uaktualniony w obszarze klienta,
  • "pHint" jest wskaźnikiem do obiektu mogącego dostarczyć informacji o fragmencie obszaru, który ma zostać zaktualizowany w obszarze klienta.
Nowa klasa widoku musi coś wiedzieć o obszarze, w którym rysujemy (np. jego jego rozmiar i to jak daleko widok został przewinięty za pomocą paska przewijania). Te informacje muszą zostać dostarczone przed pierwszym narysowaniem widoku. Można wstawić odpowiedni kod do funkcji OnInitialUpdate w klasie widoku. Wymagane informacje można dostarczyć korzystając z funkcji SetScrollSizes o następującym prototypie: "void SetScrollSizes(int MapMode, SIZE Total, const SIZE & Page = sizeDefault, const SIZE & Line = sizeDefault);" gdzie:
  • "MapMode" może mieć następujące wartości:
    • "MM_TEXT",
    • "MM_LOENGLISH",
    • "LO_METRIC",
    • "MM_TWIPS",
    • "MM_HEINGLISH",
    • "MM_HIMETRIC",
  • "Total" to całkowity obszar rysowania,
  • "Page" definiuje odległość w poziomie i w pionie o jaką przewinąć stronę,
  • "Line" definiuje odległość w poziomie i w pionie o jak przewinąć linie.
Aby wyświetlić menu kontekstowe należy użyć funkcji TrackPopupMenu z klasy CMenu. Jej pierwszy argument to suma bitowa dwóch wartości. Lewa może posiadać następujące opcje:
  • "TPM_CENTERALIGN": wyśrodkowuje menu w poziomie względem współrzędnej x podanej jako drugi argument funkcji,
  • "TPM_LEFTALIGN": umieszcza menu tak, że jego lewa strona jest wyrównana względem współrzędnej z podanej jako drugi argument funkcji,
  • "TPM_RIGHALIGN": umieszcza menu tak, że jego prawa strona jest wyrównana względem współrzędnej x podanej jako drugi argument funkcji,
Prawa natomiast to wybór spośród:
  • "TPM_LEFTBUTTON": menu kontekstowe śledzi lewy przycisk myszy,
  • "TPM_RIGHTBUTTON": menu kontekstowe śledzi prawy przycisk myszy.
Większość programów dla systemu Windows używa okien dialogowych do obsługi części danych wejściowych. Wybieramy opcję z menu i pojawia nam się okno dialogowe z różnymi kontrolkami, w których podajemy informacje. Większość kontrolek należy do jednej z przedstawionych niżej kategorii:
  • kontrolki statyczne: używane są dostarczania tytułów lub informacji opisowych,
  • przyciski: dostarczają mechanizm wprowadzania danych jednym kliknięciem,
  • paski przewijania: zwykle używane do przewijania tekstu lub obrazów,
  • pola list: zawierają listę dostępnych możliwości wyboru,
  • kontrolki edycji: pozwalają wprowadzić lub edytować wyświetlany tekst,
  • listy kombinowane: przedstawiają listę dostępnych opcji, które można wybrać oraz dają możliwość samodzielnego wprowadzania tekstu.
Skalowanie w systemie Windows zwykle wiąże się z wykorzystaniem jednego ze skalowalnych trybów mapowania:
  • "MM_ISOTROPIC": w tym trybie Windows wymusza aby współczynnik skali dla osi x i y były równe, 
  • "MM_ANISOTROPIC": niezależne skalowanie osi x i y.
Sposób w jaki logiczne współrzędne są zamieniane na współrzędne urządzenia jest zależny od następujących parametrów:
  • "Window Origin": logiczne współrzędne lewego górnego rogu okna ustawiane przez wywołanie funkcji SetWindowOrg klasy CDC,
  • "Window Entent": rozmiar okna określony we współrzędnych logicznych przez funkcję SetWindowExt klasy CDC,
  • "Viewport Origin": współrzędne lewego górnego rogu okna we współrzędnych urządzenia (pikselach) ustawiane przez funkcję SetViewportOrg klasy CDC,
  • "Viewport Entent": rozmiar okna we współrzędnych urządzenia (pikselach) ustawiany przez funkcję SetViewportExt klasy CDC.
Czasem zachodzi potrzeba wyświetlania na bieżąco jakichś informacji w oknie aplikacji. Można to zrobić na pasku stanu, który domyślnie pojawia się na dole okna. Pasek ten posiada segmenty zwane polami. Do domyślnie dostarczonego przez kreatora aplikacji paska stanu można pisać uzyskując dostęp do zmiennej "m_wndStatusBar" obiektu CMainFrame dla aplikacji. Składowa "m_wndStatusBar" w CMainFrame jest egzemplarzem klasy CStatusBar. Możemy użyć tej klasy do zaimplementowania własnych pasków stanu. Definiuje ona pasek sterowania z wieloma polami, w których można wyświetlać informacje. Obiekty typu CStatusBar dostarczają ten sam zestaw funkcji co wspólne paski stanu systemu Windows poprzez funkcję składową GetStatusBarCtrl. Dla każdej ze wspólnych kontrolek systemu Windows istnieje klasa MFC, która ją kapsułkuje (ta dla wspólnej kontrolki paska stanu to CStatusBarCtrl). Aby ustawić początkowy tekst paska stanu używamy funkcji SetText należącej do klasy CStatusBarCtrl. Pierwszy jej argument to łańcuch tekstu, która ma zostać napisany, drugi to indeks części, która ma zawierać łańcuch, a trzeci może być jedną z poniższych stałych:
  • 0: tekst posiada ramkę, która jest zagłębiona w pasku zadań,
  • "SET_NOBORDERS": tekst jest pisany bez obramowania,
  • "SET_OWNERDRAW": tekst jest rysowany przez okno macierzyste,
  • "SET_POPUP": tekst ma obramowanie, które wygląda jakby wystawało z paska zadań.
Jeżeli chodzi o serializację dokumentu to kod za to odpowiedzialny został zawarty w klasie CMyDoc i obejmuje:
  • makrodefinicję "DECLARE_DYNCREATE", która umożliwia dynamiczne tworzenie obiektów klasy CMyDoc przez platformę aplikacji podczas procesu serializacji wejścia,
  • funkcję składową Serialize, która jest funkcją wirtualną i jest wywoływana w celu przeprowadzenia operacji serializacji wejścia i wyjścia na danych składowych klasy (każda serializowalna klasa musi ją zawierać),
  • domyślny konstruktor klasy wykorzystywany przez platformę do składania obiektu podczas czytania z pliku, a poskładany obiekt jest następnie wypełniany danymi z pliku w celu ustawienia wartości danych składowych obiektu.
Jeżeli chodzi o drukowanie dokumentu to ono sterowane przez bieżący widok. Oto logika tego procesu:
  • funkcja OnPreparePrinting (składowa widoku) oblicza liczbę stron i wywołuje funkcję DoPreparePrinting,
  • funkcja OnBeginPrinting (składowa widoku) alokuje zasoby Graphical Device Interface,
  • funkcja StartDoc z klasy CDC,
  • funkcja OnPrepareDC (składowa widoku) zmienia początek układu współrzędnych wziernika i ustawia atrybuty kontekstu urządzenia,
  • rozpoczęcie pętli, która wykonuje się dopóki są jeszcze jakieś strony,
  • funkcja StartPage klasy CDC,
  • funkcja OnPrint (składowa widoku) drukuje nagłówki, stopki oraz bieżącą stronę,
  • funkcja EndPage klasy CDC,
  • koniec pętli,
  • funkcja EndDoc klasy CDC,
  • funkcja OnEndPrinting (składowa widoku) zwalniająca zasoby Graphical User Interface.
Aby wypisać nazwę pliku dokument stosujemy funkcję TextOut klasy CDC. Dodatkowo możemy określić położenie tekstu względem prostokąta otaczającego za pomocą SetTextAlign i jej następujących wartości argumentów:
  • "TA_LEFT",
  • "TA_RIGHT",
  • "TA_CENTER",
  • "TA_TOP",
  • "TA_BOTTOM",
  • "TA_BASELINE": wyrównuje linię odniesienia czcionki użytej dla tekstu z punktem określającym pozycję tekstu.

Obiekt klasy CPrintInfo odgrywa podstawową rolę w procesie drukowania. Oto jej dane składowe:
  • "m_PD": wskaźnik do obiektu CPrintDialog, który wyświetla okno dialogowe drukowania,
  • "m_bDirect": jest ustawiana na "TRUE" przez platformę jeżeli operacja drukowania ma pominąć okno dialogowe drukowania (w przeciwnym razie ma wartość "FALSE"),
  • "m_bPreview": składowa typu BOOL, której wartość to "TRUE" jeżeli wybrano "Print Preview" z menu "File" (w przeciwnym razie "FALSE"),
  • "m_bContinuePrinting": składowa typu BOOL jest ustawiona na "TRUE" jeżeli platforma kontynuuje pętlę drukowania przedstawioną na schemacie, a "FALSE" jeżeli pętla jest skończona,
  • "m_CurPage": wartość typu UINT przechowująca numer bieżącej strony,
  • "m_nNumPreviewPages": wartość typu UINT określająca liczbę stron wyświetlanych w oknie podglądu wydruku,
  • "m_lpUserData": jest okna typu LPVOID i przechowuje wskaźnik do obiektu, który tworzysz,
  • SetMinPage: funkcja ustawiająca numer pierwszej strony dokumentu,
  • SetMaxPage: funkcja ustawiająca numer ostatniej strony dokumentu,
  • GetMinPage: zwraca numer pierwszej strony dokumentu,
  • GetMaxPage: zwraca numer ostatniej strony dokumentu,
  • GetFromPage: zwraca numer pierwszej strony dokumentu, który ma zostać wydrukowany,
  • GetToPage: zwraca numer ostatniej strony dokumentu, który ma zostać wydrukowany.
Aby utworzyć w Visual Studio bibliotekę rozszerzającą DLL w celu dodania zestawu klas do MFC należy:
  • otworzyć okno tworzenia nowego projektu,
  • wybrać jako szablon "MFC", a następnie "MFC DLL",
  • w polu "Name:" podać nazwę biblioteki (w naszym przypadku będzie to "ExtDLLMy"),
  • kliknąć "OK",
  • w sekcji "Application Setting" zaznaczyć "MFC extension DLL",
  • kliknąć "Finish".
Kreator wygeneruje dla nas potrzebne pliki jednak dwa z nich są kluczowe dla implementacji naszej DLL, a mianowicie:
  • "ExtDLLMy.cpp": zawiera funkcję DllMain i jest głównym plikiem źródłowym biblioteki,
  • "ExtDLLMy.def": informacje z tego pliku są używane podczas kompilacji (zawiera on nazwę DLL, a także można do niego dodać definicję tych elementów DLL, które mają być dostępne dla programów korzystających z biblioteki).
Źródło: Horton I., Visual Studio 2005. Od podstaw, Helion SA, 2007