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ć)