Zadania przykładowe do ćwiczeń z „Architektury komputerów”
cz. II, maj 2010
1. Podać liczbę, która zostanie wyświetlona na ekranie w wyniku wykonania poniższego fragmentu programu. Podprogram wyswietl32 wyświetla na ekranie w postaci dziesiętnej liczbę binarną zawartą w rejestrze EAX.
qxy dw 254, 255, 256
— — — — — — — —
mov eax, dword PTR qxy + 1
call wyswietl32
2. Funkcja MessageBoxA@16(0, adr1, adr2, 0) wyświetla komunikat na ekranie: adr1 jest adresem tekstu wyświetlanego w okienku komunikatu, adr2 jest adresem tekstu wyświetlanego w tytule komunikatu. Koniec tekstu wskazuje bajt o wartości 0. Rozkaz LEA oblicza adres efektywny rozkazu.
Określić postać komunikatu po wykonaniu poniższego fragmentu programu.
napis db 'informatyka', 0, 4 dup (?)
— — — — — — — — —
mov ecx, 12
przepisz: mov al, napis[ecx-1]
mov napis[ecx+3], al
loop przepisz
push 0
push OFFSET napis
lea eax, napis[3]
push eax
push 0
call _MessageBoxA@16
3. W programach obsługi kalendarza MS Visual Studio dni świąteczne w miesiącu koduje się w postaci jedynek umieszczonych na odpowiednich bitach słowa 32-bitowego. Tablica zawierająca 12 takich elementów pozwala zakodować informacje obejmujące rok.
Napisać podprogram w asemblerze wyświetlający na ekranie daty dni świątecznych w podanym miesiącu. Parametry wywołania podprogramu znajdują się w rejestrach:
CL - numer miesiąca (1 - 12)
EBX - adres tablicy zawierającej zakodowane daty dni świątecznych w poszczególnych miesiącach.
4. W rejestrach EDX:EBX:EAX znajduje się 96-bitowy ciąg bitów. Napisać fragment programu, w którym ciąg ten zostanie przesunięty cyklicznie w lewo o 1 pozycję.
Wskazówka: wykorzystać rozkazy przesunięcia w lewo SHL (bity wychodzące z rejestru wpisywane są do CF) i RCL (zawartość CF wpisywana jest na najmłodszy bit rejestru, a bity wychodzące z rejestru wpisywane są do CF). Wykorzystać także rozkaz BT.
5. W programie asemblerowym zdefiniowano format 32-bitowych liczb mieszanych bez znaku, przyjmując, że najmniej znaczący bit ma wagę 2−7.
Napisać fragment programu w asemblerze, który wpisze 1 do znacznika CF (rozkaz STC) jeśli część całkowita liczby zawartej w rejestrze EBX jest różna od zera; w przeciwnym razie CF powinien zostać wyzerowany (rozkaz CLC).
6. Na poniższym rysunku pokazane są dwa formaty 32-bitowych liczb stałoprzecinkowych bez znaku używane w programie.
Zakładając, że w rejestrze ESI znajduje się liczba zakodowana wg pierwszego formatu (najmniej znaczący bit ma wagę 2−8), a w rejestrze EDI liczba zakodowana wg drugiego formatu (najmniej znaczący bit ma wagę 2−7), napisać fragmentu, który porówna obie liczby. Jeśli liczba w rejestrze ESI jest większa od liczby w rejestrze EDI, to do CF należy wpisać 1, w przeciwnym razie 0 (rozkazy STC/CLC).
7. Poniżej podano fragment programu w języku C.
int a, b, * wsk, wynik;
wsk = &b;
a = 21; b = 25;
wynik = roznica(&a, &wsk);
Napisać podprogram w asemblerze przystosowany do wywoływania z poziomu języka C, którego prototyp ma postać:
int roznica (int * odjemna,
int ** odjemnik);
Podprogram ten powinien obliczyć różnicę dwóch liczb całkowitych ze znakiem w kodzie U2.
8. Poniżej podano fragment programu w języku C.
int pomiary[7], * wsk;
- - - - - - - - - - - - - - -
wsk = szukaj_elem_min(pomiary, 7);
printf("\nElement minimalny = %d\n",
* wsk);
Napisać podprogram w asemblerze przystosowany do wywoływania z poziomu języka C, którego prototyp ma postać:
int * szukaj_elem_min (
int tablica[ ], int n);
Podprogram ten powinien wyznaczyć najmniejszy element tablicy i zwrócić adres (wskaźnik) tego elementu. Liczbę elementów tablicy określa drugi parametr funkcji.
9. Napisać podprogram w asemblerze przystosowany do wywoływania z poziomu języka C. Prototyp funkcji implementowanej przez ten podprogram ma postać:
void nowy_strcat (char ∗ wynik, char * zrodlo);
gdzie wynik i zrodlo są adresami (wskaźnikami) tablic zawierających ciągi znaków ASCII, przy czym koniec ciągu wskazuje bajt o wartości 0. Podprogram powinien dopisać znaki zawarte w tablicy zrodlo do znaków zawartych w tablicy wynik.
Napisać także krótki program w języku C ilustrujący sposób ilustrujący sposób wywoływania tego podprogramu. W programie tym należy wczytać z klawiatury dwa dowolne teksty (funkcja scanf), a następnie, korzystając z opracowanej funkcji, wyświetlić połączone teksty.
10. Funkcja biblioteczna języka C o prototypie
void * malloc(unsigned int k);
przydziela k-bajtowy obszar pamięci i zwraca adres przydzielonego obszaru. Jeśli wymagany obszar nie może być przydzielony, to funkcja zwraca wartość 0.
Napisać podprogram w asemblerze, przystosowany do wywoływania z poziomu języka C — prototyp tego podprogramu ma postać:
int * kopia_tablicy(int tabl[],
unsigned int n);
Podprogram kopia_tablicy tworzy nową tablicę o rozmiarach identycznych z oryginalną i zwraca adres nowej tablicy (albo 0, jeśli tablicy nie można było utworzyć).
Do nowej tablicy należy skopiować wszystkie elementy tablicy oryginalnej o wartościach będących liczbami parzystymi. Pozostałe elementy nowej tablicy wypełnić zerami.
Wskazówki:
Nową tablicę należy utworzyć poprzez przydzielenie odpowiedniego obszaru pamięci za pomocą funkcji malloc.
Sprawdzenie czy liczba jest parzysta najłatwiej wykonać poprzez odczytanie najmłodszego bitu liczby (bit nr 0).
11. Napisać podprogram w asemblerze obliczający wartość funkcji kwadrat metodą rekurencyjną korzystając z zależności:
a2 = (a - 2) 2 + 4∗a - 4 dla a > 1
a2 = 1 dla a = 1
a2 = 0 dla a = 0
przy czym argument a jest liczbą całkowitą 32-bitową zawartą w przedziale <1, 65535>.
Podprogram powinien być przystosowany do wywoływania z poziomu języka C, a jego prototyp ma postać:
unsigned int kwadrat (
unsigned int a);
W podprogramie nie można używać rozkazów mnożenia i rozkazów przesunięć.
12. Podać wartość zwracaną przez funkcję iteracja w poniższym fragmencie programu w języku C
w = iteracja(32);
Kod funkcji iteracja zapisany w asemblerze ma postać
_iteracja PROC
push ebp
mov ebp, esp
mov al, [ebp+8]
sal al, 1
; SAL wykonuje przesuniecie logiczne
; w lewo
jc zakoncz
inc al
push eax
call _iteracja
add esp, 4
pop ebp
ret
zakoncz: rcr al, 1
; rozkaz RCR wykonuje przesunięcie
; cykliczne w prawo przez CF
pop ebp
ret
_iteracja ENDP
13. W pamięci komputera, począwszy od adresu podanego w rejestrze ESI znajduje się 32-bitowa liczba zmiennoprzecinkowa w formacie float. Napisać fragment programu, który przekształci tę liczbę na 64-bitowy format double i uzyskany rezultat wpisze do pamięci począwszy od adresu podanego w rejestrze EDI. W omawianym fragmencie nie można używać rozkazów koprocesora arytmetycznego.
14. W rejestrze EAX znajduje się liczba zmiennoprzecinkowa w formacie float. Napisać fragment programu, który zwiększy tę liczbę o 2, przy czym w fragmencie nie mogą występować rozkazy koprocesora arytmetycznego. Dodatkowo zakładamy, że liczba zmiennoprzecinkowa jest dodatnia.
15. W rejestrze EAX znajduje się liczba zmiennoprzecinkowa w formacie float. Napisać fragment programu, który zwiększy tę liczbę o 0.25, przy czym w fragmencie nie mogą występować rozkazy koprocesora arytmetycznego. Dodatkowo zakładamy, że liczba zmiennoprzecinkowa jest dodatnia.
16. Poniżej podano kod (niepełny) podprogramu w asemblerze, który zwiększa o 1 liczbę zmiennoprzecinkową w formacie double. Podprogram przystosowany jest do wywoływania z poziomu języka C, a jego prototyp ma postać:
double plus_jeden (double x);
Nie używając rozkazów koprocesora, uzupełnić brakujący fragment podprogramu, przy założeniu, że liczba x jest większa od 1, a zawartość pola wykładnika (w formacie double) należy do przedziału <1023, 1075>. Uwaga: po wykonaniu dodawania przeprowadzić normalizację liczby.
_plus_jeden PROC
push ebp
mov ebp, esp
push ebx
push esi
push edi
; odczytanie liczby
; w formacie double
mov eax, [ebp+8]
mov edx, [ebp+12]
; wpisanie 1 na pozycji o wadze 2^0
; mantysy do EDI:ESI
mov esi, 0
mov edi, 00100000H
; wyodrębnienie pola
; wykładnika (11-bitowy)
; bit znaku liczby z założenia = 0
mov ebx, edx
shr ebx, 20
; obliczenie pierwotnego wykładnika
; potęgi
sub ebx, 1023
; zerowanie wykładnika i bitu znaku
and edx, 000FFFFFH
; dopisanie niejawnej jedynki
or edx, 00100000H
— — — — — — — — — — — — — — — —
— — — — — — — — — — — — — — — —
; załadowanie obliczonej wartości z
; EDX:EAX na wierzchołek stosu
; koprocesora
push edx
push eax
fld qword PTR [esp]
add esp, 8
pop edi
pop esi
pop ebx
pop ebp
ret
_plus_jeden ENDP
17. Liczbę zmiennoprzecinkową można stosunkowo łatwo wyświetlić na ekranie, jeśli uprzednio zostanie zapisana w formacie BCD. Instrukcja koprocesora FBSTP powoduje zapisanie liczby zmiennoprzecinkowej w upakowanym formacie BCD, a następnie usunięcie liczby ze stosu. Argumentem instrukcji FBSTP musi być 10-bajtowy obszar pamięci deklarowany w asemblerze za pomocą dyrektywy DT. Przed zapisaniem liczba zmiennoprzecinkowa jest zaokrąglana do liczby całkowitej. Przykładowo, liczba 1000*pi zostaje zapisana w 10-bajtach w postaci (szesnastkowo) 42 31 00 00 00 00 00 00 00 00.
Napisać podprogram w asemblerze wyświetlający na ekranie liczbę zmiennoprzecinkową znajdującą się na wierzchołku stosu koprocesora. Liczba cyfr po kropce podana jest w rejestrze CL. Przyjąć, że wyświetlana liczba jest zawsze dodatnia.
Wskazówka: przed wykonaniem rozkazu FBSTP wykonać mnożenie przez 10a, gdzie a jest zawartością rejestru CL. Mnożnik powinien być przedstawiony w postaci zwykłej liczby binarnej, a mnożenie należy wykonać za pomocą rozkazu FIMUL. W trakcie wyświetlania liczby należy dodatkowo wyświetlić kropkę przed cyframi części ułamkowej.
18. Poniższy program w języku C wczytuje promień r koła z klawiatury, oblicza pole koła i wyświetla na ekranie wynik obliczenia.
Obliczenie pola koła wykonuje podprogram zakodowany w asemblerze, przystosowany do wywoływania z poziomu języka C, którego prototyp na poziomie języka C ma postać:
void pole_kola (float * pr);
Argument pr jest adresem (wskaźnikiem) zmiennej, w której przechowywany jest promień r koła.
Napisać podprogram w asemblerze dokonujący opisanego obliczenia i uruchomić program składający się z plików źródłowych w języku C i w asemblerze.
#include <stdio.h>
void pole_kola (float * pr);
int main()
{
float k;
printf("\nProszę podać promień: ");
scanf_s("%f", &k);
pole_kola (&k);
printf("\nPole koła wynosi %f\n",
k);
return 0;
}
19. W Internecie stosuje się często kodowanie w standardzie MIME Base64. W standardzie tym kolejne trzy bajty pliku traktuje się jako ciąg 24 bitów, które dzieli na cztery grupy po 6 bitów. Zawartość grupy 6-bitowej traktuje się jako liczbę binarną z przedziału 0 63. Liczba ta stanowi indeks w poniższej tablicy znaków
znaki db 'ABCDEFGHIJKLMNOPQRSTUWXYZ'
db abcdefghijklmnopqrstuwxyz0123456789+/'
Kodowanie polega na przypisaniu każdej grupie 6-bitowej odpowiedniego znaku z tablicy. Dekodowanie polega na zastąpieniu znaku w kodzie ASCII spośród podanych wyżej przez 6-bitowy indeks znaku w podanej tablicy.
Napisać podprogram w asemblerze przystosowany do wywoływania z poziomu języka C o prototypie:
void base64_to_obszar (
char * tabl_base64, unsigned int n,
void * obszar) ;
Podprogram powinien przekształcić ciąg n-bajtów w standardzie MIME Base64 wskazany przez pierwszy parametr na kod oryginalny, którego położenie określa trzeci parametr. Zakładamy, że liczba n jest podzielna przez 4.
Napisać krótki program w języku C ilustrujący sposób wywoływania tego podprogramu - jeśli pierwszym argumentem funkcji będzie tablica
tabl[]=”26UtADFA”;
to ciąg wynikowy powinien mieć postać:
0xDB, 0xA5, 0x2D, 0x00, 0x31, 0x40
20. Zmodyfikować program przykładowy podany w instrukcji laboratoryjnej do ćwiczenia 6 (plik gwiazdki.asm) w taki sposób, by znaki gwiazdek wyświetlane na ekranie rozdzielone były znakiem spacji.
Wskazówki i uzupełnienia:
Na dysku D: utworzyć katalog zadanie i przesłać do niego całą zawartość katalogu DOSBox na serwerze mkzl (ścieżka \\mkzl\public\AKO\DOSBox).
Uruchomić maszynę wirtualną DOSBox i wykonać polecenie
mount t d:\zadanie
Przejść do dysku t:
Przeprowadzić asemblację i konsolidację programu gwiazdki.asm, a następnie uruchomić program. Jeśli program działa poprawnie, to można przystąpić do zmiany kodu programu wg podanego wyżej zadania.
Rozwiązanie będzie łatwiejsze, jeśli w kodzie programu (za zmienną wektor8) zdefiniuje się nową zmienną, w której przechowywany będzie kod ostatnio wyświetlonego znaku.
21. Zmodyfikować program przykładowy podany w instrukcji laboratoryjnej do ćwiczenia 6 (plik gwiazdki.asm) w taki sposób, by kolejne gwiazdki wyświetlane były na przemian w kolorze zielonym i czerwonym.
Wskazówki i uzupełnienia:
Na dysku D: utworzyć katalog zadanie i przesłać do niego całą zawartość katalogu DOSBox na serwerze mkzl (ścieżka \\mkzl\public\AKO\DOSBox).
Uruchomić maszynę wirtualną DOSBox i wykonać polecenie
mount t d:\zadanie
Przejść do dysku t:
Przeprowadzić asemblację i konsolidację programu gwiazdki.asm, a następnie uruchomić program. Jeśli program działa poprawnie, to można przystąpić do zmiany kodu programu wg podanego wyżej zadania.
Rozwiązanie będzie łatwiejsze, jeśli w kodzie programu (za zmienną wektor8) zdefiniuje się nową zmienną, w której przechowywany będzie kod ostatnio użytego koloru.