   #Start Prev Next Contents

   Jak pisac programy w jezyku asembler?

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
   21h:
     * AH = 3Ch - utworzenie pliku (wymazanie, gdy juz istnieje).
       W rejestrze CX podajemy atrybuty pliku (ustawiony bit 0 oznacza
       plik tylko do odczytu, bit 1 - czy plik ma byc ukryty, bit 2 -
       plik systemowy, 3 - etykieta woluminu, 4 - zawsze zero, 5 - plik
       archiwalny), a DS:DX wskazuje na nazwe pliku.
       UWAGA: Nazwa musi byc zakonczona bajtem zerowym
       Niewypelnienie powyzszego warunku jest przyczyna wielu bledow w
       programach.
       W rejestrze AX otrzymujemy "uchwyt do pliku" (file handle) -
       specjalna wartosc przydzielana nam przy otwieraniu pliku.
     * AH = 3Dh - otworzenie istniejacego pliku.
       W rejestrze AL podajemy tryb dostepu, DS:DX wskazuje na nazwe
       pliku.
       Tryby dostepu okresla sie nastepujacymi bitami w AL (pomijam
       nieistotne informacje, calosc mozecie znalezc w RBIL):
        Bit     Opis
        2-0     tryb dostepu
                000 tylko do odczytu
                001 tylko do zapisu
                010 odczyt i zapis
        3       zarezerwowany, musi byc 0
        6-4     tryb wspoldzielenia (DOS 3.0+)
                000 tryb zgodnosci
                001 DENYALL - zabron innym odczytu i zapisu
                010 DENYWRITE - zabron innym zapisu
                011 DENYREAD - zabron innym odczytu
                100 DENYNONE - niczego nie zabraniaj
        7       dziedziczenie
                Jesli ten bit jest ustawiony, plik jest prywatny dla biezacego
procesu
                i nie bedzie dziedziczony przez procesy potomne
       UWAGA: Nazwa musi byc zakonczona bajtem zerowym
       W rejestrze AX otrzymujemy "uchwyt do pliku" (file handle) -
       specjalna wartosc przydzielana nam przy otwieraniu pliku.
     * AH = 3Eh - zamkniecie otwartego pliku.
       W rejestrze BX podajemy uchwyt do pliku.
     * AH = 3Fh - odczyt z pliku.
       W rejestrze BX podajemy uchwyt do pliku, w CX - liczba bajtow do
       odczytania, DS:DX wskazuje na miejsce, dokad bedziemy zapisywac.
     * AH = 40h - zapis do pliku.
       W rejestrze BX podajemy uchwyt do pliku, w CX - liczba bajtow do
       zapisania, DS:DX wskazuje na miejsce, z ktorego bedziemy czytac
       dane do zapisania.
     * AH = 42h - przechodzenie na okreslona pozycje w pliku.
       Rejestr AL mowi DOSowi, skad wyruszamy: 0 - z poczatku pliku, 1 -
       z biezacej pozycji, 2 - z konca pliku. BX = uchwyt pliku, CX : DX
       - odleglosc, o ktora sie przesuwamy (moze byc ujemna).
     * AH = 41h - usuwanie pliku.
       DS:DX wskazuje na nazwe pliku.
       UWAGA: Nazwa musi byc zakonczona bajtem zerowym

   Wszystkie te funkcje ustawiaja flage carry (CF=1), gdy wystapil jakis
   blad.
   Po szczegoly (w tym kody bledow) odsylam do Listy Przerwan Ralfa
   Brown'a.

   Przykladowe uzycie tych funkcji:
   (przeskocz przyklady)
   Utworzenie pliku i zapisanie czegos do niego:
        mov     ah, 3ch         ; numer funkcji - utworzenie
        mov     dx, nazwa       ; adres nazwy pliku
        xor     cx, cx          ; atrybuty. Zero oznacza normalny plik.
        int     21h             ; utworzenie pliku
        jc      blad            ; sprawdzamy, czy nie ma bledu.

        mov     [uchwyt], ax
        mov     bx, ax          ; zapisujemy uchwyt

        mov     ah, 40h         ; numer funkcji - zapis
                                ; BX = uchwyt do pliku
        mov     cx, 1024        ; liczba bajtow do zapisania
        mov     dx, bufor       ; adres bufora, z ktorego bierzemy bajty
        int     21h             ; zapis do pliku
        jc      blad            ; sprawdzamy, czy nie ma bledu.

        mov     ah, 3eh         ; numer funkcji - zamkniecie pliku
                                ; BX = uchwyt do pliku
        int     21h             ; zamykamy plik
        jc      blad            ; sprawdzamy, czy nie ma bledu.

   Otwarcie istniejacego pliku, odczytanie i zapisanie czegos do niego:
        mov     ah, 3dh         ; numer funkcji - otwieranie pliku
        mov     al, 00010010b   ; wylaczny dostep do odczytu i zapisu
        mov     dx, nazwa       ; adres nazwy pliku
        int     21h             ; utworzenie pliku
        jc      blad            ; sprawdzamy, czy nie ma bledu.

        mov     [uchwyt], ax
        mov     bx, ax          ; zapisujemy uchwyt

        mov     ah, 3fh         ; numer funkcji - odczyt
                                ; BX = uchwyt do pliku
        mov     cx, 1024        ; liczba bajtow do odczytania
        mov     dx, bufor       ; adres bufora, do ktorego czytamy
        int     21h             ; czytamy z pliku
        jc      blad            ; sprawdzamy, czy nie ma bledu.

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

        mov     ah, 40h         ; numer funkcji - zapis
                                ; BX = uchwyt do pliku
        mov     cx, 1024        ; liczba bajtow do zapisania
        mov     dx, bufor       ; adres bufora, z ktorego bierzemy bajty
        int     21h             ; zapis do pliku
        jc      blad            ; sprawdzamy, czy nie ma bledu.

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

        mov     ah, 3eh         ; numer funkcji - zamkniecie pliku
                                ; BX = uchwyt do pliku
        int     21h             ; zamykamy plik
        jc      blad            ; sprawdzamy, czy nie ma bledu.

   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 BX, wiec ciagle w nim jest
   uchwyt do 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 -o na_male.com -f bin na_male.asm
; fasm na_male.asm na_male.com

org 100h

start:
        mov     dx, info
        mov     ah, 9
        int     21h

        mov     ax, 3d02h       ; otworz do odczytu i zapisu,
                                ; zabron wszystkim dostepu
        mov     dx, plik        ; adres nazwy pliku
        int     21h

        jnc     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     bx, ax          ; zapisujemy uchwyt do pliku
        mov     bp, 400h        ; BP = rozmiar bufora.

czytaj:
        mov     ah, 3fh         ; funkcja czytania
                                ; BX = uchwyt
        mov     dx, bufor       ; adres bufora, dokad czytamy
        mov     cx, bp          ; kilobajt - rozmiar bufora
        int     21h             ; odczyt

        jnc     czyt_ok
        call    plik_blad       ; uruchamiamy te procedure, gdy wystapil blad

czyt_ok:
        xor     di, di          ; DI bedzie wskaznikiem do bufora.
                                ; Na poczatku go zerujemy.
        cmp     ax, cx          ; Czy liczba bajtow odczytana (AX) =
                                ; = liczba zadana (CX) ?
        jne     przy_eof        ; jesli nie, to plik sie skonczyl

zamiana:
        mov     dl, [bufor+di]  ; 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+di], dl  ; i zapisujemy w miejsce,
                                ; gdzie poprzednio byl

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

        mov     dx, ax          ; DX = liczba przeczytanych bajtow
        mov     ax, 4201h       ; idz do ... od pozycji biezacej.
                                ; aby zapisac zmienione litery,
                                ; musimy przejsc
                                ; sie w pliku o 1 kilobajt wstecz.
                                ; Do CX:DX wpisujemy odleglosc
        neg     dx              ; DX = -DX
;       dec     cx              ; CX po wyjsciu z petli jest zerem,
                                ; wiec wykonanie DEC zrobi z niego -1.
        mov     cx, 0ffffh      ; CX = -1
                                ; CX:DX = -DX = -liczba przeczytanych bajtow

                                ; BX = uchwyt
        int     21h             ; wykonujemy przeskok w pliku

        jnc     idz_ok
        call    plik_blad

idz_ok:                         ; po udanym przeskoku

        mov     dx, bufor       ; DX = adres bufora, skad bedziemy brac dane
                                ; do zapisania
                                ; BX = uchwyt
        mov     ah, 40h         ; funkcja zapisz
        mov     cx, bp          ; CX = BP = 400h = dlugosc bufora.
        int     21h             ; zapisujemy

        jmp     short czytaj    ; i idziemy czytac nowa partie danych.

przy_eof:                       ; gdy jestesmy juz przy koncu pliku.

;       xor     di, di          ; DI juz = 0 (wczesniej to zrobilismy)

        mov     bp, ax          ; BP = liczba przeczytanych znakow
        mov     cx, ax          ; CX = liczba przeczytanych znakow

zamiana2:
        mov     dl, [bufor+di]  ; 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+di], dl  ; i zapisujemy w miejsce,
                                ; gdzie poprzednio byl

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

        mov     dx, bp
;       dec     cx              ; CX po wyjsciu z petli jest zerem, wiec
                                ; wykonanie DEC zrobi z niego -1.
        mov     cx, 0ffffh      ; CX = -1
                                ; CX:DX = -DX
        mov     ax, 4201h       ; idz do ... od pozycji biezacej.
        neg     dx              ; DX = -DX.
                                ; CX:DX = -DX = -liczba przeczytanych bajtow

                                ; BX = uchwyt
        int     21h             ; wykonujemy przeskok w pliku

        jnc     idz_ok2
        call    plik_blad

idz_ok2:                        ; po udanym przeskoku

        mov     dx, bufor       ; zapiszemy do pliku reszte danych.
                                ; DX = adres bufora.
                                ; BX = uchwyt
        mov     cx, bp          ; CX = liczba bajtow uprzednio odczytanych
        mov     ah, 40h         ; funkcja zapisu do pliku
        int     21h             ; zapisujemy

        jnc     zamk            ; gdy nie ma bledu, to zamkniemy plik
        call    plik_blad

zamk:
        mov     ah, 3eh
                                ; BX = uchwyt
        int     21h             ; zamykamy nasz plik
        jnc     zamk_ok
        call    plik_blad

zamk_ok:
        mov     ax, 4c00h
        int     21h             ; wyjscie...


plik_blad:                      ; procedura wyswietla informacje o tym, ze
                                ; wystapil blad i wypisuje numer tego bledu
        push    ax
        push    bx

        mov     dx, blad_plik
        mov     bx, ax
        mov     ah, 9
        int     21h

        mov     ax, bx
        call    pl

        pop     bx
        pop     ax

        ret

pl:                     ; procedura wypisuje liczbe (4 znaki szesnastkowe)

        mov     bx, ax
        shr     ax, 12
        call    pc2

        mov     al, bh
        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 ax

        cmp     al, 9
        mov     ah, 0eh
        ja      hex
        or      al, "0"
        jmp     short pz
hex:
        add     al, "A"-10
pz:
        int     10h

        ret

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,13,"$"
input1          db "Podaj nazwe pliku do przetworzenia: $"
zla_nazwa       db 10,13,"Zla nazwa pliku.$"
blad_plik       db 10,13,"Blad operacji na pliku. Kod: $"

   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
