Сборник по задачам и примерам Assembler

     

Запись в файл или устройство



Запись в файл или устройство

Запись в файл производится функцией 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 в приведенной выше программе можно закомментировать.


Содержание раздела