W cyklu o MS-DOS publikujemy informacje pozwalające efektywnie korzystać z usług tego systemu. W tym odcinku omówione zostaną wywołania funkcji uniwersalnych, związanych z obsługą plików dyskowych. Istnieją one wprawdzie we wszystkich wersjach systemu, jednak umożliwiają dostęp tylko do plików znajdujących się aktualnie wybranym katalogu.
Blok sterujący pliku (FCB - ang. File Control Block) jest 44-bajtową strukturą opisującą używany plik (Rys.5, Mikroklan 7/87). Programista ma nie tylko dostęp do FCB, ale i niemal pełną nad nim kontrolę. Zarządzanie plikami za pomocą bloków sterujących jest charakterystyczne dla funkcji uniwersalnych (przejętych z systemu CP/M). Funkcje rozszerzone (przejęte z systemu UNIX) nie korzystają z FCB, używając zamiast niego numeru identyfikacyjnego pliku. Czynności, które przy pracy z funkcjami uniwersalnymi musi wykonywać programista, w przypadku funkcji rozszerzonych wykonuje system operacyjny.
FCB składa się z dwóch części : części głównej (37 bajtów) i położonego przed nią rozszerzenia (7-bajtów). Przy opisie FCB operuje się przesunięciami liczonymi względem adresu FCB, czyli początkowego adresu części głównej. Przesunięcia bajtów tworzących rozszerzenie FCB są więc ujemne. Rozszerzenie wykorzystywane jest tylko wtedy, gdy plik ma niekonwencjonalne atrybuty, takie jak plik systemowy, czy ukryty. Jednakże zawsze musimy rezerwować miejsce na te 7 bajtów, bowiem niezależnie od tego czy rozszerzenie jest wykorzystane czy nie, informacja o tym zawarta jest w nim samym (przesunięcie -7). Wartość FFh świadczy o obecności rozszerzenia, wartość 00h - o jego braku. W blokach rozszerzonych, bajt o przesunięciu -1 zawiera bity atrybutów pliku.
Blok sterujący może być otwarty lub nie. Blok nie otwarty jest niepełny, składa się z bajtów o przesunięciu od 0 (lub -7 w przypadku bloku rozszerzonego) do 0Fh i zawiera informacje wpisywane przez programistę przez otwarciem pliku (funkcja 0Fh). Po otwarciu pliku blok sterujący zajmuje bajty aż do przesunięcia 36 (24h) włącznie. System operacyjny po załadowaniu programu do pamięci automatycznie generuje dwa nie rozszerzone i nie otwarte bloki sterujące, zajmujące po 16 bajtów - przesunięcia 5Ch ... 6Bh i 6Ch ... 7Bh względem początku PSP (Rys.4, Mikroklan 7/87). Odpowiadają one plikom, których nazwy podano za nazwą programu w poleceniu ładowania (dzieje się tak tylko wtedy, gdy nazwy te rzeczywiście wystąpiły). Taki mechanizm umożliwia łatwy dostęp do plików, których nazwy są parametrami poleceń zapamiętanych na dysku.
Po otwarciu pierwszego z dwóch plików odpowiadający mu FCB ulegnie powiększeniu (bajty o przesunięciu 5Ch ... 81h względem początku PSP) i tym samym zniszczy informacje znajdujące się w drugim FCB. Aby można było korzystać z informacji o drugim pliku, należy więc przed otwarciem pierwszego pliku przepisać drugi FCB w inne miejsce pamięci. Podobnie, przy operacji swobodnego dostępu do pierwszego pliku, obszar, w który należy wpisać numer rekordu (przesunięcie 21h ... 24h względem pierwszego FCB, czyli 7Eh ... 81h względem początku PSP), pokryje się częściowo z obszarem wybranym przez system na bufor transmisji dyskowych DTA (ang. Disk Transfer Area), o przesunięciu 80h ... 100h względem początku PSP. Przed operacją swobodnego dostępu do pierwszego pliku, należy więc zmienić położenie bufora DTA.
Zestawienie funkcji uniwersalnych
Funkcja 0Dh - zeruje napęd dysków i opróżnia wewnątrzsystemowe bufory pamięciowe plików, ale nie zamyka plików. Dlatego prawidłowe długości plików zostaną zapisane w katalogu na dysku tylko wtedy, gdy najpierw dla każdego otwartego i zmienianego pliku zostanie wywołana funkcja 10h lub 3Eh (zamknięcie pliku).
Funkcja 0Eh - wybiera aktualny napęd dysków (ang. default drive), to znaczy określa literę, którą system przyjmuje jako symbol napędu dysków, jeśli nazwa pliku zostanie podana bez symbolu napędu. Numer napędu należy podać w rejestrze DL, przy czym 0 odpowiada napędowi A:, 1 - napędowi B:, itd. Po zakończeniu realizacji funkcji w rejestrze AL zapisana jest liczba przyłączonych napędów dysków.
Funkcja 0Fh - otwiera plik wskazany przez FCB. Adres FCB należy podać w rejestrach DS (segment) i DX (przemieszczenie w ramach segmentu). Plik musi już istnieć. W poszczególnych bajtach FCB muszą być podane następujące informacje :
- -7h : FFH, gdy FCB jest rozszerzony
- -6h ... -2h : wszystkie równe 0
- -1h : atrybuty pliku (gdy pod -7h jest FFh)
- 0h : "specjalny" numer napędu (0 - napęd aktualny, 1 - napęd A, 2 - napęd B, itd.)
- 1h ... 8h : nazwa pliku lub urządzenia
- 9h ... Bh : rozszerzenie nazwy pliku (jeśli występuje).
Wynik operacji przekazywany jest do rejestru AL: 0 oznacza pomyślne otwarcie pliku, zaś FFh, że operacja nie została wykonana poprawnie. W przypadku otwarcia pliku system operacyjny zmienia następujące pola FCB :
- 00h : konkretny numer napędu
- 0Eh ... 0Fh : długość rekordu = 80h
- 10h ... 13h : wielkość pliku w bajtach
- 14h ... 15h : data ostatniej zmiany w pliku
- 16h ... 1Fh : informacje dla DOS.
Podaną w zerowym bajcie FCB liczbę 0 (napęd aktualny) system zamienia na konkretną liczbę odpowiadającą napędowi aktualnemu (1 - napęd A:, 2 - napęd B:, itd.). Ponadto otwarcie pliku powoduje wpisanie długości rekordu równej 128 (80h) bajtów. Przed dostępem do pliku można tę wielkość zmienić.
Funkcja 10h - zamyka plik o FCB wskazywanym przez rejestry DS:DX. FCB musi zawierać te same informacje, co w przypadku otwierania pliku. Wynik operacji (0 - sukces, FFh - błąd realizacji) wpisywany jest do rejestru AL. DOS porównuje dane określające plik w skorowidzu na dysku i w FCB - w przypadku ich niezgodności (świadczy to o zmianie dyskietek w napędzie) nie zamyka pliku. Jeśli nie zmieniono dyskietki i system odnalazł plik, aktualizowana jest odpowiednia pozycja w katalogu dysku.
Funkcja 11h - poszukuje pierwszej nazwy pliku, która pasuje do podanego wzorca. Rejestry DS:DX wskazują na FCB, w którym w odpowiednich polach należy określić nazwę pliku i jej rozszerzenie. Można przy tym używać symboli wieloznacznych (ang. wild card). Gwiazdka zastępuje dowolny ciąg znaków, a znak zapytania dowolny pojedynczy znak. W miejsce podanej nazwy wieloznacznej wpisywana jest odnaleziona, pasująca do niej nazwa pliku, zaś w rejestrze AL umieszczona jest wartość 0 (jeśli nazwa nie zostanie odnaleziona system wpisuje do rejestru AL wartość FFh).
W zarezerwowanych dla siebie polach FCB system umieszcza informacje, które może wykorzystać w przypadku wywołania funkcji 12h. Informacje te są jednak niszczone przez każde inne wywołanie funkcji korzystającej z FCB. Gdy wywołania funkcji 11h i 12h są rozdzielone wywołaniami innych funkcji, należy przepisać zawartość FCB w inne miejsce pamięci.
W przypadku rozszerzonego FCB można określić atrybuty poszukiwanego pliku. Poszukiwania przeprowadzane są wtedy zgodnie z następującymi rezultatami : jeśli bajt atrybutów zawiera kombinację bitów oznaczających plik ukryty, systemowy lub będący katalogiem, uwzględniane są pliki zwykłe o podanych atrybutach. Jeśli bajt atrybutów określa etykietę dysku (ang. volume label), poszukiwana jest tylko etykieta. W wersjach systemu wcześniejszych od 2.0 nie wolno poszukiwać pozycji będących skorowidzami lub etykietami. We wszystkich wersjach systemu bity atrybutów oznaczające plik archiwalny lub plik tylko do odczytu nie mogą stanowić kryterium poszukiwań.
Funkcja 12h - poszukuje następnej nazwy pliku zgodnej ze wzorcem podanym wcześniej przy wywołaniu funkcji 11h. Przy poszukiwaniu należy najpierw wywołać funkcję 11h, a następnie wielokrotnie wywoływać funkcję 12h.
Funkcja 13h - usuwa z katalogu plik określony w FCB wskazywanym przez DS:DX. Po powrocie z wywołania funkcji wynik operacji znajduje się w rejestrze AL : 0 oznacza, że plik o podanej nazwie istniał i został usunięty, zaś FFh - że nie było takiego pliku.
Funkcja 14h - odczytuje w sposób sekwencyjny rekord z pliku. Przed wywołaniem rejestry DS:DX wskazują FCB, w którym oprócz nazwy i rozszerzenia pliku musi być wpisany aktualny numer bloku (pozycje CH i DH) oraz aktualny numer rekordu w ramach bloku (pozycja 20h). Wpisany musi też być rozmiar rekordu (porównaj opis funkcji 0Fh). System po każdym wywołaniu funkcji 14h uaktualnia w FCB numer bloku i numer rekordu w bloku tak, by wskazywały one na następny rekord w pliku. Kolejne wywołania funkcji 14h będą więc powodowały odczytywanie kolejnych rekordów pliku, czyli dostęp sekwencyjny.
W przypadku pomyślnego odczytu system umieszcza w rejestrze AL wartość 0 oraz wpisuje odczytane dane do bufora DTA (porównaj opis funkcji 1Ah). Inna wartość w rejestrze AL świadczy, że operacja nie została wykonana:
- system napotkał koniec pliku; nie ma już więcej danych do odczytu,
- dane mogłyby być odczytane, ale obszar DTA jest zbyt mały (wykracza poza segment),
- po wprowadzeniu pewnej liczby bajtów wykryty został znacznik końca pliku; pozostała część bufora wypełniona jest zerami.
Funkcja 15h - zapisuje rekord do pliku w sposób sekwencyjny. Jest to odwrotność funkcji 14h. Parametry wejściowe powinny być identyczne jak dla funkcji 14h, a ponadto w DTA muszą się znajdować dane przeznaczone do zapisania na dysku. Numer bloku i rekordu w bloku też jest tu uaktualniany. Liczba przekazywana w rejestrze AL po zakończeniu operacji oznacza : 0 - zapis bez błędu, 1 - brak miejsca na dysku, 2 - za mały obszar DTA.
Funkcja 15h zapisuje rekord tylko logicznie. System buforuje bowiem dane przesyłane na dysk i dokonuje fizycznej transmisji dopiero w chwili zapełnienia wewnętrznego bufora o wielkości odpowiadającej sektorowi na dysku. W przypadku nienormalnego zakończenia programu przed zamknięciem pliku można utracić dane - wówczas nie tylko nie będą one uwzględnione w długości pliku zapisanej w katalogu, ale na dodatek danych wchodzących w skład ostatnio tworzonego sektora może w ogóle nie być na dysku.
Uwaga : system nie pozwoli wprawdzie bez użycia rozszerzonego FCB skasować pliku z atrybutem "tylko do odczytu", nie uchroni jednak pliku przed zapisem do niego rekordu przy użyciu nierozszerzonego FCB.
Funkcja 16h - tworzy i otwiera plik. Najpierw sprawdzany jest katalog. Jeśli plik o nazwie i rozszerzeniu podanych w FCB wskazywanym przez rejestry DS:DX już istnieje, to jest on otwierany, jeśli zaś jeszcze nie istnieje, to znajdowana jest wolna pozycja w katalogu, plik jest tworzony i otwierany. W obu przypadkach długość pliku w FCB jest ustawiana na 0. Jest to równoznaczne z utratą wszelkich zapisanych dotychczas w pliku informacji. Dlatego funkcja ta wywoływana jest na ogół przed zapisem danych do nowego pliku. Przed odczytem danych z istniejącego pliku używa się funkcji 0Fh, która ustawia rzeczywistą długość pliku w FCB.
Funkcja 17h - zmienia nazwę pliku. Dotychczasowe parametry pliku powinny być podane w FCB wskazywanym przez DS:DX (numer napędu, nazwa i rozszerzenie). Nową nazwę należy wpisać począwszy od adresu o przemieszczeniu 10h względem początku FCB (tam gdzie system zwykle wpisuje wielkość pliku). Po powrocie z wywołania liczba w rejestrze AL oznacza : 0 - operacja wykonana prawidłowo, FFh - nie znaleziono pliku do przemianowania lub użyta nowa nazwa już istnieje. Jeśli nowa nazwa zawiera symbole wieloznaczne (* lub ?), to w ich miejsce podstawiane są odpowiednie znaki ze starej nazwy.
Funkcja 18h - (podobnie jak funkcje od 1Dh do 20h) jest używana przez DOS dla jego własnych, wewnętrznych potrzeb (oficjalnie nie istnieje). Chociaż znane są pewne cechy tych funkcji, to ich wykorzystywanie jest ryzykowne - w przyszłych wersjach systemu funkcji tych może nie być.
Funkcja 19h - podaje numer napędu będącego w danej chwili napędem aktualnym. Numer podawany jest w rejestrze AL : 0 oznacza napęd A:, 1 - napęd B:, itd.
Funkcja 1Ah - ustala adres bufora transmisji dyskowych (DTA). Adres ten podaje się w rejestrach DS:DX. System po inicjalizacji przeznacza na ten bufor 128 bajtów pamięci począwszy od adresu o przemieszczeniu 80h względem początku PSP; za pomocą funkcji 1Ah można jednak zmieniać adres bufora.
Funkcja 1Bh - podaje informacje o sposobie organizacji informacji zapisanych na dyskietce znajdujących się w aktualnym napędzie (funkcja 1Ch podaje te same informacje dla dowolnego napędu). Po powrocie z wywołania funkcji dostępne są następujące informacje :
- DS:BX - wskaźnik do bajtu identyfikującego format zapisu (bajtu identyfikacyjnego FAT - tabeli alokacji plików, która zostanie omówiona w następnym odcinku),
- CX - wielkość sektora (w bajtach, np. 512),
- AL - liczba sektorów przypadająca na jednostkę alokacji czyli grono (ang. cluster); dla dyskietek jednostronnych wynosi ona 1, dla dwustronnych - 2, dla 10-megabajtowego sztywnego dysku - 8,
- DX - liczba jednostek alokacji na całym dysku.
Uwaga : wywołanie tej funkcji powoduje zmianę wartości rejestru DS (gdyż bajt identyfikacyjny FAT na pewno nie znajduje się w realizowanym programie). Dlatego przed jej wywołaniem na ogół należy wykonać rozkaz PUSH DS, a po powrocie - POP DS.
Funkcja 1Ch - podaje te same informacje, co funkcja 1Bh, ale dla napędu o numerze specjalnym podanym w DL (0 oznacza napęd aktualny, 1 - napęd A:, 2 - napęd B:, itd.).
Funkcja 21h - czyta wskazany rekord z pliku (dostęp swobodny). Plik określany jest w FCB wskazywanym przez DS:DX, zaś numer rekordu należy umieścić w czterobajtowym polu o przemieszczeniu 21h względem początku FCB (numer rekordu zapisuje się w postaci liczby 32-bitowej), przy czym bajty zapisywane są w kolejności od najmniej do najbardziej znaczącego. Po powrocie z wywołania funkcji numer ten nie ulega zmianie, więc przed każdym wywołaniem funkcji 21h należy nadawać mu odpowiednią wartość. O wyniku operacji świadczy liczba w rejestrze AL, która ma takie samo znaczenie, jak przy odczycie sekwencyjnym (patrz opis funkcji 14h). Dane wpisywane są do bufora DTA.
Funkcja 22h - zapisuje wskazany rekord do pliku (dostęp swobodny). Wymaga podania takich samych informacji jak funkcja 21h. O wyniku operacji świadczy zawartość rejestru AL, która ma takie samo znaczenie jak w przypadku funkcji 15h. Dane pobierane są z bufora DTA. Podobnie jak przy zapisie sekwencyjnym (funkcja 15h), system nie zabezpiecza przed zapisem pliku tylko do odczytu otworzonego z użyciem nierozszerzonego FCB.
Funkcja 23h - podaje wielkość pliku określonego w FCB wskazywanym przez rejestry DS:DX. Przed wywołaniem plik ten nie może być otwarty, zaś w FCB musi być wpisana wielkość rekordu (w bajtach) i nazwa. Można używać nazw wieloznacznych i rozszerzonego FCB - znaleziony będzie wtedy pierwszy plik zgodny ze wzorcem i atrybutami, podobnie jak w funkcji 11h. Jeśli operacja zostanie prawidłowo wykonana, rejestr AL, będzie zawierał 0, zaś do FCB zostanie wpisana wielkość pliku (wyrażona w rekordach). Jeśli podany zbiór nie zostanie znaleziony, a AL umieszczona będzie wartość FFh.
Funkcja 24h - wpisuje do FCB numer rekordu przy dostępie swobodnym obliczony na podstawie numeru bloku i numeru rekordu w ramach bloku stosowanych przy dostępie sekwencyjnym. Przed wywołaniem funkcji rejestry DS:DX muszą wskazywać na FCB otwartego pliku.
Działanie omówionych funkcji ilustrują dwa krótkie programy. Pierwszy usuwa wszystkie pliki typu .BAK, a więc wykonuje to samo, co można by uzyskać wydając polecenie DEL *.BAK. Drugi program nadaje się do praktycznego wykorzystania : przekształca plik tekstowy uzyskany w czasie pracy z procesorem tekstu WordStar w zwykły plik ASCII i pozwala obejrzeć go (jak również dowolny inny plik ASCII) na ekranie.
NAME DELETE_BAK
; Program przeszukuje aktualny skorowidz i usuwa wszystkie
; pliki z rozszerzeniem .BAK
; funkcje DOS
OUT_STRING EQU 09h ; wyświetlenie ciągu znaków
SEARCH_FIRST EQU 11h ; poszukiwanie pierwszego pliku
SEARCH_NEXT EQU 12h ; poszukiwanie następnego pliku
DELETE EQU 13h ; usunięcie pliku
SET_DTA EQU 1Ah ; inicjacja nowego bufora dyskowego
TERMINATE EQU 4Ch ; zakończenie programu
DOS EQU 21h ; przerwanie wywołań funkcji
; kody znaków
CR EQU 0Dh ; return (enter)
LF EQU 0Ah ; nowy wiersz
STACK SEGMENT STACK
DW 20 DUP (?)
STACK ENDS
CODE SEGMENT 'CODE'
ASSUME CS:CODE, DS:CODE, ES:CODE, SS:STACK
DELETED DB '.BAK zostal usuniety',CR,LF,'$'
NOT_DELETED DB '.BAK nie został usuniety',CR,LF,'$'
DTA DB 128 DUP (?) ; nowy zbiór dyskowy (DTA)
EFCB DB 7 DUP (0) ; rozszerzone FCB
FCB DB 0,'????????BAK' ; nieotwarty FCB ze wzorcem
DB 25 DUP (?) ; pozostała część FCB
START:
MOV AX, CODE
MOV DS, AX ; inicjacja segmentu danych
MOV ES, AX ; inicjacja segmentu dodatkowego
MOV DX, OFFSET DTA
MOV AH, SET_DTA
INT DOS ; inicjacja nowego bufora dyskowego
MOV AH, SEARCH_FIRST ; poszukiwanie pierwszego pliku .BAK
LOOP:
MOV DX, OFFSET FCB
INT DOS
OR AL, AL ; czy znaleziono plik
JNZ FINISHED
CALL ERASE ; usunięcie pliku
MOV AH, SEARCH_NEXT ; poszukiwanie następnego pliku
JMP LOOP
FINISHED:
MOV AH, TERMINATE
INT DOS ; zakończenie programu
ERASE PROC
MOV DX, OFFSET DTA
MOV AH, DELETE
INT DOS ; usunięcie pliku
PUSH AX ; ochrona kodu błędu
MOV AL, ' '
MOV DI, OFFSET DTA + 1
MOV CX, 9
MOV DX, DI ; adres wyprowadzania
REPNE SCASB ; poszuk. pierw. spacji w nazwie pliku
MOV BYTE PTR [DI-1],'$' ; ogranicznik wyświetl. ciągu znaków
MOV AH, OUT_STRING
INT DOS ; wyświetlenie nazwy usuwanego pliku
POP AX ; odzyskanie kodu błędu
OR AL, AL ; czy plik został usunięty?
JZ SUCCES
MOV DX, OFFSET NOT_DELETED ; plik nie został usunięty
JMP INFORM
SUCCES:
MOV DX, OFFSET DELETED
INFORM:
MOV AH, OUT_STRING
INT DOS ; wyświetlenie komunikatu
RET
ERASE ENDP
CODE ENDS
END START
--- ^ --- ^ ----
NAME WS_ASCII
; program czyta plik tekstowy WordStar-a, wyświetla go na ekranie
; w postaci znaków ASCII i ewentualnie zapisuje w postaci ASCII
; w nowym pliku na dysku, wywoływany jest poleceniem :
; wsas plik1 albo wsas plik1 plik2
; gdzie plik1 oznacza plik ZRÓDŁOWY WordStar-a, a plik2 ewentualny
; DOCELOWY plik ASCII. Plik1 może też być zwykłym plikiem ASCII -
; wówczas program pozwala na obejrzenie go i ewentualne skopiowanie
; do pliku2.
; funkcje DOS:
CHAR_OUT EQU 02h ; wysłanie znaku na ekran
CLOSE EQU 10h ; zamknięcie pliku
CREATE EQU 16h ; utworzenie i otwarcie pliku
OPEN EQU 0Fh ; otwarcie pliku
READ EQU 14h ; odczyt sekwencyjny pliku
WRITE EQU 15h ; zapis sekwencyjny pliku
TERMINATE EQU 4Ch ; zakończenie programu
SET_DTA EQU 1Ah ; utworzenie bufora dyskowego
STRING_OUT EQU 09h ; wyświetlenie ciągu znaków
DOS EQU 21h ; przerwanie wywołań funkcji
CONTROL_Z EQU 1Ah ; znak końca pliku
; kody błędów
EOF EQU 01h ; napotkanie końca pliku
DTA_SMALL EQU 02h ; za mały bufor DTA
PARTIAL_REC EQU 03h ; ostatni odczytany rekord nie jest pełny
; przesunięcia w FCB lub PSP
CURRENT_RECORD EQU 20h ; numer rekordu przy dostępie sekwencyjnym
ORYGINAL_FCB1 EQU 5Ch ; przesunięcie FCB pliku1 w PSP
ORYGINAL_FCB2 EQU 6Ch ; przesunięcie FCB pliku2 w PSP
STACK SEGMENT STACK
DB 20 DUP (?)
STACK ENDS
DATA SEGMENT 'DATA'
BUFFER DB 128 DUP (?) ; bufor dyskowy
EFCB1 DB 7 DUP (0) ; rozszerzenie FCB pliku1
FCB1 DB 37 DUP (?) ; FCB pliku1
EFCB2 DB 7 DUP (0) ; rozszerzenie FCB pliku2
FCB2 DB 37 DUP (?) ; FCB pliku2
DATA ENDS
CODE SEGMENT 'CODE'
ASSUME CS:CODE, DS:DATA, ES:DATA, SS:STACK
START:
MOV AX, DATA
MOV ES, AX ; inicjacja segmentu dodatkowego
MOV DI, OFFSET FCB1 ; adres docelowy FCB pliku
MOV SI, ORIGINAL_FCB1 ; początek systemowego FCB pliku1
MOV CX, 12 ; długość nieotwartego FCB
REPNE MOVSB ; przepisanie nieotwartego FCB pliku1
MOV BYTE PTR FCB1[CURRENT_RECORD], 0
MOV DI, OFFSET FCB2 ; adres docelowy FCB pliku2
MOV SI, ORIGINAL_FCB2 ; początek systemowego FCB PLIKU2
MOV CX, 12
REPNE MOVSB ; przepisanie nieotwartego FCB pliku2
MOV BYTE PTR FCB2[CURRENT_RECORD], 0
; inicjacja numeru rekordu przy
; odczycie sekwencyjnym
MOV AX, ES
MOV DS, AX ; inicjacja segmentu danych
MOV AL, BYTE PTR FCB2[1] ; pobranie pierwszego znaku z nazwy pliku2
CMP AL, ' '
JZ CONT1 ; skok gdy nie podano nazwy pliku2
; podano nazwę pliku2
MOV DX, OFFSET FCB2
MOV AH, CREATE
INT DOS ; utworzenie i otwarcie pliku2
CONT1:
MOV DX, OFFSET BUFFER ; adres bufora dyskowego
MOV AH, SET_DTA
INT DOS ; wybranie nowego bufora dyskowego
MOV DX, OFFSET FCB1
MOV AH, OPEN
INT DOS ; otwarcie pliku
LOOP:
MOV DX, OFFSET FCB1
MOV AH, READ
INT DOS ; sekwencyjny odczyt rekordu z pliku1
PUSH AX ; chroń na stosie wynik operacji w AL
MOV DI, OFFSET BUFFER
MOV CX, LENGTH BUFFER
MAKEASC:
MOV AL, [DI]
AND AL, 7Fh
MOV [DI], AL ; zamień znaki w buforze na znaki ASCII,
INC DI ; zerując w każdym znaku
LOOP MAKEASC ; najstarszy bit
MOV AL, BYTE PTR FCB2[1] ; pobranie pierwszego znaku z nazwy pliku
CMP AL, ' '
JZ CONT2 ; skok gdy nie podano nazwy pliku2
; podano nazwę pliku 2
MOV DX, OFFSET FCB2
MOV AH, WRITE
INT DOS ; zapisanie rekordu ASCII do pliku2
CONT2:
MOV DX, OFFSET BUFFER
MOV CX, LENGHT BUFFER
POP AX ; AL <- wynik operacji odczytu
CMP AL, EOF ; czy koniec pliku ?
JZ FINISHED
CMP AL, DATA_SMALL ; czy za mały bufor dyskowy ?
JZ FINISHED ;
CMP AL, PARTIAL_REC ; czy odczyt pełnego rekordu ?
JNZ OUTPUT
; odczyt niepełnego rekordu
MOV DI, DX
MOV AL, CONTROL_Z
REPNE SCASB ; poszukiwanie znaku końca pliku
MOV AX, LENGHT BUFFER-1
SUB AX, CX ; obliczenie długości do znaku końca pliku
MOV CX, AX
CALL OUT_STRING ; wyświetlenie niepełnego rekordu
JMP FINISHED
OUTPUT:
CALL OUT_STRING ; wyświetlenie rekordu
JMP LOOP ; obsługa następnego rekordu
FINISHED:
MOV DX, OFFSET FCB1
MOV AH, CLOSE
INT DOS ; zamknięcie pliku1
MOV AL,BYTE PTR FCB2[1] ; pobranie pierwszego znaku z nazwy pliku2
CMP AL, ' '
JZ CONT3 ; skok gdy nie podano nazwy pliku2
; podano nazwę pliku 2
MOV DX, OFFSET FCB2
MOV AH, CLOSE
INT DOS
CONT3:
MOV TERMINATE
INT DOS ; zakończenie programu
OUT_STRING PROC
CMP CX, 0 ; czy długość ciągu znaków = 0?
JZ RETURN
MOV DI, DX
CONT4:
MOV AL, '$'
REPNE SCASB ; poszukiwanie znaku '$'
CMP CX, 0 ; czy nie znaleziono ?
JNZ OUT
MOV BL, [DI-1] ; zastąpienie ost. znaku w buf, przez '$'
MOV BYTE PTR [DI-1], '$'
OUT:
MOV AH, STRING_OUT
INT DOS ; wyświetlenie zawartości bufora
CMP CX, 0 ; czy wyświetlono cały bufor
JZ LAST
MOV DL, '$'
MOV AH, CHAR_OUT
INT DOS ; wyświetlenie znaku '$'
MOV DX, DI ; ustawienie adresu pocz. nast. ciągu znaków
JMP CONT4
LAST:
MOV DL, AL
MOV AH, CHAR_OUT
INT DOS ; wyświetlenie ostatniego znaku
RETURN:
RET
OUT_STRING ENDP
CODE ENDS
END START
Zbigniew Pojmański, Mikroklan, październik 1987r.