Architektura Komputerów 2 – Laboratorium nr 3 – Dostęp do plików przez wywołania systemowe

Obsługa plików Linuksowymi wywołaniami systemowymi

Aby odczytać lub zapisać dane do pliku, z poziomu języka Asembler, musimy posłużyć się wywołaniami systemowymi otwierającymi plik do odczytu i/lub zapisu oraz, po skończonej pracy z plikiem, zamykającymi go, a następnie odczytać lub zapisać dane w sposób identyczny jak ma to miejsce w przypadku odczytu danych od użytkownika z klawiatury lub wyświetlania komunikatów na ekran. Zamiast strumieni (wirtualnych plików) STDIN i STDOUT podaje się tutaj identyfikator otwartego pliku. Podczas otwierania pliku należy wyspecyfikować co zamierzamy z nim zrobić – plik możemy otworzyć tylko do odczytu, do zapisy, do zapisu i odczytu, do dopisywania, etc. W przypadku gdy spróbujemy otworzyć nieistniejący plik do zapisu, plik ten może zostać utworzony. Należy wtedy sprecyzować z jakimi prawami dostępu ma on zostać utworzony podając ich wartość liczbową.

Przykład zapisu do pliku:

.data
SYSREAD = 0
SYSWRITE = 1
SYSOPEN = 2
SYSCLOSE = 3
FREAD = 0
FWRITE = 1
file_out: .ascii "out.txt\0" # Nazwa pliku musi kończyć się zerowym bajtem

.bss
.comm out_buffer, 1024

# Otwarcie pliku $file_out do zapisu

# Jeśli plik nie istnieje, zostanie on utworzony z prawami dostępu 644:
# 6 - zapis (2) i odczyt (4) przez właściciela - użytkownika tworzącego plik
# 4 - odczyt przez grupę do której należy właściciel i został przypisany plik
# (Właściciel może znajdować się w wielu grupach, ale plik musi przynależeć
# do dokładnie jednej z nich. Domyślnie jest to grupa zawierająca jedynie
# właściciela.)
# 4 (kolejne) - odczyt przez wszystkich pozostałych użytkowników

mov $SYSOPEN, %rax  # Pierwszy parametr - numer wywołania systemowego
mov $file_out, %rdi # Drugi parametr - nazwa pliku
mov $FWRITE, %rsi   # Trzeci parametr - sposób otwarcia
mov $0644, %rdx     # Czwarty parametr - prawa dostępu
syscall             # Wywołanie przerwania
mov %rax, %r8       # Przeniesienia wartości zwróconej przez wywołanie
                    # - identyfikatora otwartego pliku, do rejestru R8

# Zapis zawartości bufora out_buffer do pliku
mov $SYSWRITE, %rax
mov %r8, %rdi       # Zamiast STDOUT, podajemy id otwartego pliku
mov $out_buffer, %rsi
mov $1024, %rdx
syscall

# Zamknięcie pliku
mov $SYSCLOSE, %rax # Pierwszy parametr - numer wywołania
mov %r8, %rdi       # Drugi parametr - ID otwartego pliku
syscall             # Wywołanie przerwania

Ponieważ w systemach Unixowych pliki są sposobem reprezentacji wielu rzeczy – np. gniazd lub procesów. W podobny sposób, do przedstawionego powyżej, można by otworzyć np. gniazdo TCP/UDP i nawiązać komunikację przez sieć.

Zadanie 1 – Program konwertujący podaną przez użytkownika liczbę heksadecymalną na system ósemkowy korzystający z własności baz skojarzonych

Program wczytuje wejściowy ciąg heksadecymalny mający do 1024 cyfr heksadecymalnych z pliku in.txt (umieszczonego w tym samym folderze z którego uruchomiony jest program) do bufora in. Następnie następuje zamiana cyfr heksadecymalnych z wczytanego ciągu na wartość liczbową i zapisanie kolejnych bajtów utworzonych z par cyfr heksadecymalnych do bufora value. Dalej, korzystając z własności baz skojarzonych, program pobiera po 3 kolejne bajty (24 bity) bufora wartości do rejestru RAX i odpowiednio go przesuwając w zagnieżdżonej pętli oraz odczytując po 3 bity i dodając do nich wartość znaku ASCII cyfry ‘0’, koduje wartości kolejnych cyfr ósemkowych. Są one zapisywane do bufora out, a dalej, po zakończeniu dekodowania, wyświetlane.

.data
STDIN = 0
STDOUT = 1
SYSREAD = 0
SYSWRITE = 1
SYSOPEN = 2
SYSCLOSE = 3
FREAD = 0
FWRITE = 1
SYSEXIT = 60
EXIT_SUCCESS = 0
file_in: .ascii "in.txt\0"   # Plik z którego odczytany zostanie ciąg wejściowy
file_out: .ascii "out.txt\0" # Plik do którego zostanie zapisany wynik

.bss
.comm in, 1024   # Bufor zawierający znaki (cyfry hex) odczytane z pliku
.comm value, 528 # Bufor zawierający wartości kolejnych bajtów odczytanej
                 # liczby. Jego rozmiar jest spowodowany konwersji systemów
                 # korzystając z właściwości baz skojarzonych. Wspólną
                 # wielokrotnością 4 (ilość bitów które koduje jedna
                 # cyfra heksadecymalna) i 3 (-||- w systemie ósemkowym).
.comm out, 1409  # Bufor wyjściowy zawierający znaki ósemkowe po konwersji.
                 # Rozmiar jest większy o jeden z uwagi na znak końca linii.

.text
.globl main # Dla debugowania

main:
# WYZEROWANIE BUFORA WARTOŚCI
mov $528, %r8 # Licznik dla pętli zerującej
mov $0, %al   # Wartość wstawiana do bufora

petla1:
dec %r8
mov %al, value(, %r8, 1) # Zapisywanie wartości 0 (z AL) do bufora

cmp $0, %r8 # Powrót do petla1 jeśli licznik > 0
jg petla1



# WCZYTANIE PIERWSZEGO CIĄGU
# Otwarcie pliku $file_in do odczytu
mov $SYSOPEN, %rax
mov $file_in, %rdi
mov $FREAD, %rsi
mov $0, %rdx
syscall
mov %rax, %r10 # Przepisanie identyfikatora otwartego pliku do R10

# Odczyt z pliku do bufora
mov $SYSREAD, %rax
#mov $STDIN, %rdi
mov %r10, %rdi
mov $in, %rsi
mov $1024, %rdx
syscall
mov %rax, %r8 # Zapisanie ilości odczytanych bajtów do rejestru R8

# Zamknięcie pliku
mov $SYSCLOSE, %rax
mov %r10, %rdi
mov $0, %rsi
mov $0, %rdx
syscall

# DEKODOWANIE JEGO WARTOŚCI W PĘTLI
dec %r8       # Pomijamy znak końca linii
mov $528, %r9 # Licznik do pętli. Liczymy od końca, aby dekodowane
              # wartości dopisywać od na koniec bufora.

petla2:
dec %r8
dec %r9

# DEKODOWANIE PIERWSZYCH 4 BITÓW
mov in(, %r8, 1), %al

# Wybór odpowiedniej sytuacji poniżej
cmp $'A', %al
jge litera

# Jeśli cyfra < A
sub $'0', %al
jmp powrot_do_petli2_1

# Jeśli cyfra >= A
litera:
sub $55, %al

# Powrót do pętli po odjęciu kodu '0' lub 'A'
# aby uzyskać wartość cyfry z jej kodu ASCII.
powrot_do_petli2_1:

# Wyskok z pętli jeśli zdekodowano ostatnią cyfrę z bufora in
cmp $0, %r8
jle powrot_do_petli2_3

# DEKODOWANIE KOLEJNYCH 4 BITÓW
mov %al, %bl
dec %r8
mov in(, %r8, 1), %al

cmp $'A', %al
jge litera2

sub $'0', %al
jmp powrot_do_petli2_2

litera2:
sub $55, %al

powrot_do_petli2_2:
# Pomnożenie wartości zdekodowanej cyfry przez 16 (drugiej części
# bajtu) i dodanie jej do obecnej liczby w buforze.
mov $16, %cl
mul %cl
add %bl, %al

# Zapisanie zdekodowanego bajtu do nowego bufora
powrot_do_petli2_3:
mov %al, value(, %r9, 1)

# Powrót na początek pętli, aż do zdekodowania całego ciągu
cmp $0, %r8
jg petla2



# KONWERSJA NA SYSTEM ÓSEMKOWY I ZAPIS DO ASCII
mov $527, %r8  # Licznik bajtów z bufora value
mov $1407, %r9 # Licznik znaków ósemkowych z bufora out

petla3:
# Odczyt kolejnych bajtów i przesunięcia bitowe,
# aby pobrać z bufora value, do rejestru RAX
# 3 kolejne bajty we właściwej kolejności.
mov $0, %rax
sub $2, %r8
mov value(, %r8, 1), %al
shl $8, %rax
inc %r8
mov value(, %r8, 1), %al
shl $8, %rax
inc %r8
mov value(, %r8, 1), %al
sub $3, %r8

mov $8, %r10 # Licznik dla zagnieżdżonej pętli w której nastąpi
             # odczyt 8 znaków ósemkowych z 3 bajtowej liczby.

petla4:
mov %al, %bl  # Skopiowanie pierwszego bajtu liczby do rejestru BL
and $7, %bl   # Usunięcie wszystkich bitów poza trzema najmniej znaczącymi
add $'0', %bl # Dodanie kodu znaku ASCII '0' do wyniku maskowania
mov %bl, out(, %r9, 1) # Zapis znaku ASCII do bufora wyjściowego

shr $3, %rax # Przesunięcie bitowe dotychczasowej liczby o 3 bity w prawo,
             # tak aby pozbyć się już zdekodowanych 3 bitów.
dec %r9      # Zmniejszenie liczników pętli
dec %r10
cmp $0, %r10 # Skok na początek zagnieżdżonej pętli,
             # jeśli pozostały jeszcze bity liczby do zdekodowania
jg petla4

cmp $0, %r8  # Skok na początek petla3, aby pobrać kolejne
jg petla3    # 3 bajty cyfry do dekodowania.



# ZAPISANIE WYNIKU
# Otwarcie pliku $file_out do zapisu.
# Jeśli plik nie istnieje, utworzenie go z prawami 644.
mov $SYSOPEN, %rax
mov $file_out, %rdi
mov $FWRITE, %rsi
mov $0644, %rdx
syscall
mov %rax, %r8

# Zapis bufora out do pliku
movq $1408, %r9
movb $0x0A, out(, %r9, 1)
mov $SYSWRITE, %rax
#mov $STDOUT, %rdi
mov %r8, %rdi
mov $out, %rsi
mov $1409, %rdx
syscall

# Zamknięcie pliku
mov $SYSCLOSE, %rax
mov %r8, %rdi
mov $0, %rsi
mov $0, %rdx
syscall

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

Zadanie 2 – Program konwertujący liczbę podaną w systemie czwórkowym na zapis binarny

Program jest modyfikacją opisanego uprzednio kodu z zadania domowego. Zasadniczo działanie programu jest identyczne, jednak tym razem w pętli dekodującej, odczytywane są jedynie pełne bajty, a zagnieżdżona pętla wywoływana jest 8 razy – tak aby zdekodować wszystkie bity z osobna.

Szczegółowy opis działania programu ujęty został w komentarzach w kodzie:

.data
STDIN = 0
STDOUT = 1
SYSREAD = 0
SYSWRITE = 1
SYSOPEN = 2
SYSCLOSE = 3
FREAD = 0
FWRITE = 1
SYSEXIT = 60
EXIT_SUCCESS = 0
file_in: .ascii "in.txt\0"   # Plik z którego odczytany zostanie ciąg wejściowy
file_out: .ascii "out.txt\0" # Plik do którego zostanie zapisany wynik

.bss
.comm in, 1024   # Bufor zawierający znaki (cyfry czwórkowe) odczytane z pliku
.comm value, 256 # Bufor zawierający wartości kolejnych bajtów odczytanej liczby.
.comm out, 2049  # Bufor zawierający znaki ASCII 0/1 wyniku + znak końca linii.

.text
.globl main # Dla debugowania

main:
# WYZEROWANIE BUFORA WARTOŚCI
mov $256, %r8
mov $0, %al

petla1:
dec %r8
mov %al, value(, %r8, 1)
cmp $0, %r8
jg petla1



# WCZYTANIE PIERWSZEGO CIĄGU
# Otwarcie pliku $file_in do odczytu
mov $SYSOPEN, %rax
mov $file_in, %rdi
mov $FREAD, %rsi
mov $0, %rdx
syscall
mov %rax, %r10 # Przepisanie identyfikatora otwartego pliku do R10

# Odczyt z pliku do bufora
mov $SYSREAD, %rax
#mov $STDIN, %rdi
mov %r10, %rdi
mov $in, %rsi
mov $1024, %rdx
syscall
mov %rax, %r8 # Zapisanie ilości odczytanych bajtów do rejestru R8

# Zamknięcie pliku
mov $SYSCLOSE, %rax
mov %r10, %rdi
mov $0, %rsi
mov $0, %rdx
syscall

# DEKODOWANIE JEGO WARTOŚCI W PĘTLI
dec %r8       # Pomijamy znak końca linii
mov $256, %r9 # Licznik do pętli. Liczymy od końca, aby dekodowane
              # wartości dopisywać od na koniec bufora.

petla2:
dec %r8
dec %r9

# Zdekodowanie pierwszych 2 bitów
mov in(, %r8, 1), %al # Wczytanie kodu znaku do rejestru AL
sub $'0', %al # Odjęcie kodu znaku '0', aby uzyskać liczbę
cmp $0, %r8   # Jeśli pozostała ilość znaków do odczytania
              # jest równa zero - przeskok do etykiety zapisz
jle zapisz

# Zdekodowanie kolejnych 2 bitów
dec %r8
mov in(, %r8, 1), %bl # Pobranie kolejnej cyfry do rejestru BL
sub $'0', %bl
shl $2, %bl  # Przesunięcie bitowe na odpowiednią pozycje
add %bl, %al # Dodanie wyniku (obecnych 2 bitów) do rejestru AL (pełnych 8 bitów)

cmp $0, %r8
jle zapisz

# Zdekodowanie kolejnych 2 bitów
dec %r8
mov in(, %r8, 1), %bl
sub $'0', %bl
shl $4, %bl
add %bl, %al

cmp $0, %r8
jle zapisz

# Zdekodowanie kolejnych 2 bitów
dec %r8
mov in(, %r8, 1), %bl
sub $'0', %bl
shl $6, %bl
add %bl, %al

cmp $0, %r8
jle zapisz

# Zapisanie wartości do nowego bufora
zapisz:
mov %al, value(, %r9, 1)

# Powrót na początek pętli, aż do zdekodowania całego ciągu
cmp $0, %r8
jg petla2



# KONWERSJA NA BIN I ZAPIS DO BUFORA WYJŚCIOWEGO
mov $256, %r8  # Licznik bajtów z bufora value
mov $2047, %r9 # Licznik znaków cyfr binarnych ('0'/'1') z bufora out

petla3:
dec %r8
mov value(, %r8, 1), %al # Odczyt pełnego bajta liczby z bufora wartości

mov $8, %r10 # Licznik do pętli zagnieżdżonej
             # Pętla wykona się 8 razy - dla każdego bitu z osobna

petla4:
mov %al, %bl  # Skopiowanie obecnej wartości do rejestru BL
and $1, %bl   # Usunięcie z niego wszystkich bitów poza najmniej znaczącym
add $'0', %bl # Dodanie do wyniku kodu znaku ASCII '0'
mov %bl, out(, %r9, 1) # Zapis wyniku do bufora wyjściowego

shr $1, %rax # Przesunięcie obecnej wartości o 1 w prawo,
             # aby usunąć już zdekodowaną pozycję.
dec %r9      # Zmniejszenie liczników pętli
dec %r10
cmp $0, %r10 # Powrót na początek pętli,
jg petla4    # aż do wykonania operacji dla każdego bitu

cmp $0, %r8  # Skok na początek petla3, aby pobrać kolejny bajt danych
jg petla3



# ZAPISANIE WYNIKU
# Otwarcie pliku $file_out do zapisu.
# Jeśli plik nie istnieje, utworzenie go z prawami 644.
mov $SYSOPEN, %rax
mov $file_out, %rdi
mov $FWRITE, %rsi
mov $0644, %rdx
syscall
mov %rax, %r8

# Zapis bufora out do pliku
movq $2048, %r9
movb $0x0A, out(, %r9, 1)
mov $SYSWRITE, %rax
#mov $STDOUT, %rdi
mov %r8, %rdi
mov $out, %rsi
mov $2049, %rdx
syscall

# Zamknięcie pliku
mov $SYSCLOSE, %rax
mov %r8, %rdi
mov $0, %rsi
mov $0, %rdx
syscall

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

Zadanie 3 – Program dodający dwie podane przez użytkownika liczby heksadecymalne

Program wczytuje podane przez użytkownika ciągi znaków heksadecymalnych do buforów ascii_in1 oraz ascii_in2. Następnie podobnie jak w zadaniu pierwszym, następuje zdekodowanie tych ciągów heksadecymalnych do postaci bajtowej w buforach value_in1 i value_in2. Dalej obie liczby, bajt po bajcie, dodawane są z obsługą propagacji przeniesienia. Przed pierwszym dodawaniem, za pomocą instrukcji clc czyszczona jest flaga przeniesienia z poprzedniej pozycji. Ponieważ ta sama flaga w rejestrze flagowym modyfikowana jest przez instrukcję porównania – cmp, po dodaniu liczb, rejestr flagowy umieszczany jest na stosie, a przed dodaniem jego wartość jest ze stosu pobierana. Wynik dodawania umieszczany jest w buforze value_out. Po dodaniu liczb, zawartość tego bufora konwertowana jest ponownie na zapis heksadecymalny i umieszczana w buforze ascii_out, a następnie wyświetlana.

.data
STDIN = 0
STDOUT = 1
SYSREAD = 0
SYSWRITE = 1
SYSEXIT = 60
EXIT_SUCCESS = 0

.bss
.comm ascii_in1, 1024 # Bufor przechowujący pierwszy ciąg znaków ASCII
.comm ascii_in2, 1024 # Bufor przechowujący drugi ciąg znaków ASCII
.comm ascii_out, 1027 # Bufor przechowujący wynikowy ciąg znaków ASCII
                      # Jest on większy od poprzednich o 1 znak,
                      # aby pomieścić przeniesienie z poprzedniej pozycji
                      # i znak nowej linii.
.comm value_in1, 513 # Bufor przechowujący pierwszą liczbę w postaci bajtowej
.comm value_in2, 513 # -||- drugą
.comm value_out, 513 # -||- wynik

.text
.globl main

main:
# WYZEROWANIE BUFORÓW WARTOŚCI
mov $513, %r8
mov $0, %al

petla1:
dec %r8
mov %al, value_in1(, %r8, 1)
mov %al, value_in2(, %r8, 1)
cmp $0, %r8
jg petla1



# WCZYTANIE PIERWSZEGO CIĄGU
mov $SYSREAD, %rax
mov $STDIN, %rdi
mov $ascii_in1, %rsi
mov $1024, %rdx
syscall

# DEKODOWANIE JEGO WARTOŚCI W PĘTLI
mov %rax, %r8 # Przeniesienie długości wczytanego ciągu do rejestru R8
dec %r8       # Nie potrzebujemy znaku końca linii
mov $513, %r9 # Licznik do pętli (liczymy od końca)

petla2:
dec %r8
dec %r9

# DEKODOWANIE PIERWSZYCH 4 BITÓW
mov ascii_in1(, %r8, 1), %al

# Wybór odpowiedniej sytuacji poniżej
cmp $'A', %al
jge litera

# Jeśli cyfra < A
sub $'0', %al
jmp powrot_do_petli2_1

# Jeśli cyfra >= A
litera:
sub $55, %al

# Powrót do pętli po odjęciu kodu '0' lub 'A'
# aby uzyskać wartośc cyfry z jej kodu ASCII.
powrot_do_petli2_1:

# Wyskok z pętli jeśli zdekodowano ostatnią cyfrę z bufora valueX_in
cmp $0, %r8
jle powrot_do_petli2_3

# DEKODOWANIE KOLEJNYCH 4 BITÓW
mov %al, %bl
dec %r8
mov ascii_in1(, %r8, 1), %al

cmp $'A', %al
jge litera2

sub $'0', %al
jmp powrot_do_petli2_2

litera2:
sub $55, %al

powrot_do_petli2_2:
# Pomnożenie wartości zdekodowanej cyfry przez 16 (drugiej części
# bajtu) i dodanie jej do obecnej liczby w buforze.
mov $16, %cl
mul %cl
add %bl, %al

# Zapisanie zdekodowanego bajtu danych do nowego bufora
powrot_do_petli2_3:
mov %al, value_in1(, %r9, 1)

# Powrót na początek pętli, aż do zdekodowania całego ciągu
cmp $0, %r8
jg petla2



# WCZYTANIE DRUGIEGO CIĄGU
mov $SYSREAD, %rax
mov $STDIN, %rdi
mov $ascii_in2, %rsi
mov $1024, %rdx
syscall

# DEKODOWANIE JEGO WARTOŚCI W PĘTLI
mov %rax, %r8
dec %r8
mov $513, %r9

petla3:
dec %r8
dec %r9

# DEKODOWANIE PIERWSZYCH 4 BITÓW
mov ascii_in2(, %r8, 1), %al

cmp $'A', %al
jge litera3

sub $'0', %al
jmp powrot_do_petli3_1

litera3:
sub $55, %al

powrot_do_petli3_1:
cmp $0, %r8
jle powrot_do_petli3_3

# DEKODOWANIE KOELJNYCH 4 BITÓW
mov %al, %bl
dec %r8
mov ascii_in2(, %r8, 1), %al

cmp $'A', %al
jge litera4

sub $'0', %al
jmp powrot_do_petli3_2

litera4:
sub $55, %al

powrot_do_petli3_2:
mov $16, %cl
mul %cl
add %bl, %al

# Zapisanie zdekodowanej bajtu do nowego bufora
powrot_do_petli3_3:
mov %al, value_in2(, %r9, 1)

cmp $0, %r8
jg petla3



# DODANIE OBU WARTOSCI
clc           # Wyczyszczenie flagi przeniesienia z poprzedniej pozycji
pushfq        # Umieszczenie rejestru flagowego na stosie
mov $512, %r8 # Licznik do pętli

petla4:
mov value_in1(, %r8, 1), %al # Odczyt wartości z buforów
mov value_in2(, %r8, 1), %bl # do rejestrów AL i BL
popfq        # Pobranie zawartości rejestru flagowego ze stosu
             # (rozkaz CMP modyfikuje flagę przeniesienia)
adc %bl, %al # Dodanie z propagacją i przyjęciem przeniesienia
pushfq       # Umieszczenie rejestru flagowego na stosie
mov %al, value_out(, %r8, 1) # Zapis nowej wartości do bufora

dec %r8     # Zmniejszenie licznika pętli i powrót na jej początek
cmp $0, %r8 # aż do wykonania dodawania dla każdej pozycji w buforze wynikowym.
jg petla4



# ZAMIANA NA ZNAKI HEX
mov $512, %r8  # Liczniki do pętli
mov $1025, %r9

petla5:
mov value_out(, %r8, 1), %al # Odczyt wartości
mov %al, %bl # Skopiowanie wartości do rejestru AL
mov %al, %cl # -||- CL
# "Rozdzielenie" liczby na dwie 4 bitowe części.
# W rejestrze BL znajdą się 4 najmniej znaczące bity.
# W rejestrze CL 4 kolejne.
shr $4, %cl
and $0b1111, %bl
and $0b1111, %cl
add $'0', %bl # Dodanie kodów ASCII '0' do każdej z części
add $'0', %cl

# Jeśli wartość w BL jest większa niż 9,
# dodanie stosownej wartości, tak aby wynikiem była litera A-F.
cmp $'9', %bl
jle dalej
add $7, %bl

dalej:
# Podobnie jak wyżej, ale dla rejestru CL.
cmp $'9', %cl
jle dalej2
add $7, %cl

dalej2:
# Zapis wartości do bufora wynikowego
# i zmniejszenie licznika pozycji w tym buforze.
mov %bl, ascii_out(, %r9, 1)
dec %r9
mov %cl, ascii_out(, %r9, 1)
dec %r9

# Zmniejszenie licznika pętli i wykonanie jej dla wszystkich pozycji liczb.
dec %r8
cmp $0, %r8
jge petla5



# WYŚWIETLENIE WYNIKU
movq $1026, %r8
movb $0x0A, ascii_out(, %r8, 1) # Dodanie do wyniku znaku końca linii
mov $SYSWRITE, %rax
mov $STDOUT, %rdi
mov $ascii_out, %rsi
mov $1027, %rdx
syscall

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

Zadanie 4 – Program dodający dwie podane przez użytkownika liczby zapisane w U16

Program jest modyfikacją kodu z zadania domowego opisanego powyżej. Jedyna zmiana polega na uzupełnieniu bufora wartości (value_inX) wartościami 0xFF jeśli wprowadzona liczba jest ujemna. Jeśli liczba składa się z nieparzystej ilości cyfr heksadecymalnych, najstarsza pozycja w buforze uzupełniana jest wartością 0xF0.

.data
STDIN = 0
STDOUT = 1
SYSREAD = 0
SYSWRITE = 1
SYSEXIT = 60
EXIT_SUCCESS = 0

.bss
.comm ascii_in1, 1024 # Bufor przechowujący pierwszy ciąg znaków ASCII
.comm ascii_in2, 1024 # Bufor przechowujący drugi ciąg znaków ASCII
.comm ascii_out, 1027 # Bufor przechowujący wynikowy ciąg znaków ASCII
                      # Jest on większy od poprzednich o 1 znak,
                      # aby pomieścić przeniesienie z poprzedniej pozycji
                      # i znak nowej linii.
.comm value_in1, 513 # Bufor przechowujący pierwszą liczbę w postaci bajtowej
.comm value_in2, 513 # -||- drugą
.comm value_out, 513 # -||- wynik

.text
.globl main

main:
# WYZEROWANIE BUFORÓW WARTOŚCI
mov $513, %r8
mov $0, %al

petla1:
dec %r8
mov %al, value_in1(, %r8, 1)
mov %al, value_in2(, %r8, 1)
cmp $0, %r8
jg petla1



# WCZYTANIE PIERWSZEGO CIĄGU
mov $SYSREAD, %rax
mov $STDIN, %rdi
mov $ascii_in1, %rsi
mov $1024, %rdx
syscall

# DEKODOWANIE JEGO WARTOŚCI W PĘTLI
mov %rax, %r8 # Przeniesienie długości wczytanego ciągu do rejestru R8
dec %r8       # Nie potrzebujemy znaku końca linii
mov $513, %r9 # Licznik do pętli (liczymy od końca)

petla2:
dec %r8
dec %r9

# DEKODOWANIE PIERWSZYCH 4 BITÓW
mov ascii_in1(, %r8, 1), %al

# Wybór odpowiedniej sytuacji poniżej
cmp $'A', %al
jge litera

# Jeśli cyfra < A
sub $'0', %al
jmp powrot_do_petli2_1

# Jeśli cyfra >= A
litera:
sub $55, %al

# Powrót do pętli po odjęciu kodu '0' lub 'A'
# aby uzyskać wartośc cyfry z jej kodu ASCII.
powrot_do_petli2_1:

# Flaga RDI po zakończeniu dekodowania będzie zawierała informacje,
# czy dekodowanie zakończono na pozycji parzystej czy też nie.
# W przypadku gdy podana liczba jest ujemna, na tej podstawie
# uzupełniony zostanie bufor wartości.
mov $0, %rdi

# Wyskok z pętli jeśli zdekodowano ostatnią cyfrę z bufora valueX_in
cmp $0, %r8
jle powrot_do_petli2_3

mov $1, %rdi

# DEKODOWANIE KOLEJNYCH 4 BITÓW
mov %al, %bl
dec %r8
mov ascii_in1(, %r8, 1), %al

cmp $'A', %al
jge litera2

sub $'0', %al
jmp powrot_do_petli2_2

litera2:
sub $55, %al

powrot_do_petli2_2:
# Pomnożenie wartości zdekodowanej cyfry przez 16 (drugiej części
# bajtu) i dodanie jej do obecnej liczby w buforze.
mov $16, %cl
mul %cl
add %bl, %al

# Zapisanie zdekodowanego bajtu danych do nowego bufora
powrot_do_petli2_3:
mov %al, value_in1(, %r9, 1)

# Powrót na początek pętli, aż do zdekodowania całego ciągu
cmp $0, %r8
jg petla2

# UZUPEŁNIENIE PIERWSZEGO CIĄGU DLA U16
# Pobieranie pierwszego znaku z bufora wejściowego
mov $0, %rsi
mov ascii_in1(, %rsi, 1), %al

# Jeśli jest on mniejszy niż 8 - pominięcie tego kroku
cmp $56, %al # 8 W ASCII
jl pomin

# W przeciwnym przypadku - uzupełnienie początku bufora o wartości 0xFF
petla_a:
	mov $255, %al
	mov %al, value_in1(, %rsi, 1)
	inc %rsi
cmp %r9, %rsi
jl petla_a

# Jeśli dekodowanie zakończono w połowie bajtu, uzupełnienie
# tego bajtu dodając do niego 0xF0
cmp $0, %rdi
jg pomin
	mov value_in1(, %rsi, 1), %al
	add $240, %al
	mov %al, value_in1(, %rsi, 1)
pomin:



# WCZYTANIE DRUGIEGO CIĄGU
mov $SYSREAD, %rax
mov $STDIN, %rdi
mov $ascii_in2, %rsi
mov $1024, %rdx
syscall

# DEKODOWANIE JEGO WARTOŚCI W PĘTLI
mov %rax, %r8
dec %r8
mov $513, %r9

petla3:
dec %r8
dec %r9

# DEKODOWANIE PIERWSZYCH 4 BITÓW
mov ascii_in2(, %r8, 1), %al

cmp $'A', %al
jge litera3

sub $'0', %al
jmp powrot_do_petli3_1

litera3:
sub $55, %al

powrot_do_petli3_1:
mov $0, %rdi
cmp $0, %r8
jle powrot_do_petli3_3
mov $1, %rdi

# DEKODOWANIE KOLEJNYCH 4 BITÓW
mov %al, %bl
dec %r8
mov ascii_in2(, %r8, 1), %al

cmp $'A', %al
jge litera4

sub $'0', %al
jmp powrot_do_petli3_2

litera4:
sub $55, %al

powrot_do_petli3_2:
mov $16, %cl
mul %cl
add %bl, %al

# Zapisanie bajtu do nowego bufora
powrot_do_petli3_3:
mov %al, value_in2(, %r9, 1)

cmp $0, %r8
jg petla3

# UZUPEŁNIENIE DRUGIEGO CIĄGU DLA U16
mov $0, %rsi
mov ascii_in2(, %rsi, 1), %al
cmp $56, %al # 8 W ASCII
jl pomin2

petla_b:
	mov $255, %al
	mov %al, value_in2(, %rsi, 1)
	inc %rsi
cmp %r9, %rsi
jl petla_b

cmp $0, %rdi
jg pomin2
	mov value_in2(, %rsi, 1), %al
	add $240, %al
	mov %al, value_in2(, %rsi, 1)
pomin2:



# DODANIE OBU WARTOSCI
clc           # Wyczyszczenie flagi przeniesienia z poprzedniej pozycji
pushfq        # Umieszczenie rejestru flagowego na stosie
mov $512, %r8 # Licznik do pętli

petla4:
mov value_in1(, %r8, 1), %al # Odczyt wartości z buforów
mov value_in2(, %r8, 1), %bl # do rejestrów AL i BL
popfq        # Pobranie zawartości rejestru flagowego ze stosu
             # (rozkaz CMP modyfikuje flagę przeniesienia)
adc %bl, %al # Dodanie z propagacją i przyjęciem przeniesienia
pushfq       # Umieszczenie rejestru flagowego na stosie
mov %al, value_out(, %r8, 1) # Zapis nowej wartości do bufora

dec %r8     # Zmniejszenie licznika pętli i powrót na jej początek
cmp $0, %r8 # aż do wykonania dodawania dla każdej pozycji w buforze wynikowym.
jg petla4



# ZAMIANA NA ZNAKI HEX
mov $512, %r8  # Liczniki do pętli
mov $1025, %r9

petla5:
mov value_out(, %r8, 1), %al # Odczyt wartości
mov %al, %bl # Skopiowanie wartości do rejestru AL
mov %al, %cl # -||- CL
# "Rozdzielenie" liczby na dwie 4 bitowe części.
# W rejestrze BL znajdą się 4 najmniej znaczące bity.
# W rejestrze CL 4 kolejne.
shr $4, %cl
and $0b1111, %bl
and $0b1111, %cl
add $'0', %bl # Dodanie kodów ASCII '0' do każdej z części
add $'0', %cl

# Jeśli wartość w BL jest większa niż 9,
# dodanie stosownej wartości, tak aby wynikiem była litera A-F.
cmp $'9', %bl
jle dalej
add $7, %bl

dalej:
# Podobnie jak wyżej, ale dla rejestru CL.
cmp $'9', %cl
jle dalej2
add $7, %cl

dalej2:
# Zapis wartości do bufora wynikowego
# i zmniejszenie licznika pozycji w tym buforze.
mov %bl, ascii_out(, %r9, 1)
dec %r9
mov %cl, ascii_out(, %r9, 1)
dec %r9

# Zmniejszenie licznika pętli i wykonanie jej dla wszystkich pozycji liczb.
dec %r8
cmp $0, %r8
jge petla5



# WYŚWIETLENIE WYNIKU
movq $1026, %r8
movb $0x0A, ascii_out(, %r8, 1) # Dodanie do wyniku znaku końca linii
mov $SYSWRITE, %rax
mov $STDOUT, %rdi
mov $ascii_out, %rsi
mov $1027, %rdx
syscall

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

2 komentarze do “Architektura Komputerów 2 – Laboratorium nr 3 – Dostęp do plików przez wywołania systemowe”

Dodaj komentarz

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