Запись в файл или устройство
Запись в файл или устройство
Запись в файл производится функцией 40h с текущей позиции файлового указателя. Вход: АН = 40 h; ВХ = дескриптор файла; СХ = количество байтов для записи;
DS:DX — указатель на область, из которой записываются данные. Выход: CF = 0 — АХ = число действительно записанных байтов в файл или устройство; CF = 1 — АХ = код ошибки: 5 — в доступе отказано; 6 — недопустимый дескриптор.
Если при вызове функции 40h регистр СХ равен нулю, то данные в файл не записываются и он усекается или расширяется до текущей позиции файлового указателя. Если СХ не равен нулю, то данные в файл записываются начиная с текущей позиции файлового указателя. Операция записи также продвигает файловый указатель на число действительно записанных байтов.
Положение файлового указателя можно изменять явно с помощью функции 42h. Вначале приведем пример программы, выводящей данные на экран.
:prg07_08.asm - программа демонстрации вывода на экран строки функцией 40h.
.data
string db 'строка для вывода на экран функцией 40h'
len_string=$-stnng point_fname dd string
..........
.code
movbx.l -.стандартный дескриптор - экран
mov cx.1en_string
Ids dx.point_fname;формируем указатель на строку string
movah.40h -.номер функции DOS
int 21h ;выводим
jc exit ;переход в случае ошибки
пор -.для тестирования
Далее приведем пример программы, которая заполняет файл my_file.txt данными в виде строк символов, вводимых с клавиатуры. Длина строк — не более 80 символов. Нажатие клавиши Enter после ввода каждой строки означает, что эта строка символов должна являться отдельной строкой файла my_file.txt. Для этого перед выводом каждой строки в файл в конце ее необходимо вставлять символы OdOah. При нажатии клавиши Пробел в начале ввода очередной строки (ASCII-код — 3210 или 2016) направление ввода данных в файл изменяется следующим образом: файл расширяется на величину, равную количеству уже введенных символов, и дальнейший ввод осуществляется с конца файла. Завершение работы программы определяется моментом, когда оба введенных потока Ь в файле встречаются (не перекрываясь).
:prg07_09.asm - программа заполнения файла my_file.txt данными в виде строк символов.
:вводимыми с клавиатуры.
buf_Oahstruc
len_buf db 83 ;длина buf_0ah
len_in db 0 действительная длина введенного слова (без учета 0dh)
buf_in db 82 dup (20h) :буфер для ввода Сс учетом 0dh и позднее добавляем Oah)
ends
.data
handle dw 0 :дескриптор файла
filename db 'my_file.txt',0
point_fname dd filename
buf buf_0ah<>
prev_d label dword ;для сохранения длины предыдущей строки при выводе с конца файла prev dw 0
dw 0
middle dd 0 ;позиция в середине файла, при достижении которой снизу выходим :из программы
.code
:-----открываем файл-----...............................--
хогсх.сх ;атрибуты файла - обычный файл
movbx,2 ;режим доступа - доступ для чтения-записи, режим буферизации MS DOS
movdx,12h ;если файл существует, то открыть его без сохранения прежнего содержимого, :в обратном случае создать его
Ids si ,point_fname:формируем указатель на имя файла
movah.6ch ;номер функции DOS
int 21h открываем (создаем) файл
jc exit :если ошибка, то переход на конец ;действия при успешном открытии файла:
mov handle.ax ,-сохраним дескриптор файла ;—позиционируем файловый указатель с начала файла.......
mov ah.42h
хог al,al
хог ex,ex
хог dx.dx
mov bx, handle
int 21h cycl: ;вводим очередную строку с клавиатуры
lea dx.buf
mov ah,Oah
Int 21h ;для красоты ввода выводим на экран символ Oah
mov dl .Oah
mov ah.2
int 21h
emp buf.buf_in.20h;первый символ введенной строки сравниваем с пробелом
je revers ;переход на изменение ввода - добавляем Oah в конец введенной строки
lea si.buf.buf_in
mov al .buf .lenjn
cbw push si
add si ,ax
incsi учитываем неучтенный в lenjn символ 0dh
mov byte ptr [si],Oah H--......вывод в файл:..........................---........
I popdx указатель на область, откуда будем выводить строку
mov bx.handle
add ax,2 учитываем неучтенный в len_in символ 0dh
movcx.ax :длина выводимых данных
mov ah.40h
int 21h
jmp cycl
revers: ;записываем файл с конца, предварительно расширив его ;узнаем. сколько было уже записано до этого: ;для этого вначале сбрасываем буферы на диск
mov bx.handle
mov ah.68h
int 21h ;теперь можно и узнать - определение длины файла:
mov al ,2
хог сх.сх
хог dx.dx ;CX:DX -0 - нулевое смещение
mov ah,42h
int 21h :в DX:AX возвращается длина файла в байтах
jc exit :если ошибка :формируем полную длину в edx
shl eax,16
shld edx.eax,16
mov middle. edx сохраним как условие выхода из программы при достижении снизу расширение файла с помощью функции 42h int 21h и последующей записи :умножаем длину на 2. при первой операции записи файл расширится:
shl edx.l
shld ecx.edx.16
mov al.O
хог сх.сх
mov ah.42h
int 21h расширяем файл, устанавливая указатель
jc exit :если ошибка расширим файл, выведя последнюю введенную строку с пробелом:
cycl2: lea si,buf,buf_in
mov al .buf .lenjn
cbw ptush si
add si.ax
incsi учитываем неучтенный в lenjn символ 0dh
добавляем Oah в конец введенной строки
mov byte ptr [si],Oah ;выводим в файл:
popdx указатель на область, откуда будем выводить строку
add ах.2 учитываем неучтенный в len_in символ 0dh
movcx.ax :длина выводимых данных
movprev.ax .сохраним длину для корректировки при выводе следующей строки
mov bx.handle movah.40h int 21h jc exit
;сбрасываем буфер, чтобы смотреть изменения в файле при работе в отладчике -:легче запретить (см. обсуждение ниже)
mov bx,handle
mov ah,68h Int 21h :вводим очередную строку с клавиатуры
lea dx.buf
mov ah.Oah
Int 21h :для красоты ввода выводим на экран символ Oah
mov dl .Oah
mov ah,2
int21h
;......использование 42h с отрицательным смещением относительно
:текущего значения файлового указателя:
устанавливаем файловый указатель в позицию вывода следующей строки
;с учетом того, что выводим с конца (текущей позиции) файла:
хог есх.есх
mov al.buf,len_in
cbw
add prev.ax
add prev.2 учитываем наличие OdOah
sub ecx.prev_d :получаем отрицательное смещение - сформируем его в паре СХ:DX
shrd edx,ecx,16
shr edx.16 :довернем edx
shr ecx.16 :и есх устанавливаем файловую позицию для записи очередной строки
mov bx,handle
mov ah.42h
moval.l ;смещение от текущей позиции
int 21h :сравним текущую позицию с middle
shl eax.16
shld edx,eax,16
cmp edx,middle
jl exit
jmp cycl2 exit:
Программа выглядит не очень эстетично, но главная ее цель достигнута — показать работу с файловым указателем при записи в файл. Мы попробовали разные варианты: позиционирование на конец файла (при этом можно узнать длину файла); использование отрицательного смещения (задавая нулевое значение в CX:DX при al = 1 можно получить в DX:AX текущую позицию в файле); расширение файла путем задания в СХ: DX заведомо большего значения, чем длина файла. Как видно из программы выше, все эти и другие эффекты достигаются за счет манипулирования значениями в парах СХ :DX и DX:AX, а также в регистре AL, где задается начальное положение в файле, относительно которого производится операция чтения-записи.
В заключение рассмотрения функции 40h записи в файл отметим то, для чего мы использовали функцию сброса буферов на диск 68h. Для этого коротко необходимо коснуться проблемы буферизации ввода-вывода в MS DOS. MS DOS ис-
пользует буферизацию ввода-вывода для ускорения работы с диском. При этом, р частности, данные, записываемые на диск, не записываются на него сразу, а по-щешаются вначале в буфер. Запись буфера на диск производится при его заполнении- Буферизацию эффективно использовать при интенсивной работе с одними и теми же данными. Тогда при необходимости чтения данных с диска они 6уДУт читаться из буфера. В нашей программе буферизация нам только мешала, так как при работе в отладчике мы не могли своевременно наблюдать за изменениями выходного файла my_file.txt Для этого нам приходилось использовать функцию 68h для принудительного сохранения буферов на диск. Вход: АН = 68h; BX = дескриптор файла. Выход: CF = 0 в случае успеха; CF = 1 — АХ = код ошибки.
В результате работы функции все данные из буферов дисков DOS немедленно записываются на диск, при этом модифицируется соответствующий файлу
элемент каталога.
Для нашей задачи буферизацию лучше вовсе запретить, тогда отпадет необходимость в принудительном сохранении строк в файле для того, чтобы в динамике отслеживать его изменения. Для этого при вызове функции 6ch в регистре ВН необходимо установить бит 6 следующим образом: 6 = 0 — использовать стандартную для MS DOS буферизацию; 6 = 1 — отменить стандартную для MS DOS буферизацию. В нашем примере это можно сделать так:
:......открываем файл-------------------------------------
хогсх.сх атрибуты файла - обычный файл
mov bx.4002h :режим доступа - доступ для чтения-записи, запрет буферизации
movdx,12h :если файл существует, то открыть его без сохранения прежнего
содержимого, в обратном случае создать файл Ids si.point_fname:формируем указатель на имя файла movah.6ch :номер функции DOS int21h открываем (создаем) файл jc exit :если ошибка, то переход на конец
--------------------------------------------------------------------------
Все вызовы функции 68h в приведенной выше программе можно закомментировать.