Zespół: PT/17/TP Wrocław, 26.03.2010
Elżbieta Tchorowska, 171067
Konrad Kukulski, 163930
Ocena:
Oddano:
Pętle w programach asemblerowych na platformie Linux
Sprawozdanie z laboratorium z przedmiotu „Architektura Komputerów”
Rok. Akadem. 2009/2010, Kierunek: INF
PROWADZĄCY:
Mgr inż. Mariusz Czapski
Spis treści:
Cel ćwiczenia
Celem ćwiczenia było zapoznanie się ze strukturą pętli w programach asemblerowych i nauka jej wykorzystywania, przy formatowaniu tekstu. Zadaniem było zmodyfikowanie programu z ostatnich laboratoriów, który prosił o podanie ciągu znaków, na taki, który po otrzymaniu tego ciągu wypisze je na ekranie, dzieląc na trzy rzędy.
Przebieg ćwiczenia
Głównym materiałem był plik tekstowy z programem z poprzedniego tygodnia, który prosił o podanie imienia, poczym wypisywał je ponownie. Program wyglądał następująco:
I jego działanie zostało dokładniej opisane w poprzednim sprawozdaniu. Wygląd pliku Makefile, pomija się i przyjmuje, że jego działanie jest wiadome.
Algorytm
Celem przypomnienia: zadaniem było utworzenie trzech rzędów, gdzie w każdej miał znajdować się co trzeci znak wprowadzony z ekranu przez użytkownika. Np. wprowadzono ciąg znaków „abcdefghijk”. Program powinien zwrócić na ekran następujący rezultat:
Opracowany algorytm działał następująco:
wprowadzone znaki przechowujemy w buforze o zadanym, wystarczająco długim rozmiarze
tworzy się trzy zmienne tekstowe, dla każdego rzędu
tworzy się pętlę, której liczba kroków jest równa ilości wprowadzonych znaków
w pętli bierze się każdy kolejny element i dzieli się przez 3
sprawdza się resztę z dzielenia - jeśli wynosi 1, znak przypisujemy do zmiennej 1
jeśli mod wynosi 2, przypisujemy do zmiennej 2
jeśli mod wynosi 0, przypisujemy do zmiennej 3.
wyprowadzamy trzy zmienne na ekran
Działanie pętli
Ustalono, że najlepszą pętlą do wykorzystania w podanym algorytmie będzie pętla for. Ogólną strukturę i formę zapisu pętli podejrzano ze strony http://rudy.mif.pg.gda.pl/~bogdro/linux/index.php i wykorzystano ją w działaniu programu. Pętla wyglądała następująco:
Niestety na zajęciach laboratoryjnych nie udało się przekonwertować podanej pętli na pętlę działającą pod kompilatorem laboratoryjnym. Pętla oddana na zajęciach była błędna i wyglądała następująco:
Przy dłuższej analizie pętli dla danego kompilatora, ustalono prawdziwą strukturę pętli for, która przykładowo powinna wyglądać następująco:
Główny program
Główny program pozostał praktycznie bez zmian w porównaniu do tego, oddawanego na poprzednich laboratoriach. Należało jedynie uwzględnić bufor i trzy dodatkowe zmienne. Po przeróbkach program powinien wyglądać następująco:
Zawartość eax przenosimy do ebx, gdyż w przyszłości akumulator będzie nam potrzebny przy dzieleniu. Eax zatrzymuje się na następnym wolnym bicie, stąd zmniejszenie ebx o jeden, by „wskoczyć” na zapełniony ciąg znaków. Po tym fragmencie powinny pojawić się pętle.
Pętle w programie
Skoki do add1,2 i 3 służą umiejscowieniu znaku w odpowiedniej zmiennej tekstowej. Znak jest mały dlatego można posłużyć się końcówką „b” w komendach.
Add1 służy umiejscowieniu znaku, jeśli występuje jako trzeci lub jako wielokrotność trzeciego. Przypisujemy zatem do zmiennej txt3.
Komenda zapisu/odczytu w buforze lub zmiennej tekstowej wygląda następująco:
Bufor (a, b, c), gdzie:
a - miejsce startu w tablicy
b - ile razy chcemy przesunąć się
c - stopień skoku, ile klastrów zajmuje skok
Deklaracje add2 i add3 wyglądają podobnie:
Pętle w obecnym stanie zawsze zatrzymają się po wykonaniu jednego obiegu. Potrzeba nam jeszcze pętli, która będzie nas przenosić z powrotem do głównego ciała programu. Coś na kształt pętli if.
Wypisanie programu na ekran
Ostatnim krokiem, jest wypisanie zmiennych tekstowych na ekran. Wykonano zwykłą, znaną procedurą SYSWRITE.
Wnioski
Mimo, iż na zajęciach nie udało się stworzyć działającej pętli z powodu braku odpowiednich do tego materiałów i pomocy naukowych, po dokładniejszym zapoznaniu się z problemem, pętle nie przedstawiały większych problemów. Umownie pominięto schemat kompilacji i tworzenia pliku Makefile, przyjmując to za wiadome.
Strona 1 z 7
mov %ecx, 1
petla_for:
cmp %ecx, 10
jae koniec_petli
add %eax, %ecx
add %ecx, 3
jmp petla_for
koniec_petli:
Mov ecx, 1 ; ECX to zmienna I. i=1
petla_for:
cmp ecx, 10
jae koniec_petli ; wychodzimy, gdy i >= 10
add eax, ecx ; EAX to zmienna J. j=j+i
add ecx, 1 ; i=i+1
jmp short petla_for
koniec_petli:
a d g i
b e h j
c f i k
SYSEXIT = 1
SYSREAD = 3
SYSWRITE = 4
STDOUT = 1
STDIN = 0
EXIT_SUCCESS = 0
.align 32
.data
msg_hello: .ascii "Podaj imie\n"
msg_hello_len = . - msg_hello
msg_text: .ascii " "
mas_text_len = . - msg_text
msg_hel: .ascii " "
msg_hel_len = . - msg_hel
.global _start
_start:
mov $SYSWRITE, %eax
mov $STDOUT, %ebx
mov $msg_hello, %ecx
mov $msg_hello_len, %edx
int $0x80
mov $SYSREAD, %eax
mov $STDIN, %ebx
mov $msg_hel, %ecx
mov $msg_hel_len, %edx
int $0x80
mov $SYSWRITE, %eax
mov $STDOUT, %ebx
mov $msg_hel, %ecx
mov $msg_hel_len, %edx
int $0x80
mov $SYSEXIT, %eax
mov $EXIT_SUCCESS, %ebx
int $0x80
p3:
decl %ebx
movl %ebx, %eax
cltd
movl $3, %ecx
div %ecx
popl %ecx
jmp koniec
SYSEXIT = 1
SYSREAD = 3
SYSWRITE = 4
STDOUT = 1
STDIN = 0
EXIT_SUCCESS = 0
bufor_len=200 //dlugosc bufora
txt_len=(200/3)+2 //dlugosc zmiennych txt
.lcomm bufor, bufor_len
.lcomm txt1, txt_len
.lcomm txt2, txt_len
.lcomm txt3, txt_len
.align 32
.data
.global _start
_start:
mov $SYSREAD, %eax //funkcja czytania z
mov $STDIN, %ebx //klawiatury
mov $bufor, %ecx
mov $bufor_len, %edx
int $0x80
movl $txt1, %edi //przypisanie txt do
movl $txt2, %esi //odpowiednich rejestrow
movl $txt3, %ecx
movl %eax, %ebx //przepisanie eax do ebx
decl %ebx //zmniejszenie ebx o 1
p3: //początek petli
decl %ebx //zmniejszenie ebx o jeden na potrzębę pętli
movl %ebx, %eax //przepisanie ebx do eax
cltd //rozłożenie eax na eax i edx
movl $3, %ecx //przeniesienie 3 do ecx przez które będziemy dzielic
div %ecx //dzielenie eax przez ecx -> edx - reszta
cmpl $0, %edx //porównanie reszty do zera
je add1 //jeśli rowne skocz do add1
cmpl $1, %edx //porównanie reszty do 1
je add2 //jeśli rowne skocz do add2
jmp add3 //skok bezwarunkowy do add3
jmp koniec //koniec petli
add1:
movb bufor(0, %ebx, 1), %al //zapis znaku z bufora do akumulatora
movb %al, txt3(0, %edi, 1) //zapis z akumulatora do txt3
incl %edi //zwiekszenie edi o 1
jmp koniec //skok bezwarunkowy
add2:
movb bufor(0, %ebx, 1), %al //zapis znaku z bufora do akumulatora
movb %al, txt1(0, %edi, 1) //zapis z akumulatora do txt1
incl %edi //zwiekszenie edi o 1
jmp koniec //skok bezwarunkowy
add3:
movb bufor(0, %ebx, 1), %al //zapis znaku z bufora do akumulatora
movb %al, txt2(0, %edi, 1) //zapis z akumulatora do txt2
incl %edi //zwiekszenie edi o 1
jmp koniec //skok bezwarunkowy
cmpl $0, %ebx //porównanie ebx do 0
ja p3 //jeśli ebx>0 skacz do p3
mov $SYSWRITE, %eax //wypisanie txt1
mov $STDOUT, %ebx
mov $txt1, %ecx
mov $txt1_len, %edx
int $0x80
mov $SYSWRITE, %eax //wypisanie txt2
mov $STDOUT, %ebx
mov $txt2, %ecx
mov $txt2_len, %edx
int $0x80
mov $SYSWRITE, %eax //wypisanie txt3
mov $STDOUT, %ebx
mov $txt3, %ecx
mov $txt3_len, %edx
int $0x80
mov $SYSEXIT, %eax //zakończenie programu
mov $EXIT_SUCCESS, %ebx
int $0x80