Architektura Komputerów 2 – Laboratorium nr 2 – Pętle, podstawowe operacje logiczne i arytmetyczne

Zadanie nr 1

W ramach zadania domowego mieliśmy rozwinąć kod z pierwszych zajęć zamieniający litery małe na wielkie i odwrotnie. Nowy program dodaje do kodu ASCII każdej małej litery wartość 6 oraz do kodu każdej wielkiej litery wartość 8. Pozostałe znaki nie są zmieniane.

# deklaracja stałych
.data
STDIN = 0
STDOUT = 1
SYSREAD = 0
SYSWRITE = 1
SYSEXIT = 60
EXIT_SUCCESS = 0
BUFLEN = 512
POCZATEK_DUZYCH = 0x41
KONIEC_DUZYCH = 0x5A
POCZATEK_MALYCH = 0x61
KONIEC_MALYCH = 0x7A

# alokacja pamięci pod bufory na wczytywany
# i wyświetlany tekst
.bss
.comm textin, 512
.comm textout, 512

.text
.globl _start

_start:
# pobranie od użytkownika ciągu znaków
mov $SYSREAD, %rax
mov $STDIN, %rdi
mov $textin, %rsi
mov $BUFLEN, %rdx
syscall

# przeniesienie długości wczytanego ciągu do R8
# i wyzerowanie "licznika" RDI (potrzebnego dalej w pętli)
mov %rax, %r8
mov $0, %rdi

jmp petla

petla:
# odczyt znaku do rejestru AH
mov textin(, %rdi, 1), %ah

# jeśli kod znaku jest większy niż początek wielkich liter,
# przeskok do etykiety duze
cmp $POCZATEK_DUZYCH, %ah
jge duze
jmp powrot_do_petli

duze:
# jeśli kod znaku jest większy niż koniec wielkich liter,
# przeskok do etykiety sprawdzaj_dalej
cmp $KONIEC_DUZYCH, %ah
jge sprawdzaj_dalej
# w przeciwnym wypadku (znak to wielka litera)
add $8, %ah # dodanie do kodu znaku liczby 8
jmp powrot_do_petli

sprawdzaj_dalej:
# jeśli kod znaku jest większy niż początek małych liter,
# przeskok do etykiety male
cmp $POCZATEK_MALYCH, %ah
jge male
jmp powrot_do_petli

male:
# jeśli kod znaku jest większy niż koniec małych liter,
# przeskok do etykiety powrot_do_petli
cmp $KONIEC_MALYCH, %ah
jge powrot_do_petli
# w przeciwnym przypadku (znak jest małą literą)
add $6, %ah # dodanie do kodu znaku liczby 6
jmp powrot_do_petli

powrot_do_petli:
# zapis znaku do bufora wyjściowego
movb %ah, textout(, %rdi, 1)

# zwiększenie licznika i wykonanie kolejnych
# iteracji pętli dla kolejnych znaków
inc %rdi
cmp %r8, %rdi
jle petla

# wyświetlenie danych zapisanych do bufora wyjściowego
mov $SYSWRITE, %rax
mov $STDOUT, %rdi
mov $textout, %rsi
mov %r8, %rdx
syscall

# zakończenie działania programu i zwrot wartości na wyjściu
mov $SYSEXIT, %rax
mov $EXIT_SUCCESS, %rdi
syscall

Zadanie nr 2

Przed zajęciami przygotować mieliśmy program konwertujący wprowadzone przez użytkownika liczby w systemie siódemkowym na system dziesiętny. Zadanie zostało rozszerzone na zajęciach o sprawdzanie czy użytkownik wprowadził prawidłowe znaki – cyfry systemu siódemkowego. W przypadku wprowadzenia innego znaku program wyświetla stosowny komunikat.

.data
# deklaracja stałych
STDIN = 0
STDOUT = 1
SYSREAD = 0
SYSWRITE = 1
SYSEXIT = 60
EXIT_SUCCESS = 0
BUFLEN = 512
POCZATEK_LICZB = 0x30 # kod ASCII pierwszej liczby
NOWA_LINIA = 0xA # kod ASCII znaku nowej lini
PODSTAWA_WEJSCIA = 7
PODSTAWA_WYJSCIA = 10
komunikat: .ascii "W systemie 7-kowym nie ma takich znakow\n"
komunikat_len = .-komunikat

.bss
# alokacja miejsc w pamięci
.comm textin, 512 # bufor do którego zostanie
                  # wczytany ciąg od użytkownika
.comm textinv, 512 # bufor do którego zostanie
                   # zapisany wynik w odwrotnej kolejności
.comm textout, 512 # bufor wynikowy do którego przepisana
                   # zostanie zawartość bufora textinv
                   # w odwrotnej kolejności

.text
.globl _start



# WCZYTANIE LICZBY OD UŻYTKOWNIKA
_start:
mov $SYSREAD, %rax
mov $STDIN, %rdi
mov $textin, %rsi
mov $BUFLEN, %rdx
syscall



# ODCZYT Z KODU ASCII DO LICZBY W REJESTRZE

mov %rax, %rdi  # licznik do pętli (będzie liczył od końca)
sub $2, %rdi    # /n nas nie interesuje i liczymy od 0, a nie 1
mov $1, %rsi    # kolejne potęgi 7 (na razie 1)
mov $0, %r8     # miejsce na wynik globalny (na razie 0)
jmp petla

petla:
# wyskok z pętli jeśli RDI jest mniejsze od 0
cmp $0, %rdi
jl przed_petla2
mov $0, %rax               # wyzerowanie RAX
mov textin(, %rdi, 1), %al # odczyt litery do AL
sub $POCZATEK_LICZB, %al   # od teraz w AL jest liczba

# jeśli po odjęciu od kodu znaku kodu początku liczb,
# wartość będzie >= podstawie systemu wejściowego (7-kowego)
# lub <=0, nastąpi przeskok do etykiety blad
cmp $PODSTAWA_WEJSCIA, %al
jge blad

cmp $0, %al
jl blad

mul %rsi      # pomnożenie RAX przez obecną potęgę 7-ki (RSI)
add %rax, %r8 # dodanie obecnego wyniku (RAX/AL)
              # do globalnego (R8)

# pomnożenie obecnej potęgi 7 (RSI) przez 7 ($PODSTAWA_WE)
# mul wymaga umieszczenia mnożnej w rejestrze RAX
# i nie pozwala na mnożenie przez zmienną ($PODSTAWA_WE -> RBX)
mov %rsi, %rax
mov $PODSTAWA_WEJSCIA, %rbx
mul %rbx
mov %rax, %rsi

# zmniejszenie RDI i powrót na początek
dec %rdi
jmp petla



# ZAPIS LICZBY Z REJESTRU DO KODU ASCII W BUFORZE
przed_petla2:
mov %r8, %rax # skopiowanie zdekodowanej liczby do rejestru RAX
mov $PODSTAWA_WYJSCIA, %rbx
mov $0, %rcx
jmp petla2

petla2:
mov $0, %rdx
div %rbx
# dzielenie bez znaku liczby z rejestru RAX przez RBX
# i zapis wyniku do RAX oraz reszty z dzielenie do RDX.
# reszta z dzielenia to przy każdej iteracji pętli kolejna
# pozycja wyniku. po dodaniu kodu znaku pierwszej liczby,
# są to kody znaków ASCII liczb na kolejnych pozycjach.
add $POCZATEK_LICZB, %rdx
# zapis znaków do bufora w odwrotnej kolejności
mov %dl, textinv(, %rcx, 1)

# zwiększenie licznika i w kolejnych iteracjach powrót
# na początek pętli, aż do uzyskania zerowego wyniku dzielenia
inc %rcx
cmp $0, %rax
jne petla2
jmp przed_petla3



# ODWRÓCENIE KOLEJNOŚCI
# po wykonaniu ostatniego kroku, liczby zapisane są w buforze
# w odwrotnej kolejności, w tej pętli są przepisywane z końca
# na początek do nowego bufora textout.
przed_petla3:
mov $0, %rdi
mov %rcx, %rsi
dec %rsi
jmp petla3

petla3:
mov textinv(, %rsi, 1), %rax
mov %rax, textout(, %rdi, 1)

inc %rdi
dec %rsi
cmp %rcx, %rdi
jle petla3
jmp wyswietl



# WYŚWIETLENIE WYNIKU
wyswietl:
# dopisanie na końcu bufora wyjściowego znaku nowej linii
movb $NOWA_LINIA, textout(, %rcx, 1)
inc %rcx

# wyświetlenie tekstu z bufora textout
mov $SYSWRITE, %rax
mov $STDOUT, %rdi
mov $textout, %rsi
mov %rcx, %rdx
syscall

# zwrot zera na wyjściu programu
mov $SYSEXIT, %rax
mov $EXIT_SUCCESS, %rdi
syscall



# WYŚWIETLENIE KOMUNIKATU BŁĘDU
blad:
mov $SYSWRITE, %rax
mov $STDOUT, %rdi
mov $komunikat, %rsi
mov $komunikat_len, %rdx
syscall

# zwrot zera na wyjściu programu
mov $SYSEXIT, %rax
mov $EXIT_SUCCESS, %rdi
syscall

Zadanie nr 3

Na zajęciach przygotowaliśmy program kodujący i dekodujący ciągi liter podanych przez użytkownika przy użyciu szyfru cezara. Program przechowuje w pamięci ciąg cyfr dziesiętnych (zapisanych znakami ASCII) decydujących o liczbie przesunięć oraz znak +/- decydujący o tym czy będziemy szyfrować (+), czy deszyfrować (-). Liczba oraz znak zapisane są kodem ASCII. Kody ASCII zamieniane są na liczbę w rejestrze przy pomocy kodu z zadania domowego.

.data
STDIN = 0
STDOUT = 1
SYSREAD = 0
SYSWRITE = 1
SYSEXIT = 60
EXIT_SUCCESS = 0
BUFLEN = 512
KONIEC_LINI = 0xA
MINUS = 0x2D
POCZATEK_LICZB = 0x30
PODSTAWA_WEJSCIA = 10
ILOSC_LITER = 26
przesuniecie: .ascii "-55"
przesuniecie_len = .-przesuniecie

.bss
.comm textin, 512
.comm textout, 512

.text
.globl _start

_start:
# Pobranie ciągu do zakodowania od użytkownika
mov $SYSREAD, %rax
mov $STDIN, %rdi
mov $textin, %rsi
mov $BUFLEN, %rdx
syscall

# Przeniesienie długości wprowadzonego ciągu znaków do rejestru R9
mov %rax, %r9
dec %r9



# ODCZYT Z KODÓW ASCII DO LICZBY W REJESTRZ
mov $przesuniecie_len, %rdi # licznik do pętli (będzie liczył od końca)
sub $1, %rdi                # liczymy od 0, a nie od 1
mov $1, %rsi                # kolejne potęgi 10 (na razie 1)
mov $0, %r8                 # miejsce na wynik globalny (na razie 0)
jmp petla

petla:
# wyskok z pętli jeśli RDI jest mniejsze od 1
cmp $1, %rdi
jl przed_petla2

mov $0, %rax                     # wyzerowanie RAX
mov przesuniecie(, %rdi, 1), %al # odczyt litery do AL
sub $POCZATEK_LICZB, %al         # od teraz w AL jest liczba
mul %rsi                         # pomnożenie RAX przez obecną potęgę 10-ki (RSI)
add %rax, %r8                    # dodanie obecnego wyniku (RAX/AL)
                                 # do globalnego (R8)

# pomnożenie obecnej potęgi 10 (RSI) przez 10 ($PODSTAWA_WE)
# mul wymaga umieszczenia mnożnej w rejestrze RAX
# i nie pozwala na mnożenie przez zmienną ($PODSTAWA_WE -> RBX)
mov %rsi, %rax
mov $PODSTAWA_WEJSCIA, %rbx
mul %rbx
mov %rax, %rsi

# zmniejszenie RDI i powrót na początek
dec %rdi
jmp petla



# WŁAŚCIWE SZYFROWANIE LUB DESZYFROWANIE
#
# szyfrowanie odbywa się zgodnie z wzorem:
#   (KOD_LITERY-'a'+PRZESUNIĘCIE) mod 26 + 'a'
# deszyfrowanie:
#   (KOD_LITERY-'a'-PRZESUNIĘCIE) mod 26 + 'a' + 26*
#   * - jeśli wynik jest mniejszy niż 'a'
#
przed_petla2:
mov $0, %rdi           # licznik do pętli
mov $ILOSC_LITER, %rcx # dzielnik

jmp petla2

petla2:
mov $0, %rax # wynik dzielenia (nieistotny)
mov $0, %rdx # reszta z dzielenia
mov textin(, %rdi, 1), %al # odczyt kolejnych znaków do AL

sub $'a', %al


# jeśli pierwszy znak zmiennej przesuniecie to minus:
# skocz do dekoduj - odejmij przesunięcie
mov $0, %r10
mov przesuniecie(, %r10, 1), %bl
sub $MINUS, %bl
jz dekoduj

# w przeciwnym przypadku: koduj - dodaj przesunięcie
add %r8, %rax
jmp powrot_do_petli2

dekoduj:
sub %r8, %rax
jmp powrot_do_petli2


powrot_do_petli2:

# jeśli kod znaku po przesunięciu jest ujemny,
# przeskok do etykiety powrot_do_petli2_2
# i dopisanie ciągu FF..FF do uzupełnienia dzielnej,
# tak aby podczas dzielenia, liczba była faktycznie
# traktowana jako ujemna
cmp $0, %rax
jl uzupelnij_dzielna
jmp powrot_do_petli2_2

uzupelnij_dzielna:
mov $-1, %rdx # -1 w sys. dziesiętnym to w U16 ciąg FF..FF
jmp powrot_do_petli2_2

powrot_do_petli2_2:
idiv %rcx # dzielenie z uwzględnieniem znaku liczby

# jeśli po dzieleniu, reszta z dzielenia jest ujemna,
# przeskok do etykiety dodaj26, gdzie do reszty dodawana
# jest ilość wszystkich liter w alfabecie (modulo 26)
cmp $0, %rdx
jl dodaj26
jmp powrot_do_petli2_3

dodaj26:
add $26, %dl
jmp powrot_do_petli2_3

powrot_do_petli2_3:
# dodanie kodu pierwszej litery alfabetu, odjętego wcześniej
# na potrzeby dzielenia modulo
add $'a', %dl

mov %dl, textout(, %rdi, 1)
# zapisanie kodu litery do bufora wyjściowego

# porót na początek pętli aż do wykonania operacji dla każdego znaku
inc %rdi
cmp %r9, %rdi
jle petla2



# DOPISANIE NA KOŃCU BUFORA ZNAKU NOWEJ LINI
mov $KONIEC_LINI, %al
mov %al, textout(, %r9, 1)
inc %r9

# WYŚWIETLENIE WYNIKU
mov $SYSWRITE, %rax
mov $STDOUT, %rdi
mov $textout, %rsi
mov %r9, %rdx
syscall

# ZWROT WARTOŚCI EXIT_SUCCESS
mov $SYSEXIT, %rax
mov $EXIT_SUCCESS, %rdi
syscall

3 komentarze do “Architektura Komputerów 2 – Laboratorium nr 2 – Pętle, podstawowe operacje logiczne i arytmetyczne”

  1. Cześć :)
    Czy będziesz wstawiać swoje rozwiązania do tematu z laboratorium 2: Utrwalenie umiejętności tworzenia prostych konstrukcji
    programowych? Sporo pomagają one zrozumieć temat :D

Skomentuj asd Anuluj pisanie odpowiedzi

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *