Issuu on Google+

Opis algorytmu zadania Zadanie polega na zamienieniu wartości przechowywanej w programie jako liczba na ciąg znaków (przedstawiających cyfry) do wypisania tej liczby. Wypisywać będziemy w róŜnych systemach liczbowych.

Na pewno moŜna byłoby rozwiązać takie zadanie na róŜne sposoby, ale poniŜszy sposób wydaje się najwygodniejszy:

dla przykładu załóŜmy Ŝe naleŜy wypisać wartość 125 jako liczbę w systemie dziesiętnym;

dodatkowo posłuŜymy się zmienną licznik_cyfr w której zliczymy ile cyfr ma nasza liczba (początkowe zera nie będą nigdy wypisywane, poza jednym wypadkiem, gdyby liczba równała się 0, a wtedy licznik teŜ prawidłowo wskaŜe Ŝe trzeba wypisać tą jedyną cyfrę)

będziemy uŜywać dzielenia – ale w wersji tak zwanego „dzielenia całkowitego”, które zwraca całkowity wynik z dzielenia oraz resztę z dzielenia

licznik_cyfr na początku ustawiamy na zero

1. zaczynamy pętlę i od razu zwiększamy licznik_cyfr o jeden 2. dzielimy 125 przez 10 (poniewaŜ wypisujemy w systemie dziesiętnym) 3. wynik dzielenia (całkowitego) to 12, a reszta to 5 4. resztę z dzielenia (w tym wypadku 5) odkładamy na stos 5. jeśli wynik dzielenia (w tym wypadku 12) jest róŜny od zera, to zachowujemy go do dalszych obliczeń i wracamy do początku pętli (punkt 1.)

1. zaczyna się drugie wykonanie pętli i ponownie zwiększamy licznik_cyfr o jeden 2. dzielimy 12 przez 10


3. wynik dzielenia (całkowitego) to 1, a reszta to 2 4. resztę z dzielenia (w tym wypadku 2) odkładamy na stos 5. jeśli wynik dzielenia (w tym wypadku 1) jest róŜny od zera, to zachowujemy go do dalszych obliczeń i wracamy do początku pętli (punkt 1.)

1. zaczyna się trzecie wykonanie pętli i ponownie zwiększamy licznik_cyfr o jeden 2. dzielimy 1 przez 10 3. wynik dzielenia (całkowitego) to 0, a reszta to 1 4. resztę z dzielenia (w tym wypadku 1) odkładamy na stos 5. wynik dzielenia to w tym wypadku 0 więc pętla zostaje zakończona

Po zakończeniu wykonywania pętli licznik_cyfr zawiera wartość 3 (a więc zgadza się, bo mam 3 cyfry do wypisania), a na stosie odłoŜone są następujące cyfry (niech początek listy oznacza „górę” stosu):

1 2 5

teraz potrzebna będzie jeszcze tylko druga, bardzo prosta pętla, która musi wykonać się liczbę razy zapisaną w zmiennej licznik_cyfr (a więc w tym wypadku 3 razy); za kaŜdym wykonaniem pętla ta po prostu zdejmuje ze stosu cyfrę i wypisuje ją na ekran; a więc najpierw zostanie zdjęta i wypisana cyfra 1, potem 2, a na koniec 5; w efekcie na ekranie pojawi się napis „125” a stos został opróŜniony z odłoŜonych na nim elementów;

W przypadku wypisywania w innych systemach liczbowych postępowanie będzie dokładnie takie samo, tylko inna będzie liczba (podstawa systemu liczbowego) przez jaką będziemy dzielić;


Jeszcze jedna róŜnica dotyczy wypisywania liczb w systemach o podstawie większej niŜ 10 (systemach „większych” niŜ system dziesiętny, czyli np. w systemie szesnastkowym). OtóŜ liczbę wypisujemy na ekran za pomocą cyfr systemu dziesiętnego 0 ... 9 ale jeśli system jest większy to wtedy „zabraknie” nam cyfr i trzeba przyjąć jakieś inne znaki na oznaczenie kolejnych cyfr systemu; jak wiadomo dla systemu szesnastkowego tymi kolejnymi cyframi są litery A ... F

Kodowanie programu program dla asemblera TASM zazwyczaj ma następujący szkielet:

assume cs:code,ds:dane dane segment ; miejsce na zdefiniowane zmiennych, napisów itp. uŜywanych w programie

dane ends code segment

; segment kodu

start:

mov ax,seg dane

; zaladowanie do rejestru AX adresu na segment danych

mov ds,ax

; wartosc z rejestru AX kopiujemy do DS

(wszystko powyŜsze to były takie czynności „inicjalizacyjne”, a teraz będzie zakończenie programu)

mov ah,4ch

; funkcja 4Ch DOSu do poprawnego zakonczenia programu

int 21h

; wywolanie przerwania 21h

code ends end start


na początku programu do rejestru AL. zapiszemy liczbę która ma zostać wypisana mov al,15

poniewaŜ w dalszej części programu rejestr AL. w wyniku przeprowadzanych działań będzie ulegał modyfikacji, dlatego najpierw odłoŜymy jego wartość na stos push ax odłoŜony został cały rejestr AX (a więc AH i AL), poniewaŜ instrukcje pobierania i odkładania na stos mogą działać tylko na elementach 16 lub 32 bitowych więc samego rejestru AL. nie dałoby się odłoŜyć na stos

najpierw wypiszemy liczbę w zapisie dwójkowym (będziemy dzielić przez 2)

zmienna licznik_cyfr będzie w rejestrze CX, zaraz na początku pętli zwiększamy ją o jeden: inc cx

liczbę przez którą dzielimy (w zaleŜności od podstawy wypisywanego systemu liczbowego) zapiszemy w rejestrze BL

dlatego przed rozpoczęciem pętli pierwszej ustawimy te rejestry: mov ah,0 mov cx,0 mov bl,2

jak widać ustawiony został równieŜ rejestr AH na zero ustawiony został on dlatego, Ŝe uŜywamy w pętli instrukcji do dzielenia całkowitego: div bl działa ona w taki sposób, Ŝe dzieli liczbę zapisaną w rejestrze AX przez podaną wartość (tutaj podaną w rejestrze BL); w rejestrze AL. jest juŜ nasza liczba do podzielenia, ale rejestr AH ustawiamy na zero, Ŝeby nie doszło do obliczenia jakiegoś niespodziewanego wyniku (bo liczba w rejestrze AX która będzie dzielona składa się z części AH i AL)


w wyniku dzielenia w rejestrze AL. zostanie zapisany całkowity wynik dzielenia; natomiast reszta z dzielenia trafi do rejestru AH

jak wiadomo reszta z dzielenia to jest ta cyfra którą musimy wypisać; tylko Ŝe przed wypisaniem znaku na ekran trzeba jeszcze zamienić cyfrę (wartość liczbową) na odpowiedni kod znaku ASCII

poniŜszy rysunek przedstawia tablicę znaków ASCII:

jak widać znak cyfry zero ma kod ASCII 48, cyfry jeden ma kod 49 i tak dalej; dobrze Ŝe kody cyfr są w dobrej kolejności obok siebie; dzięki temu uŜyjemy takiego sposobu na zamianę cyfry (wartość liczbowa) na kod ASCII: add ah,'0' w tym poleceniu kompilator asemblera doda do wartości liczbowej (naszej reszty z dzielenia) wartość kodu ASCII znaku zero (zapisanie znaku w apostrofach oznacza dla kompilatora kod ASCII tego znaku);


tak obliczoną wartość kodu ASCII cyfry do wypisania odkładamy na stos: push ax

następnie sprawdzamy czy wynik dzielenia jest róŜny od zera: cmp al,0 jne petla1 jeśli tak, to wracamy do początku pętli, do jej kolejnego wykonania

gdy skończy się juŜ pętla pierwsza wtedy przechodzimy do pętli drugiej, która wypisze obliczone znaki na ekran: petla2: pop ax mov dl,ah

; kod ASCII cyfry do wypisania

mov ah,2

; funkcja DOS do wypisania pojedynczego znaku

int 21h

; wywolanie przerwania 21h

loop petla2

w pętli tej najpierw ściągamy instrukcją pop odłoŜony wcześniej na stos kod znaku; ściąganie ze stosu zacznie się od elementu ostatnio odłoŜonego (to tak jakbyśmy odkładali na biurku ksiąŜki jedna na drugą, a potem zaczynamy zdejmować kolejno te ksiąŜki, to zdejmowanie zaczyna się od ksiąŜki odłoŜonej na końcu, na szczycie stosu) zdjęliśmy ze stosu cały rejestr AX ale oczywiście potrzebna nam jest tylko jego połówka AH w której jest kod ASCII cyfry do wypisania, którą kierujemy na ekran za pomocą przeznaczonego do wypisywania pojedynczych znaków przerwania (funkcji) DOSu

na końcu pętli jest polecenie LOOP etykieta_petli słuŜy ono właśnie do jeszcze wygodniejszego tworzenia pętli (do tworzenia pętli w bardzo podobny sposób jak w językach wysokiego poziomu jest tworzona pętla FOR) działa ona w oparciu o rejestr CX który jest licznikiem dla takiej pętli; (jak wiadomo w CX zapisaliśmy liczbę cyfr do wypisania);


po kaŜdym wykonaniu pętli, gdy dojdzie juŜ do wykonania instrukcji LOOP, instrukcja ta zmniejsza wartość w rejestrze CX o jeden, sprawdza czy CX jest wciąŜ róŜne od zera i jeśli CX jeszcze nie zrównało się z zerem, to wtedy następuje powrót do początku pętli (skok pod etykieta_petli) i kolejne jej wykonanie;

w dalszej części programu liczba jest wypisywana w kolejnych systemach liczbowych w dokładnie ten sam sposób; w zasadzie zamiast robić tak rozwlekły kod, robiący cztery razy praktycznie to samo, moŜna byłoby stworzyć raz w asemblerze funkcję, która tylko jako parametr pobierałaby liczbę do wypisania i podstawę systemu liczbowego i wywołać taką funkcję cztery razy z odpowiednimi parametrami;

tylko fragment ostatni, wypisujący liczbę w systemie szesnastkowym lekko się róŜni, poniewaŜ doszedł w nim fragment, który w razie potrzeby zamiast cyfr 0 ... 9 przelicza wartość liczbową do wypisania na literę A ... F:

cmp ah,10 jae litera_nie_cyfra add ah,'0'jmp przejdz_dalej litera_nie_cyfra: sub ah,10 add ah,'A' przejdz_dalej:

jest on chyba zrozumiały, tylko fragment: sub ah,10 add ah,'A' wymaga dalszego objaśnienia – a mianowicie jeśli reszta z dzielenia przez 16 jest równa lub większa od 10, to wtedy od tej wartości liczbowej odejmujemy 10, a następnie dodajemy do tego kod ASCII znaku ‘A’ – jest to na tej samej zasadzie jak obliczany był kod ASCII cyfr – dzięki temu wartość 10 zostanie przyporządkowany kod ASCII litery A, wartości 11 kod ASCII litery B i tak dalej aŜ do 15 = F.


Opis programu w asemblerze do wypisywania liczb