Связь с администрацией сайта:       

demo

Среди толпы я одинок

Глава 18 - практика: работа с файлами, перехват и восстановление прерываний

Замечание! Это пожалуй самая большая глава! информация представленная в ней в принципе довольно важна! К сожалению при работе в NT/Windows 2000/XP вам не удастся опробовать пример! Тут нужен чистый DOS, или хотя бы Win9x! Я например воспользуюсь эмулятором!

 

 

Работа с файлами через описатели

Если программы, написанные на языках высокого уровня могут открыть файл без выполнения подготовительных действий (они выполняются автоматически), то ассемблерные программы должны создать специальные области данных, которые используются при операциях ввода/вывода. Используется два метода доступа к файлам: метод управляющего блока файла (FCB) и метод дескриптора файла. С помощью метода FCB можно получить доступ только к файлам, находящимся в текущем каталоге. Метод дескриптора файла позволяет получить доступ к любому файлу, независимо от того, какой каталог является текущим.
Начиная с DOS версии 2.0, в набор функций прерывания 21h включе-ны UNIX-подобные файловые функции. Идея их состоит в том, что, когда программа открывает файл, DOS возвращает 16-битовое значение «описателя файла» (дескриптора файла) (handle). После этого, когда программа читает, позиционирует, пишет или закрывает файл, она ссылаетесь на него через описатель. Одно из самых больших удобств – то, что можно обращаться к некоторым устройствам так, как будто это дисковые файлы, через зарезервированные описатели DOS:

 ustr

Ниже приведен перечень наиболее часто используемых функций пре-рывания 21h для работы с файлами через описатели.
Функция 3cH
Создать файл.
Вход. AH=3ch
DS:DX=адрес строки ASCIIZ с именем файла
CX=атрибут файла
Выход. AX=код ошибки, если CF установлен и описатель файла,
если ошибки нет.
Описание. Файл создается в указанном (или умалчиваемом) оглав-лении и открывается в режиме доступа «чтение/запись». Если файл уже существует, то при открытии файл усекается до нулевой длины. Если атрибут файла – «только чтение», открытие отвергается (атрибут можно изменить функцией 43H). 
Функция 5bH
Создать новый файл (не должен существовать).
Вход. AH=5bh
DS:DX=адрес строки ASCIIZ с именем файла
CX=атрибут файла
Выход. AX=код ошибки, если CF установлен и описатель файла,
если ошибки нет
Описание. Этот вызов идентичен функции 3ch, с тем исключением, что он вернет ошибку, если файл с заданным именем уже существует. 
Функция 5aH 
Создать уникальный файл.
Вход. AH=5ah
DS:DX=адрес строки ASCIIZ с путем (заканчивается \) 
CX=атрибут файла
Выход. AX=код ошибки, если CF установлен и описатель файла,
если ошибки нет
DS:DX (не изменяется) становится полным 
ASCIIZ-именем нового файла.
Описание. Открывает (создает) файл с уникальным именем в оглав-лении, указанном строкой ASCIIZ, на которую указывает DS:DX. Описа-ние пути должно быть готово к присоединению в его конец имени файла. Необходимо обеспечить минимум 12 байт в конце строки. После возврата строка DS:DX будет дополнена именем файла. DOS создает имя файла из шестнадцатеричных цифр, получаемых из текущих даты и времени. Ес-ли имя файла уже существует, DOS продолжает создавать новые имена, пока не получит уникальное имя.
Функция 3dH 
Открыть файл.
Вход. AH=3dh
DS:DX=адрес строки ASCIIZ с именем файла
AL=режим открытия
Выход. AX=код ошибки, если CF установлен и описатель файла,
если ошибки нет
Описание. В момент открытия файл должен существовать. Файл открывается в выбранном режиме доступа (AL = 0 – для чтения; AL = 1 – для записи; AL = 2 – для чтения и записи) и указатель «чтения/записи» ус-танавливается в 0. 
Функция 3eH 
Закрыть файл.
Вход. AH=3eh
BX=описатель файла
Выход. AX= код ошибки, если CF установлен
Описание. BX содержит описатель файла (handle), возвращенный при открытии. Файл, представленный этим описателем, закрывается, его буфер сбрасываются, а оглавление обновляется корректными размером, временем и датой.
Функция 41H 
Удалить файл.
Вход. AH=41h
DS:DX=адрес строки ASCIIZ с именем файла
Выход. AX=код ошибки, если CF установлен
Описание. Имя файла не может содержать обобщенные символы («?» и «*»). Файл удаляется из заданного оглавления заданного диска. Если файл имеет атрибут только чтение, то перед удалением необходимо изме-нить этот атрибут через функцию 43H.
Функция 42H 
Установить указатель чтения/записи (можно также узнать размер файла).
Вход. AH=42h
BX=описатель файла
CX:DX=смещение указателя: (CX * 65536) + DX
AL=0 переместить к началу файла + CX:DX
AL=1 переместить к текущей позиции + CX:DX
AL=2 переместить к концу файла - CX:DX
Выход. AX=код ошибки, если CF установлен
DX:AX=новая позиция указателя файла (если нет 
ошибки)
Описание. Перемещает логический указатель чтения/записи к нужному адресу, с которого начнется очередная операция чтения или за-писи. Вызов с AL=2, CX=0, DX=0 возвращает длину файла в DX:AX. DX здесь старшее значащее слово: действительная длина (DX * 65536) + AX.
Функция 3fH 
Читать из файла/устройства.
Вход. AH=3fh
BX=описатель файла
DS:DX=адрес буфера для чтения данных
CX=число считываемых байт
Выход. AX=код ошибки, если CF установлен
AX=число действительно прочитанных байт
Описание. CX байт данных считываются из файла или устройства с описателем, указанным в BX. Данные читаются с текущей позиции указателя чтения/записи файла и помещаются в буфер вызывающей програм-мы, адресуемый через DS:DX.
Всегда необходимо сравнивать возвращаемое значение AX (число прочитанных байт) с CX (запрошенное число байт):
1) если AX=CX, (и CF сброшен) – чтение было корректным без ошибок;
2) если AX=0 – достигнут конец файла (EOF);
3) если AX<CX (но ненулевой), то возможны два варианта: при чте-нии с устройства – входная строка имеет длину AX байт; при чтении из файла – в процессе чтения достигнут EOF.
Функция 40H 
Писать в файл/устройство.
Вход. AH=40h
BX=описатель файла
DS:DX=адрес буфера, содержащего данные
CX=число записываемых байт
Выход. AX=код ошибки, если CF установлен
AX=число действительно записанных байт
Описание. CX байт данных записывается в файл или на устройство с описателем, заданным в BX. Данные берутся из буфера, адресуемого через DS:DX и записываются, начиная с текущей позиции указателя чтения/записи файла. Необходимо всегда сравнивать возвращаемое значение AX (число записанных байт) с CX (запрошенное число байт для записи): если AX = CX, запись была успешной; если AX < CX, встретилась ошибка (скорее всего, переполнение).
Некоторые функции в качестве параметра используют атрибут файла. Атрибут - это один байт битовых флагов, связанный с каждым файлом и находящийся в элементе оглавления для файла. В атрибуте определены следующие биты:
x x A D V S H R
R- только чтение (нельзя обновлять или удалять);
H- скрытый;
S- системный;
V- метка тома;
D- элемент подоглавления;
A- архивный;
x- не используются.
ASCIIZ строка, содержащая имя файла, имеет вид:

«‘d:\путь\имя_файла’,0»

Если диск и/или путь опущены, они принимаются по умолчанию. После вызова функции описатель файла должен быть сохранен для последующих операций. Количество описателей в системе регламентируется файлом CONFIG.SYS.
Приведенный ниже пример иллюстрирует процесс работы с файлом через описатели в ассемблерной программе.

;Создание файла
MOV AH,3CH
MOV CX,0 
LEA DX,BUF ;DS:DX – адрес ASCIIZ строки с именем
INT 21H
JC NO_CREATE ;Проверка флага переноса 
. . . ;Работа с файлом
NO_CREATE:
. . .
BUF DB ‘d:\Users\1.txt’,0

 

Работа с файлами через DTA

Как было сказано ранее, используются два метода доступа к файлу: метод управляющего блока файла (FCB) и метод дескриптора файла. В любом случае программа при работе с файлами должна указывать место в памяти, куда будут помещаться принимаемые данные или откуда будут извлекаться выводимые. Обычно временный буфер устанавливается раз-мером в одну запись и бывает удобно описать его как строковую переменную в сегменте данных. 
Буфер, используемый методом FCB доступа к файлам, называется областью обмена с диском или DTA. На этот буфер указывает условный указатель, который хранится операционной системой и который может быть изменен программой. В документации этот указатель на DTA часто сам называют DTA. Указатель на DTA устанавливается специальной функцией DOS и после того как он установлен все функции чтения/записи автоматически обращаются к нему. Это означает, что сами функции не должны содержать адрес временного буфера.
Для установки указателя на DTA используется функцию 1AH прерывания 21H (DS:DX должны указывать на первый байт DTA). Функция 2FH прерывания 21H сообщает текущую установку указателя DTA (при возврате ES:BX содержат сегмент и смещение DTA).
Префикс программного сегмента PSP обеспечивает каждую программу 128-байтным встроенным DTA, начиная со смещения 80H и до 9FH. Программа может использовать его при нехватке памяти. Первоначально указатель на DTA указывает именно на этот буфер, поэтому если программа будет использовать его, то нет нужды устанавливать указатель. Этот буфер по умолчанию особенно удобно использовать с COM файлами, где DS указывает на начало префикса программного сегмента. Для файлов EXE может потребоваться небольшой добавочный код, чтобы использовать DTA по умолчанию. 

 dta


Примечание: после извлечения года к нему необходимо прибавить 1980.
Существует ряд функций для работы с файлами, используя DTA. Наиболее употребимые из них приведены ниже.
Функция 1ah
Установить адрес DTA.
Вход. AH=1aH
DS:DX=адрес для DTA
Выход. Нет
Описание. Устанавливает адрес DTA.
Функция 2fh
Дать текущий DTA.
Вход. AH=2fH
Выход. ES:BX=адрес для DTA
Описание. Возвращает адрес начала области ввода-вывода (DTA). Поскольку DTA глобальна для всех процессов, в рекурсивной процедуре (например, при проходе по дереву оглавления) может потребоваться со-хранить адрес DTA, а впоследствии восстановить его посредством функ-ции 1aH.
Функция 4eh
Найти 1-й совпадающий файл.
Вход. AH=4eH
DS:DX=адрес строки ASCIIZ с именем файла 
CX=атрибут файла для сравнения
Выход. AX=код ошибки, если CF установлен
DTA=заполнена данными (если не было ошибки)
Описание. DS:DX указывает на строку ASCIIZ в форме: "d:\путь\имяфайла",0. Если диск и/или путь опущены, они подразумевают-ся по умолчанию. Обобщенные символы * и ? допускаются в имени файла и расширении. DOS находит имя первого файла в оглавлении, которое совпадает с заданным именем и атрибутом, и помещает найденное имя и другую информацию в DTA.
Функция 4fh
Найти следующий совпадающий файл.
Вход. AH=4fH
DS:DX= адрес данных, возвращенных предыдущей 4eH.
Выход. AX=код ошибки, если CF установлен
DTA=заполнена данными (если не было ошибки)
Описание. DS:DX указывает на 2bH-байтовый буфер с информаци-ей, возвращенной функцией 4eH (либо DTA, либо буфер, скопированный из DTA).Необходимо использовать эту функцию после вызова 4eH. Сле-дующее имя файла, совпадающее по обобщенному имени и атрибуту фай-ла, копируется в буфер по адресу DS:DX вместе с другой информацией (см. функцию 4eH о структуре файловой информации в буфере, заполняе-мом DOS).
Ниже приведен фрагмент программы, иллюстрирующий организацию поиска файлов в текущем каталоге.

;Установить адрес DTA
MOV AH,01AH
LEA DX,FDTA
INT 21H
. . .
;Наити первый совпадающий файл
MOV AH,4EH
LEA DX,MASKA
MOV CX,10H
INT 21H
JC EXIT
NEXT:
. . .
;Найти следующий совпадающий файл
MOV AH,4FH
MOV CX,10H
LEA DX,MASKA
INT 21H
JNC NEXT
EXIT:
. . .
;========== DTA =========
FDTA DB 15H DUP (?)
FATTR DB ?
FTIME DW ?
FDATA DW ?
FSIZE DD ?
FNAME DB 0DH DUP ('$')
;========================
MASKA DB '*.*',0

 

Структура PSP

Префикс программного сегмента PSP (Program Segment Prefics) – специальная область оперативной памяти размером 256 (100h) байт. PSP может использоваться в программе для определения имен файлов и пара-метров из командной строки, введенной при запуске программы на выпол-нение, объема доступной памяти, переменных окружения системы и так далее. После загрузки программы в память сегментные регистры DS и ES указывают на начало PSP этой программы. 

 psp

Окружение DOS

DOS поддерживает область памяти, содержащую набор строк ASCIIZ, которые могут использоваться приложениями для получения некоторой системной информации и для передачи данных между программами. Эта область памяти называется окружением DOS.
Структура окружения DOS:

имя_1=значение_1«0»
имя_2=значение_2«0»
. . .
имя_N=значение_N«0»
«0»
«xxxx»
EXEC_string_1«0»
. . .
EXEC_string_NN«0»
«0»

Здесь «0» - это символ ASCII NUL (00H), а «xxxx» - 16-битовое дво-ичное значение (количество дополнительных строк).
Окружение не превышает 32K байт и начинается на границе парагра-фа. Смещение 2cH в PSP текущей программы содержит номер параграфа окружения. Используя окружение, можно найти нужное имя' серией срав-нений строк ASCIIZ, пока не достигнута пустая строка (нулевой длины), что указывает конец окружения.
В последних версиях DOS, за концом официального окружения по-мещается дополнительная строка, которая содержит диск и путь, с которых была загружена программа. Вслед за последней строкой ASCIIZ окруже-ния находится нулевой байт, указывающий конец официального окруже-ния. Следующие два байта содержат 16-битовый двоичный счетчик допол-нительных строк (обычно 0001H). Вслед за значением счетчика находится строка ASCIIZ, содержащая путь и имя файла. Это в точности та строка, которая использовалась функцией DOS 4bH (EXEC) для загрузки и запуска программы.

 

Работа с прерываниями

Иногда необходимо выполнить одну из набора специальных проце-дур, если в системе или в программе возникают определенные условия, например, нажата клавиша на клавиатуре. Действие, стимулирующее вы-полнение одной из таких процедур, называется прерыванием. Существует два общих класса прерываний: внутренние и внешние. Первые иницииру-ются состоянием ЦП или командой, а вторые - сигналом, подаваемым от других компонентов системы. 
Переход к процедуре прерывания осуществляется из любой програм-мы, а после выполнения процедуры прерывания обязательно происходит возврат в прерванную программу. Перед обращением к процедуре преры-вания должно быть сохранено состояние всех регистров и флагов, исполь-зуемых процедурой прерывания, а после окончания прерывания эти реги-стры должны быть восстановлены.
Последовательность прерывания состоит в следующем:
1) текущее значение регистра Flags включается в стек;
2) текущее значение регистра CS включается в стек;
3) текущее значение регистра IP включается в стек;
4) сбрасываются флаги IF и TF.
Новое содержимое IP и CS определяет начальный адрес выполняемой процедуры прерывания (обслуживание прерывания). Возврат в прерван-ную программу осуществляется командой, которая извлекает из стека со-держимое для IP, CS и регистра флагов (обычно это команда IRET).
Адреса подпрограмм обслуживания прерываний (вектора прерыва-ний) хранятся в таблице векторов прерываний. Таблица векторов прерыва-ний располагается по адресу 0000:0000 и представляет собой массив из 256 элементов, каждый элемент которого занимает 4 байта и представ-ляет собой начальный адрес процедуры обработки прерывания.
Иногда в программе возникает необходимость переопределения (пе-рехвата) прерываний (например, выполнение дополнительных действий при нажатии определенной клавиши клавиатуры). Процесс перехвата пре-рываний состоит в следующем:
1) подготавливается FAR-процедура – новый обработчик прерыва-ний (должна заканчиваться командой IRET);
2) сохраняется старый вектор прерывания (функция 35h прерывания 21h)
3) адрес нового обработчика заносится в таблицу векторов прерыва-ний (функция 25h прерывания 21h); 
4) в конце программы происходит восстановление первоначального обработчика прерываний.
Функция 35h
Вход. AH=35H
AL=номер прерывания (00H до 0ffH)
Выход. ES:BX=адрес обработчика прерывания
Описание. Возвращает значение вектора прерывания для INT (AL),
то есть загружает в BX 0000:[AL*4], а в ES –
0000:[(AL*4)+2].
Функция 25h
Вход. AH=25H
AL=номер прерывания (00H до 0ffH)
DS:DX=вектор прерывания (адрес подпрограммы)
Выход. Нет
Описание. Устанавливает значение элемента таблицы векторов прерываний для прерывания с номером AL равным DS:DX. Это равно-сильно записи 4-байтового адреса в 0000:(AL*4), но, в отличие от прямой записи, в момент записи прерывания будут заблокированы.

 

Разбор практической программы

Задание: Создать текстовый файл "Dir.Txt", содержащий перечень файлов в текущем каталоге. Написать программу переопределения прерывания 05h (клавиша Print Screen). 
Примечание:
1) файл «Dir.txt» можно создавать через описатели;
2) получение пути по которому была запущена программа (первая строка в файле «Dir.Txt») можно осуществить, используя окружение DOS. Для этого, во-первых, необходимо получить адрес PSP (функция 062H прерывания 21H), во-вторых, найти в нем адрес окружения DOS. В-третьих, получив из окружения DOS строку, содержащую путь и имя за-пущенного файла, выделить из нее путь к текущему каталогу;
3) на следующем этапе производится поиск первого совпадающего с маской «*.*» файла и его имя записывается в файл «Dir.Txt». Перед на-чалом поиска необходимо правильно установить атрибут файла в CX для сравнения. Далее производится поиск следующего совпадающего с маской файла, используя функцию 04FH прерывания 21H. Если такой файл най-ден, то его имя записывается в «Dir.Txt», иначе осуществляется выход из программы;
4) перед загрузкой нового вектора прерывания необходимо сохранить старый вектор (функция 35h прерывания 21h); 
5) новый обработчик прерывания должен быть FAR-процедурой;
6) для проверки, новая процедура обработки прерывания 05h должна выводить в динамик сигнал (прерывание 21H). В основной программе не-обходимо организовать большой цикл, например, выводящий на экран символы (прерывание 21h использовать нельзя, можно использовать, на-пример, прерывание 10h). Таким образом, при нажатии на PrintScreen во время этого цикла компьютер должен издавать сигнал.

Файл fandp.asm
.model tiny
.186
.code
;благодаря этой директиве мы сразу имеем адрес окружения! :) удобно! :) 
org 2Ch
okr dw ? 
org 100h
start: 
;так можно загрузить сегм. регистр! :) 
push okr
pop es
;начали искать строку с именем в окружении DOS
xor ax,ax
xor di,di
cld
met:scasb
jne d
cmp es:[di+1],byte ptr 0
je quit
d:jmp met
quit:add di,2
xor si,si
ms:mov al,byte ptr es:[di]
cmp al,0
je poka
mov path[si],byte ptr al
inc si
inc di
jmp ms
;закончили сканировать окружение в поисках пути
;перехватываем прерывание 
poka:sub si,9
mov ax,3505h
int 21h
mov word ptr oldint, bx
mov word ptr oldint+2,es
mov ax,2505h
mov dx,offset inter
int 21h
;выдаем писк! :) а то принт скрин всё равно нажать не успеем! :) 
int 5h
mov ah,0h
int 16h
lea dx,mesg
mov ah,9
int 21h
;открываем файл 
call openfile
mov handle,ax
mov ah,40h
mov bx,handle
mov cx,si
lea dx,path
int 21h
mov ah,40h
mov bx,handle
mov cx,2
lea dx,crlf
int 21h 
;установить адрес dta
mov ah,01ah
lea dx,fdta
int 21h
;начать поиск файлов
mov ah,4eh
lea dx,maska
mov cx,10h
int 21h
jc exit
mov di,0
mov cx,14
m0:cmp fname[di],'0'
je h0
inc di
h0:loop m0
mov ah,40h
mov bx,handle
mov cx,di
lea dx,fname
int 21h
mov ah,40h
mov bx,handle
mov cx,2
lea dx,crlf
int 21h
next:mov ah,4fh
mov cx,10h
lea dx,maska
int 21h
jc exit
mov di,0
mov cx,14
m2:cmp fname[di],'0'
je h1
inc di
h1:loop m2
mov ah,40h
mov bx,handle
mov cx,di
lea dx,fname
int 21h
mov ah,40h
mov bx,handle
mov cx,2
lea dx,crlf
int 21h
xor ax,ax
mov di,0
mov cx,14
m1:mov fname[di],al
inc di
loop m1
jmp next
exit: mov ah,3eh
mov bx,handle
int 21h
;восстанавливаем вектор обратно 
mov ax,2505h
mov dx,word ptr oldint+2
mov ds,dx
mov dx,word ptr cs:oldint
int 21h
ret
;процедура обработки прерывания
inter proc far 
pusha
push es
push ds
mov dl,07
mov ah,02
int 21h
pop ds
pop es
popa
jmp cs:oldint
inter endp
;процедура открытия файла
openfile proc near
mov ah,3ch
mov cx,0
mov dx,offset buf
int 21h
jnc nerr
mov dx,offset myerr
mov ah,9
int 21h
;перевод строки
mov dx,offset crlf
mov ah,09h
int 21h
nerr: ret
openfile endp
;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
oldint dd ?
mesg db "Find files$"
crlf db 0Dh,0Ah,'$'
buf db "filedir",0
myerr db "WARNING!!!
File not create$"
handle dw ?
maska db "*.*",0
fdta db 15h dup (?)
fattr db ?
ftime dw ?
fdata dw ?
fsize dd ?
fname db 14 dup ('0')
path db 256 dup (?)
end start

Компиляция:
c:\specprog\tasm\bin\tasm.exe /m fandp.asm
c:\specprog\tasm\bin\tlink.exe fandp.obj /t/x

Вот что имеем в результате запуска:
(под эмулятором и на реальном DOS при запуске вы услышите "писк")
список найденых файлов сохраняется в файле filedir

vm fandp1

 

vm fandp2

 

Rate this item
(1 Vote)
Login to post comments