[<>] ~~TOC wide~~ ====== II. Работа с файлами на дискете ====== \\ Оставьте трудиться напрасно, стараясь извлечь \\ из одного разума всю мудрость; спрашивайте природу, \\ она хранит все истины и на вопросы ваши будет \\ отвечать вам непременно и удовлетворительно. —//Р.Бэкон// {{anchor:n21}} ===== II.1. Контрольный Буфер Файла ===== Для работы с файлами Вам, безусловно, пригодится информация о Контрольном Буфере Файла ("File Control Block", FCB). FCB — это такое место в оперативной памяти компьютера (всего 25h=37 байтов), где хранится вся информация, необходимая [[msx:dos:|]] для работы с //одним файлом//, находящимся на диске. В буфере управления файлом (он же — FCB) есть все: от номера дисковода и имени файла до даты и времени его рождения. В некоторых случаях Контрольным Буфером Файла называют и неполную информацию о файле — несколько первых байтов... Предположим, что Вы программируете на языке [[msx:basic:|]]. Тогда Вы можете использовать в своих программах функцию VARPTR(#n) , где: * VARPTR ( "VARiable PoinTeR" — "указатель переменной" ) — служебное слово; * ''n'' — арифметическое выражение, целая часть значения которого определяет номер FCB (от 0 до значения выражения, стоящего в правой части оператора ''MAXFILES='' ). Эта функция возвращает //адрес// X байта, начиная с которого расположен в памяти FCBn. Взгляните на схему расположения информации в FCBn : Управляющая информация ────╮ Данные ┌───┬───┬─▼─┬───┬───┬───┬───┐ │▧▧▧│▧▧▧│∙∙∙│▧▧▧│███│∙∙∙│███│ └───┴───┴───┴───┴───┴───┴───┘ Адреса ——▶ X X+1 X+8 X+9 X+264 ▲ │ Байт, адрес которого возвращает функция VARPTR(#n) При открытии файла на языке ассемблера Вы должны создать FCB, в котором должна быть записана следующая информация: ┌─────┬─────────────────────────────────────────┐ │Номер│ Содержимое байта │ │байта│ │ ├─────┼─────────────────────────────────────────┤ │ X │ Тип файла: │ │ │ 1 — OPEN FOR INPUT │ │ │ 2 — OPEN FOR OUTPUT │ │ │ или OPEN FOR APPEND (для диска) │ │ │ 4 — файл прямого доступа (для диска) │ │ │ или файл последовательного доступа, │ │ │ открытый и для чтения, и для записи. │ │ │ 8 — OPEN FOR APPEND │ │ │ (для устройства MEM:) │ ├─────┼─────────────────────────────────────────┤ ╭─────────── X+1 │ Ссылка на область памяти, в которой │ │ │ X+2 │ хранится спецификация файла │ │ ├─────┼─────────────────────────────────────────┤ │ │ X+3 │ ? │ │ ├─────┼─────────────────────────────────────────┤ │ │ X+4 │ Номер текущего устройства: │ │ │ │ 0 — активное устройство по умолчанию│ │ │ │ 1 — дисковод A: │ │ │ │ 2 — дисковод B: , и т.д. │ │ │ │ FCh — устройство GRP: │ │ │ │ FDh — устройство CRT: │ │ │ │ FEh — устройство LPT: │ │ │ │ FFh — устройство CAS: │ │ │ │ Дополнительные устройства: │ │ │ │ D4h — устройство COM: │ │ │ │ (локальная сеть MSX-1) │ │ │ │ C0h — устройство MEM: │ │ ├─────┼─────────────────────────────────────────┤ │ │ X+5 │ ? │ │ ├─────┼─────────────────────────────────────────┤ │ │ ∙∙∙ │ ? │ │ ├─────┼─────────────────────────────────────────┤ │ │ X+9 │ Содержимое записи файла (256 байт) │ │ └─────┴─────────────────────────────────────────┘ │ │ Спецификация файла │ ┌─────┬────────────────────────────────────────────────────────┐ │ │Номер│ Содержимое байта │ │ │байта│ │ │ ├─────┼────────────────────────────────────────────────────────┤ ╰──▶ +00 │ Номер дисковода (0 — по умолчанию, 1 — A:) │ │ +01 │ Имя файла │ │ +09 │ Расширение имени файла │ ├─────┼────────────────────────────────────────────────────────┤ │ +12 │ Номер текущего блока (или записи) │ │ +14 │ Текущий размер записи (по умолчанию равен 128) │ │ +16 │ Размер файла в байтах │ │ +20 │ Дата изменения файла │ │ +22 │ Время изменения файла │ ├─────┼────────────────────────────────────────────────────────┤ │ +24 │ Идентификатор устройства ("Device ID", см. табл. ниже) │ │ +25 │ Размещение директории ("Directory location") │ │ +26 │ Первый кластер файла │ │ +28 │ Последний кластер файла │ │ +30 │ Последний доступный кластер (относительно начала файла)│ ├─────┼────────────────────────────────────────────────────────┤ │ +32 │ Номер текущей записи файла последовательного доступа │ ├─────┼────────────────────────────────────────────────────────┤ │ +33 │ Номер случайной записи для файла прямого доступа │ │ +34 │ (байты 33,34,35 — если размер записи больше 63 │ │ +35 │ байты 33,34,35,36 — если размер записи меньше 64) │ │ +36 │ │ └─────┴────────────────────────────────────────────────────────┘ Следует отметить, что в системе [[msx:basic:|]] значимыми являются все байты FCB, а в системе [[msx:dos:|]] при открытии файла используется только спецификация файла. ^ Device ID ^ Устройство ^ |040h|Диск| |0FFh|CON (консоль)| |0FBh|PRN (принтер)| |0FCh|LST (принтер)| |0FEh|AUX (вспомогательное)| |0FDh|NUL (нулевое)| {{anchor:n22}} ===== II.2. Примеры использования точек BDOS, относящихся к работе с диском ===== \\ Примеры, немногочисленные, лаконичные, приведенные \\ к месту, сообщают рассуждению блеск, глубину \\ и убедительность, но оно становится невразумительным, \\ если примеров и подробностей чересчур много. —//Л.К.Вовенарг. Размышления и максимы// Предложенные ниже примеры позволят Вам подробнее разобраться в использовании входных точек системы [[msx:bdos|BDOS]], но для этого вначале необходимо хорошенько ознакомиться со структурой Контрольного Буфера Файла (FCB) и таблицей спецификации файла. __//Пример 1.//__ Считывание содержимого сектора. LD C,1Ah ;Установка нового адреса передачи LD DE,500h ;В регистре DE - адрес передачи CALL 5 LD DE,1 ;Чтение H секторов начиная с сектора LD H,3 ;с номером 01, находящимся в регистре DE LD L,0 ;В регистре L - номер дисковода LD C,2Fh ;Функция чтения JP 5 __//Пример 2.//__ Программа, позволяющая пользователю, находящемуся в операционной системе, удалить файл, находящийся на диске. BDOS EQU 5 ;Точка BDOS для MSX-DOS. LD DE,5Ch ;Адрес FCB LD C,13h ;Функция BDOS, которая позволяет удалять CALL BDOS ;файл, определяемый FCB, начальный адрес OR A ;которого находитя в DE (DE = 005Ch). RET Z ;Если Файл на диске отсутствует, то выдаем LD DE,MESS ;сообщение об этом. LD C,9 ;Функция 9 выводит на экран строку символов, JP 5 ;пока не встретит символ "$". MESS: DEFM "File not found$" __//Пример 3.//__ Программа, позволяющая из операционной системы переименовать файл, находящийся на дискете. BDOS EQU 5 ;Точка BDOS для MSX-DOS. LD DE,5Ch ; По этому адресу в операционной системе хранятся: ; номер устройства (0-текущий дисковод); ; имя файла и его расширение. ; Всего 12 байтов. Еще через 4 байта хранится то же самое для имени ; второго файла в командной строке. Таким образом, в DE+16 хранится ; первый байт блока FCB второго файла. LD C,17h ;Функция BDOS для переименования. CALL BDOS ; OR A ;Если функция выполнилась, то - выход, RET Z ;иначе печать сообщения о том, что файл LD DE,MESS ;отсутствует. LD C,9 ; JP 5 MESS: DEFM "File not found$" __//Пример 4.//__ Программа, позволяющая создать файл на диске с данными, которые требуется сохранить (''OPEN.DBS''–имя нового файла). BDOS EQU 5 ;Точка BDOS для MSX-DOS. LD DE,File ;Функция BDOS, которая позволяет открыть LD C,16h ;новый файл на диске. Старый файл с этим CALL BDOS ;именем уничтожается. LD HL,6 ;Пусть текущий размер записи будет равен LD (File+14),HL ; 6 (запишем только 6 байтов). LD HL,0 ;Номер записи для файла последовательного LD (File+33),HL ;доступа устанавливаем равным нулю. LD (File+35),HL ;Тот же номер записи - и для файла прямого ;доступа. Все 4 байта заполняем 00h. LD DE,Data ;Установка адреса передачи ("откуда"). LD C,1Ah CALL BDOS LD DE,File ;Осуществляем одну запись размером в 6 байт. LD HL,1 LD C,26h ;Функция BDOS для посылки записи в файл. CALL BDOS LD DE,File ;При закрытии файла на диск дозаписывается LD C,10h ;все до последнего байта данных, временно JP BDOS ;расположившихся в оперативной памяти (в ;буфере файла). Эта операция необходима ;всегда при записи на диск. File: NOP DEFM "OPEN DBS" ;Имя нового файла. DEFS 25 Data: DEFB 0Ah,0Bh,0Ch,0Dh,0Eh,0Fh; Данные. __//Пример 5.//__ Программа позволяет выводить каталог диска на экран. LD DE,500h ; Установка адреса передачи LD C,1Ah CALL BDOS LD C,11h ; Поиск первого появления файлов LD DE,File ; В имени файла используется специальный START: CALL 5 ; символ "?" OR A RET NZ CALL Consol ; Если файл еще есть, то выводим имя. LD C,12h ; Поиск следующего появления файлов. JR START ; Когда поиск прекратится, ; подпрограмма завершается Consol:LD HL,0A0Dh ; Перевод строки в конце слова LD (050Ch),HL LD A,24h ; Символ окончания строки "$" LD (050Eh),A LD DE,0501h ; Первый байт - номер дисковода: его LD C,9 ; не стоит выводить. JP 5 File: DEFB 0,"???????????" ; Шаблон поиска __//Пример 6.//__ Программа, позволяющая просмотреть содержание текстового файла, находящегося на диске. BDOS EQU 5 LD HL,5Ch ;Пересылка номера устройства (драйвера) LD DE,Konec ;и имени файла в отведенную область для LD BC,0Ch ;дальнейшей работы с блоком FCB LDIR LD DE,Konec ;Открытие файла LD C,0Fh CALL BDOS LD DE,Adres ;Установление адреса передачи для загрузки LD C,1Ah ;файла с диска CALL BDOS LD HL,(Konec+16) ;Устанавливаем размер записи для считывания LD (Konec+14),HL ;равный объему файла. LD HL,0 ;Номер записи для файла прямого и последо﹣ LD (Konec+33),HL ;вательного доступа обнуляем, т. е. чита﹣ LD (Konec+35),HL ;ем файл с самого начала LD HL,1 ;Производим считывание одной записи =размеру LD DE,Konec ;файла по адресу передачи LD C,27h CALL BDOS LD HL,Adres ;┌─────────────────────────────────────┐ LABL_1:LD A,(HL) ;│Программа распечатки ASCII кодов. │ CP 1Ah ;│Вывод символов из текстового файла │ JP Z,LABL_2 ;│будет осуществляться до тех пор,пока │ INC HL ;│не встретиться последний символ │ RST 30h ;│файла с кодом: 1Аh. Этот код означает│ DEFB 0 ;│конец любого текстового файла. │ DEFW 0A2h ;│(по стандарту ASCII) │ JP LABL_1 ;│ │ LABL_2:RST 0 ;│Выход в MSX-DOS (рестарт). │ ;└─────────────────────────────────────┘ Konec: DEFS 38 ;Обнуление 38 байтов для блока FCB Adres: DEFS 25 ;Адрес передачи __//Пример 7.//__ Программа, позволяющая копировать файлы. FILEIN EQU 5Ch ;Системные адреса хранения FILEOUT EQU 6Ch ;слов командной строки BDOS EQU 5 ;Адрес вызова BDOS ;Некоторые макро-определения: READ MACRO LD HL,4000h ;Копирование по 16 Kбайт LD DE,FIN LD C,27h ;Функция считывания с диска CALL BDOS LD (VAL),HL RET ENDM WRITE MACRO LD DE,FOUT LD C,26h ;Функция записи на диск LD HL,(VAL) CALL BDOS RET ENDM CLOSE MACRO LD DE,FOUT LD C,10h ;Функция закрытия файла CALL BDOS RET ENDM ADRES MACRO LD DE,800h LD C,1Ah ;Установка адреса передачи CALL BDOS RET ENDM ; ——————————————————————— ; Начало программы START: LD A,0C9h ;Для скоростной работы дисковода LD (38h),A ;прерывания по таймеру отключили DI LD HL,FILEIN LD DE,FIN ;Первое слово командной строки - LD BC,0Ch ;и есть первый файл. LDIR LD HL,FILEOUT+1 LD A,20h CP (HL) JR NZ,OTHER EX DE,HL LD HL,FILEIN+1 LD BC,0Bh ;Если второго слова нет, то LDIR ;имя файла(2) =: имя файла(1) OTHER: LD HL,FILEOUT LD DE,FOUT LD BC,0Ch LDIR EI LD DE,FIN LD C,0Fh ;Открытие для считывания файла(1) CALL BDOS RЕТ NZ LD DE,FOUT LD C,16h ;Открытие для записи файла(2) CALL BDOS LD HL,0 LD (FIN+33),HL LD (FIN+35),HL ;Считываем и записываем с нулевой LD (FOUT+33),HL ;записи LD (FOUT+35),HL INC HL LD (FIN+14),HL LD (FOUT+14),HL ;Размер записей = 1 SNOWA: CALL LDIRADRES CALL DISKIN LD A,H ;Пока на выходе при считывании в HL OR L ;не будет 0, записываем количество JR Z,ENDCOPY ;считанных записей, иначе закрываем CALL LDIRADRES ;файл (2) CALL DISKOUT JR SNOWA ENDCOPY:CALL DISKCLOSE LD DE,STROKA ;Сообщение о выполнении LD C,9h CALL BDOS LD A,0C3h ;Воостановление прерываний по таймеру LD (38h),A ;и выход в систему RST 0 ;Подпрограммы работы с дисководом, FCB, сообщения, переменные. DISKIN: READ DISKOUT: WRITE DISKCLOSE: CLOSE LDIRADRES: ADRES STROKA: DEFB 0Dh,0Ah DEFM "Copy complete$" VAL: DEFS 2 FIN: DEFS 40 FOUT: DEFS 40 __//Пример 8.//__ ; ; ┌───────────────────────────────────────────────────┐ ; │ Программа, позволяющая защитить диск от команд: │ ; │ DIR, COPY, DEL и т.д. │ ; │ Т.е. ото всех команд ОС, кроме команды FORMAT. │ ; └───────────────────────────────────────────────────┘ ; .z80 ; StrPar EQU 82h ; По этому адресу расположена строка ; параметров Bdos MACRO asx ; Макрос вызова подпрограмм BDOS ld c,asx Call 5 ENDM ; Устaновка нового адреса передачи для считывания каталога диска ld de,NewDMA Bdos 1Ah ; Поиск имени дисковода и обработка результатов поиска ld hl,StrPar ld b,':' Call SearCHR cp 255 ; Обработка ошибки jr z,ErrStr dec hl ld a,(hl) ; Обработка имени дисковода and 0DFh sub 'A' ; Получаем информацию о диске (заметьте, что в BDOS #1Bh имя драйвера ; "А" эквивалентно 01h) push af ld e,a inc e Bdos 1Bh cp 255 ; Обработка ошибки jr nz,Next_Step ld de,Mess3 jr The_End ; Чтение каталога диска по адресу передачи Next_Step: pop af ld l,a ; Имя драйвера ld h,7 ; Максимальная длина каталога ld e,(ix+11h) ; Извлекаем номер первого ld d,(ix+12h) ; кластера каталога push de ; Сохраняем de и hl для push hl ; записи перед выходом Bdos 2Fh ; Читаем каталог в память ld b,(ix+0Bh) ; Число файлов, ld hl,NewDMA ; адрес начала каталога в памяти ; для последующей обработки ; Проверка ключей ld a,(StrPar) cp '-' jr z,Protect cp '+' jr z,ReSet ; Плохие ключи - сообщение и выход ErrStr: ld de,Mess0 The_End: Bdos 9 rst 0 ; ; Засекречивание каталога. Для предотвращения возможной записи на этот ; диск все элементы директории должны быть активными, поэтому свободные ; входные точки заполняются. Protect: ld a,(hl) cp 0 ; Если первый символ элемента каталога jr nz,F_0 ; равен 0, то установить его в "!" ld (hl),'!' F_0: cp 0E5h ; если же первый символ - "Е" русского jr nz,F_1 ; алфавита, то установить его в ":" ld (hl),':' F_1: ld de,11 add hl,de set 1,(hl) ; В байте атрибутов установить бит 1 в 1 ld de,21 add hl,de Djnz Protect ld bc,Mess1 jr Finish ; Рассекречивание каталога ReSet: ld a,(hl) cp '!' ; Если первый символ элемента каталога jr nz,I_0 ; равен "!", то установить его в 0 ld (hl),0 I_0: cp ':' ; Если первый символ элемента каталога jr nz,I_1 ; равен ":", то установить его в "Е" ld (hl),0E5h I_1: ld de,11 add hl,de res 1,(hl) ; Рассекретить файл ld de,21 add hl,de Djnz ReSet ld bc,Mess2 ; Перед выходом в систему - запись измененных секторов на диск Finish: pop hl pop de push bc Bdos 30h pop de ; Сообщение, jr The_End ; конец программы ; Подпрограмма SearCHR ; Поиск символа в строке параметров, начиная с некоторой позиции ; Вход: b - код символа ; hl - позиция в строке (абсолютный адрес) ; Выход: если в a - не 255, ; то в hl - абсолютный адрес символа; ; иначе в hl - адрес конца строки (образец не найден). SearCHR: ld a,(hl) cp 13 jr z,NoFound cp b ret z inc hl jr SearCHR NoFound: ld a,255 ret Mess0: db 'Ошибка в строке параметров !',13,10 db 'Вызов: A> undir <ключи> <дисковод>',13,10 db ' <ключи> : - (спрятать)',13,10 db ' + (рассекретить)',13,10,'$' Mess1: db 'Каталог засекречен !',13,10,'$' Mess2: db 'Каталог рассекречен !',13,10,'$' Mess3: db 'Плохое имя дисковода !',13,10,'$' NewDMA EQU $ END **Небольшое пояснение.** \\ Если Вы заметили, наша программа различает, с чего начинается пустая входная точка каталога — с нуля или с "Е". Это было специально сделано для того, чтобы после засекречивания–рассекречивания директории диска не оставалось никаких следов работы этой программы. Предыдущий вариант этой утилиты не имел такой особенности. После рассекречивания все пустые входные точки начинались с кода Е5h, из–за чего очень сильно увеличивалось время прочтения каталога — система прочитывала все 112 имен файлов. Но стоит системе встретить входную точку, начинающуюся с нуля, она сразу прекращает просмотр каталога. Учитывая эту особенность, при совершенствовании программы мы несколько усложнили ее, заставив разли﹣ чать начало входных точек Справочника. **А теперь — внимание !** \\ Маленький фокус (для тех, кто захочет немного поработать со скрытыми файлами) Для того, чтобы найти в каталоге скрытый файл (с байтом атрибутов, неравным нулю), установите старший бит (со значением 80h) в номере драйвера в единицу. Если, например, шаблон поиска расположен по адресу ''Pattern'', то это можно сделать командами ассемблера: ld a,(Pattern) or 80h ld (Pattern),a . Теперь, пользуясь подпрограммами BDOS ''Search First'' и ''Search Next'', Вы легко найдете не только файлы без установленного байта атрибутов, но и //скрытые// и др. файлы, находящиеся на диске. ---- [<>] {{tag>msxfdfss}}