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
Przy odczytaniu pliku z 1 podpunktu wyskakuje mi segmentation fault.
Dokładnie w linii 57? U mnie wersja sprzed okomentowania kodu do sprawozdania działa.