   #Start Prev Next Contents

   Jak pisac programy w jezyku asembler pod Linuksem?

Czesc 11 - Pamiec jest nietrwala, czyli jak poslugiwac sie plikami

   Jak wiemy, wszystkich danych nie zmiescimy w pamieci. A nawet jesli
   zmiescimy, to pozostana tam tylko do najblizszego wylaczenia pradu.
   Dlatego trzeba je zapisywac do pliku, a potem umiec je z tego pliku
   odczytac. W tej czesci zajmiemy sie wlasnie operacjami na plikach.

   Do operowania na plikach posluzymy sie kilkoma funkcjami przerwania
   80h:
     * EAX = 5 (sys_open) - otwarcie/utworzenie pliku.
       EBX = adres nazwy pliku (zakonczonej bajtem zerowym).
       ECX = flagi (atrybuty) - 0=Tylko do odczytu, 1=Tylko do zapisu,
       2=Odczyt i zapis, 0100h=Utworz.
       EDX = tryb otwarcia (rozklad bitow jest taki sam, jak przy
       uprawnieniach do pliku, w kolejnosci: zapis, odczyt, uruchomienie
       dla wlasciciela, grupy i innych).
       W EAX funkcja zwraca deskryptor pliku.
     * EAX = 8 (sys_creat) - utworzenie pliku.
       EBX = adres nazwy pliku (zakonczonej bajtem zerowym).
       ECX = tryb utworzenia (bity takie same jak w EDX dla EAX=5).
       W EAX funkcja zwraca deskryptor pliku.
     * EAX = 3 (sys_read) - odczyt z pliku.
       EBX = deskryptor (specjalny numerek) pliku.
       ECX = adres bufora, do ktorego bedziemy czytac.
       EDX = liczba bajtow do odczytania.
       W EAX funkcja zwraca liczbe odczytanych bajtow.
     * EAX = 4 (sys_write) - zapis do pliku.
       EBX = deskryptor pliku.
       ECX = adres bufora, z ktorego beda pobierane dane do zapisu.
       EDX = liczba bajtow do zapisania. Jak zapewne sobie przypominacie,
       tej wlasnie funkcji uzywalismy do wyswietlania napisow na ekranie,
       z EBX = 1 (1 = standardowe urzadzenie wyjscia).
       W EAX funkcja zwraca liczbe zapisanych bajtow.
     * EAX = 6 (sys_close) - zamyka plik.
       EBX = deskryptor pliku.
     * EAX = 19 (sys_lseek) - przechodzenie na okreslona pozycje w pliku.
       EBX = deskryptor pliku.
       ECX = dlugosc skoku (moze byc ujemna).
       EDX mowi, skad wyruszamy: 0 - poczatek pliku, 1 - biezaca pozycja
       w pliku, 2 - koniec pliku.
       Zwraca w EAX biezaca pozycje w pliku.
     * EAX = 10 (sys_unlink) - usuwa plik.
       EBX = adres nazwy pliku (zakonczonej bajtem zerowym).

   Bledy (podobnie jak w innych funkcjach Linuksowych) sa zwykle
   sygnalizowane przez EAX mniejsze od zera.
   Po szczegoly odsylam do mojego spisu funkcji systemowych,
   linuxasembly.org, www.lxhp.in-berlin.de/lhpsyscal.html oraz do stron
   manuala dotyczacych poszczegolnych funkcji, na przyklad man 2 open.

   Przykladowe uzycie tych funkcji:
   (przeskocz przyklady)

   Utworzenie pliku i zapisanie czegos do niego:
        mov     eax, 8          ; numer funkcji - tworzenie pliku
        mov     ebx, nazwa      ; adres nazwy pliku
        mov     edx, 111111111b ; tryb otwierania - osemkowo 777
        int     80h

        cmp     eax, 0
        jl      blad            ; czy wystapil blad?

        mov     ebx, eax        ; EBX = deskryptor pliku

        mov     eax, 4          ; numer funkcji - zapis
                                ; EBX = deskryptor pliku
        mov     ecx, bufor      ; adres bufora
        mov     edx, 1024       ; liczba bajtow
        int     80h

        cmp     eax, 0
        jl      blad            ; czy wystapil blad?

        mov     eax, 6          ; numer funkcji - zamknij
                                ; EBX = deskryptor pliku
        int     80h

        cmp     eax, 0
        jl      blad            ; czy wystapil blad?

   Otwarcie istniejacego pliku, odczytanie i zapisanie czegos 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 - osemkowo 777
        int     80h

        cmp     eax, 0
        jl      blad            ; czy wystapil blad?

        mov     ebx, eax        ; EBX = deskryptor pliku

        mov     eax, 3          ; numer funkcji - odczyt
                                ; EBX = deskryptor pliku
        mov     ecx, bufor      ; adres bufora
        mov     edx, 1024       ; liczba bajtow
        int     80h

        cmp     eax, 0
        jl      blad            ; czy wystapil blad?

        ; .... operacje na bajtach z pliku, na przyklad
        xor     byte [bufor], 0ffh

        mov     eax, 4          ; numer funkcji - zapis
                                ; EBX = deskryptor pliku
        mov     ecx, bufor      ; adres bufora
        mov     edx, 1024       ; liczba bajtow
        int     80h

        ; Zauwazcie, ze zapisane bajty wyladowaly po odczytanych, gdyz nie
        ; zmienilismy pozycji w pliku, a ostatnia operacja (odczyt) zostawila
        ; ja tuz po odczytanych bajtach

        cmp     eax, 0
        jl      blad            ; czy wystapil blad?

        mov     eax, 6          ; numer funkcji - zamknij
                                ; EBX = deskryptor pliku
        int     80h

        cmp     eax, 0
        jl      blad            ; czy wystapil blad?

   A teraz "prawdziwy" przyklad. Bedzie to nieco uszczuplona (pominalem
   wczytywanie nazwy pliku) wersja mojego programu "na_male.asm". Program
   ten zamienia wszystkie wielkie litery w podanym pliku na ich male
   odpowiedniki. Reszta znakow pozostaje bez zmian. Jedna rzecz jest
   warta uwagi - nigdzie nie zmieniam rejestru EBX, wiec ciagle w nim
   jest deskryptor pliku i nie musze tego uchwytu zapisywac do pamieci. A
   teraz kod:
   (przeskocz na_male.asm)
; 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 - zabron innym dostepu
        int     80h

        cmp     eax, 0

        jnl     otw_ok
        call    plik_blad       ; uruchamiamy te procedure,
                                ; gdy wystapil blad

        jmp     zamk_ok         ; jesli nie udalo sie nam nawet
                                ; otworzyc 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, dokad czytamy
        mov     edx, ebp
        int     80h

czyt_ok:
        xor     edi, edi        ; EDI bedzie wskaznikiem do bufora.
                                ; Na poczatku go zerujemy.

        cmp     eax, edx        ; czy liczba bajtow odczytana (EAX) =
                                ; = liczba zadana (EDX) ?
        jne     przy_eof        ; jesli nie, to plik sie skonczyl

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         ; jesli okazal sie wielka litera,
                                ; zamieniamy go na mala
        mov     [bufor+edi],dl  ; i zapisujemy w miejsce,
                                ; gdzie poprzednio byl

znak_ok:
        inc     edi             ; przechodzimy do innych znakow
        loop    zamiana         ; az przejdziemy przez caly bufor
                                ; (CX = BP = 400h)

        mov     ecx, eax        ; ECX = liczba przeczytanych bajtow

        mov     eax, 19         ; funkcja przejscia do innej
                                ; pozycji w pliku
                                ; EBX = deskryptor
        neg     ecx             ; ECX = - liczba przeczytanych bajtow
        mov     edx, 1          ; wyruszamy z biezacej 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 = dlugosc bufora.
        int     80h

        cmp     eax, 0
        jg      czytaj          ; i idziemy czytac nowa partie danych
                                ; (jesli nie ma bledu)

        call    plik_blad

        jmp     zamk

przy_eof:                       ; gdy jestesmy juz przy koncu pliku.

;       xor     edi, edi        ; EDI juz = 0 (zrobione wczesniej)

        mov     ebp, eax        ; EBP = liczba przeczytanych znakow
        mov     ecx, eax        ; ECX = liczba przeczytanych znakow

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          ; jesli okazal sie wielka litera,
                                ; zamieniamy go na mala
        mov     [bufor+edi],dl  ; i zapisujemy w miejsce,
                                ; gdzie poprzednio byl

znak_ok2:
        inc     edi             ; przechodzimy do innych znakow
        loop    zamiana2        ; az przejdziemy przez caly bufor
                                ; (CX = BP = liczba bajtow)

        mov     ecx, eax        ; EDX = liczba przeczytanych bajtow

        mov     eax, 19         ; funkcja przejscia do innej
                                ; pozycji w pliku
                                ; EBX = deskryptor
        neg     ecx             ; ECX = - liczba przeczytanych bajtow
        mov     edx, 1          ; wyruszamy z biezacej 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 bajtow
        int     80h

        cmp     eax, 0
        jnl     zamk            ; i zamykamy plik (jesli nie ma bledu)

        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 wyswietla informacje
                                ; o tym, ze wystapil blad i
                                ; wypisuje numer tego bledu.
        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,
                                ; ze wystapil blad

        pop     ebx
        call    pl              ; wypisanie numeru bledu

        mov     eax, 4
        mov     ebx, 1
        mov     ecx, nwln
        mov     edx, 1
        int     80h             ; przejscie 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: wyswietla cyfre, 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 wielkosci 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 byl za trudny, prawda? Cala tresc skupia sie na
   odczytaniu paczki bajtow, ewentualnej ich podmianie i zapisaniu ich w
   to samo miejsce, gdzie byly wczesniej.

   Pliki sa podstawowym sposobem przechowywania danych. Mysle wiec, ze
   sie ze mna zgodzicie, iz opanowanie ich obslugi jest wazne i nie jest
   to az tak trudne, jakby sie moglo wydawac.

   Poprzednia czesc kursu (klawisz dostepu 3)
   Kolejna czesc kursu (klawisz dostepu 4)
   Spis tresci off-line (klawisz dostepu 1)
   Spis tresci on-line (klawisz dostepu 2)
   Ulatwienia dla niepelnosprawnych (klawisz dostepu 0)
     _________________________________________________________________

Cwiczenia

    1. Napisz program, ktory wykona po kolei nastepujace czynnosci:
         1. Utworzy nowy plik
         2. Zapisze do niego 256 bajtow o wartosciach od 00 do FF (nie
            musicie zapisywac po 1 bajcie)
         3. Zamknie ten plik
         4. Otworzy ponownie ten sam plik
         5. Zapisze odczytane bajty w nowej tablicy 256 slow w taki
            sposob:
                00 00 00 01 00 02 00 03 00 04 .... 00 FD 00 FE 00 FF
            czyli kazdy oddzielony bajtem zerowym (nalezy przeczytac
            wszystkie bajty, po czym recznie je przeniesc gdzie indziej i
            "wzbogacic")
         6. Zamknie otwarty plik
         7. Usunie ten plik
