Jak wiemy, wszystkich danych nie zmieścimy w pamięci. A nawet jeśli zmieścimy, to pozostaną tam tylko do najbliższego wyłączenia prądu. Dlatego trzeba je zapisywać do pliku, a potem umieć je z tego pliku odczytać. W tej części zajmiemy się właśnie operacjami na plikach.
Do operowania na plikach posłużymy się kilkoma funkcjami przerwania 80h:
EBX = adres nazwy pliku (zakończonej bajtem zerowym).
ECX = flagi (atrybuty) - 0=Tylko do odczytu, 1=Tylko do zapisu, 2=Odczyt i zapis, 0100h=Utwórz.
EDX = tryb otwarcia (rozkład bitów jest taki sam, jak przy uprawnieniach do pliku, w
kolejności: zapis, odczyt, uruchomienie dla właściciela, grupy i innych).
W EAX funkcja zwraca deskryptor pliku.
EBX = adres nazwy pliku (zakończonej bajtem zerowym).
ECX = tryb utworzenia (bity takie same jak w EDX dla EAX=5).
W EAX funkcja zwraca deskryptor pliku.
EBX = deskryptor (specjalny numerek) pliku.
ECX = adres bufora, do którego będziemy czytać.
EDX = liczba bajtów do odczytania.
W EAX funkcja zwraca liczbę odczytanych bajtów.
EBX = deskryptor pliku.
ECX = adres bufora, z którego będą pobierane dane do zapisu.
EDX = liczba bajtów do zapisania. Jak zapewne sobie przypominacie, tej właśnie
funkcji używaliśmy do wyświetlania napisów na ekranie, z EBX = 1
(1 = standardowe urządzenie wyjścia).
W EAX funkcja zwraca liczbę zapisanych bajtów.
EBX = deskryptor pliku.
EBX = deskryptor pliku.
ECX = długość skoku (może być ujemna).
EDX mówi, skąd wyruszamy: 0 - początek pliku, 1 - bieżąca pozycja w pliku,
2 - koniec pliku.
Zwraca w EAX bieżącą pozycję w pliku.
EBX = adres nazwy pliku (zakończonej bajtem zerowym).
Błędy (podobnie jak w innych funkcjach Linuksowych) są zwykle sygnalizowane przez EAX mniejsze od zera.
Po szczegóły odsyłam do mojego spisu funkcji
systemowych,
linuxasembly.org,
www.lxhp.in-berlin.de/lhpsyscal.html oraz do stron manuala dotyczących
poszczególnych funkcji, na przykład man 2 open.
Przykładowe użycie tych funkcji:
Utworzenie pliku i zapisanie czegoś do niego:
mov eax, 8 ; numer funkcji - tworzenie pliku mov ebx, nazwa ; adres nazwy pliku mov edx, 111111111b ; tryb otwierania - ósemkowo 777 int 80h cmp eax, 0 jl blad ; czy wystąpił błąd? mov ebx, eax ; EBX = deskryptor pliku mov eax, 4 ; numer funkcji - zapis ; EBX = deskryptor pliku mov ecx, bufor ; adres bufora mov edx, 1024 ; liczba bajtów int 80h cmp eax, 0 jl blad ; czy wystąpił błąd? mov eax, 6 ; numer funkcji - zamknij ; EBX = deskryptor pliku int 80h cmp eax, 0 jl blad ; czy wystąpił błąd?
Otwarcie istniejącego pliku, odczytanie i zapisanie czegoś do niego:
mov eax, 5 ; numer funkcji - otwieranie pliku mov ebx, nazwa ; adres nazwy pliku mov ecx, 2 ; zapis i odczyt mov edx, 111111111b ; tryb otwierania - ósemkowo 777 int 80h cmp eax, 0 jl blad ; czy wystąpił błąd? mov ebx, eax ; EBX = deskryptor pliku mov eax, 3 ; numer funkcji - odczyt ; EBX = deskryptor pliku mov ecx, bufor ; adres bufora mov edx, 1024 ; liczba bajtów int 80h cmp eax, 0 jl blad ; czy wystąpił błąd? ; .... operacje na bajtach z pliku, na przykład xor byte [bufor], 0ffh mov eax, 4 ; numer funkcji - zapis ; EBX = deskryptor pliku mov ecx, bufor ; adres bufora mov edx, 1024 ; liczba bajtów int 80h ; Zauważcie, że zapisane bajty wylądowały po odczytanych, gdyż nie ; zmieniliśmy pozycji w pliku, a ostatnia operacja (odczyt) zostawiła ; ją tuż po odczytanych bajtach cmp eax, 0 jl blad ; czy wystąpił błąd? mov eax, 6 ; numer funkcji - zamknij ; EBX = deskryptor pliku int 80h cmp eax, 0 jl blad ; czy wystąpił błąd?
A teraz prawdziwy
przykład.
Będzie to nieco uszczuplona (pominąłem wczytywanie nazwy pliku)
wersja mojego programu na_male.asm
. Program ten zamienia wszystkie wielkie litery w podanym
pliku na ich małe odpowiedniki. Reszta znaków pozostaje bez zmian.
Jedna rzecz jest warta uwagi - nigdzie nie zmieniam rejestru EBX, więc ciągle w nim
jest deskryptor pliku i nie muszę tego uchwytu zapisywać do pamięci.
A teraz kod:
; Program zamienia wszystkie litery w podanym pliku z wielkich na male. ; ; Autor: Bogdan D. ; kontakt: bogdandr (at) op (dot) pl ; ; nasm -O999 -f elf na_male.asm ; ld -s -o na_male na_male.o section .text global _start _start: mov eax, 4 mov ebx, 1 mov ecx, info mov edx, info_dl int 80h ; wypisanie informacji o programie mov eax, 5 mov ebx, plik mov ecx, 2 mov edx, 111000000b ; 700 - zabroń innym dostępu int 80h cmp eax, 0 jnl otw_ok call plik_blad ; uruchamiamy tę procedurę, ; gdy wystąpił błąd jmp zamk_ok ; jeśli nie udało się nam nawet ; otworzyć pliku, to od razu ; wychodzimy z programu. otw_ok: mov ebx, eax ; zapisujemy deskryptor pliku mov ebp, 400h ; EBP = rozmiar bufora. czytaj: mov eax, 3 ; funkcja czytania ; EBX = deskryptor mov ecx, bufor ; adres bufora, dokąd czytamy mov edx, ebp int 80h czyt_ok: xor edi, edi ; EDI będzie wskaźnikiem do bufora. ; Na początku go zerujemy. cmp eax, edx ; czy liczba bajtów odczytana (EAX) = ; = liczba żądana (EDX) ? jne przy_eof ; jeśli nie, to plik się skończył zamiana: mov dl, [bufor+edi] ; wczytujemy znak z bufora do DL cmp dl, "A" jb znak_ok cmp dl, "Z" ja znak_ok or dl, 20h ; jeśli okazał się wielką literą, ; zamieniamy go na małą mov [bufor+edi],dl ; i zapisujemy w miejsce, ; gdzie poprzednio był znak_ok: inc edi ; przechodzimy do innych znaków loop zamiana ; aż przejdziemy przez cały bufor ; (CX = BP = 400h) mov ecx, eax ; ECX = liczba przeczytanych bajtów mov eax, 19 ; funkcja przejścia do innej ; pozycji w pliku ; EBX = deskryptor neg ecx ; ECX = - liczba przeczytanych bajtów mov edx, 1 ; wyruszamy z bieżącej pozycji int 80h cmp eax, 0 jnl idz_ok call plik_blad idz_ok: ; po udanym przeskoku mov eax, 4 ; funkcja zapisu do pliku ; EBX = deskryptor mov ecx, bufor mov edx, ebp ; EDX = EBP = 400h = długość bufora. int 80h cmp eax, 0 jg czytaj ; i idziemy czytać nową partię danych ; (jeśli nie ma błędu) call plik_blad jmp zamk przy_eof: ; gdy jesteśmy już przy końcu pliku. ; xor edi, edi ; EDI już = 0 (zrobione wcześniej) mov ebp, eax ; EBP = liczba przeczytanych znaków mov ecx, eax ; ECX = liczba przeczytanych znaków zamiana2: mov dl, [bufor+edi] ; pobieramy znak z bufora do DL cmp dl, "A" jb znak_ok2 cmp dl, "Z" ja znak_ok2 or dl,20h ; jeśli okazał się wielką literą, ; zamieniamy go na małą mov [bufor+edi],dl ; i zapisujemy w miejsce, ; gdzie poprzednio był znak_ok2: inc edi ; przechodzimy do innych znaków loop zamiana2 ; aż przejdziemy przez cały bufor ; (CX = BP = liczba bajtów) mov ecx, eax ; EDX = liczba przeczytanych bajtów mov eax, 19 ; funkcja przejścia do innej ; pozycji w pliku ; EBX = deskryptor neg ecx ; ECX = - liczba przeczytanych bajtów mov edx, 1 ; wyruszamy z bieżącej pozycji int 80h cmp eax, 0 jnl idz_ok2 call plik_blad idz_ok2: ; po udanym przeskoku mov eax, 4 ; funkcja zapisu do pliku ; EBX = deskryptor mov ecx, bufor mov edx, ebp ; EDX=EBP=liczba przeczytanych bajtów int 80h cmp eax, 0 jnl zamk ; i zamykamy plik (jeśli nie ma błędu) call plik_blad zamk: mov eax, 6 ; zamykamy plik ; EBX = deskryptor int 80h zamk_ok: mov eax, 1 xor ebx, ebx int 80h plik_blad: ; procedura wyświetla informację ; o tym, że wystąpił błąd i ; wypisuje numer tego błędu. push eax push ebx push ecx push edx push ebx mov eax, 4 mov ebx, 1 mov ecx, blad_plik mov edx, blad_plik_dl int 80h ; wypisanie informacji o tym, ; że wystąpił błąd pop ebx call pl ; wypisanie numeru błędu mov eax, 4 mov ebx, 1 mov ecx, nwln mov edx, 1 int 80h ; przejście do nowej linii pop edx pop ecx pop ebx pop eax ret pl: piszrej: ;we: ebx - rejestr do wypisania (hex) ;wy: rejestr, niszczone: eax mov eax, ebx shr eax, 28 call pc2 mov eax, ebx shr eax, 24 and al, 0fh call pc2 mov eax, ebx shr eax, 20 and al, 0fh call pc2 mov eax, ebx shr eax, 16 and al, 0fh call pc2 mov ax, bx shr ax, 12 and al, 0fh call pc2 mov ax, bx shr ax, 8 and al, 0fh call pc2 mov al, bl shr al, 4 and al, 0fh call pc2 mov al, bl and al, 0fh call pc2 ret pc2: ;we: AL - cyfra hex ;wy: wyświetla cyfrę, niszczone: nic push eax push ebx push ecx push edx cmp al, 9 ja hex or al, "0" jmp short pz hex: add al, "A"-10 pz: mov [cyfra], al mov eax, 4 mov ebx, 1 mov ecx, cyfra mov edx, 1 int 80h pop edx pop ecx pop ebx pop eax ret section .data align 16 bufor times 400h db 0 ; bufor wielkości jednego kilobajta ;plik times 80 db 0 plik db "aaa.txt",0 ; nazwa pliku info db "Program zamienia wielkie litery w pliku na male.",10 info_dl equ $-info input1 db "Podaj nazwe pliku do przetworzenia: " input1_dl equ $-input1 zla_nazwa db 10, "Zla nazwa pliku." zla_nazwa_dl equ $-zla_nazwa blad_plik db 10,"Blad operacji na pliku. Kod: " blad_plik_dl equ $-blad_plik cyfra db 0
Ten program chyba nie był za trudny, prawda? Cała treść skupia się na odczytaniu paczki bajtów, ewentualnej ich podmianie i zapisaniu ich w to samo miejsce, gdzie były wcześniej.
Pliki są podstawowym sposobem przechowywania danych. Myślę więc, że się ze mną zgodzicie, iż opanowanie ich obsługi jest ważne i nie jest to aż tak trudne, jakby się mogło wydawać.
00 00 00 01 00 02 00 03 00 04 .... 00 FD 00 FE 00 FFczyli każdy oddzielony bajtem zerowym (należy przeczytać wszystkie bajty, po czym ręcznie je przenieść gdzie indziej i
wzbogacić)