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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
.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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 |
.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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 |
.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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 |
.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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 |
.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.