открыт, так как при открытии файла DOS вставляет в это поле значение по умолчанию, равное 128. Каждый раз при вызове функции данные загружаются в память, начиная с первого байта DTA. Если DTA используется как небольшой временный буфер, то перед чтением следующей записи содержимое DTA должно быть перенесено в область данных файла, отведенную в памя- ти. Можно наоборот установить указатель DTA на стартовый адрес памяти, начиная с которого будет размещаться файл, а после чтения каждой записи указатель увеличивать на размер записи, с тем чтобы он указывал на место, где должна быть следующая запись. Установкой полей текущей записи (DB, смещение 1FH) и блока текущей записи (DW, смещение 0CH) отличными от нуля, последова- тельный может читаться, начиная с любого требуемого места (ус- тановка должна быть сделана после открытия FCB). После каждого чтения поле текущей записи автоматически увеличивается на 1, а после чтения 128 записей увеличивается поле текущего блока. При возврате AL равен 0, если вся запись успешно прочитана. При обна- ружении конца файла AL будет содержать 1, если функция 14H вообще не возвратила данных и 3 - если запись прочитана частично. В приведенном примере из файла считываются две записи и после- довательно помещаются в нужную область памяти. Размер записи установлен равным 256 байтам. Записи считываются в цикле и после того, как первая запись считана, указатель на DTA изменяется таким образом, чтобы он указывал на следующий пустой байт в об- ласти данных. ;---помещаем FCB в сегмент данных FCB DB 0,'OLDDATA DAT', 25 DUP(0) DATA_AREA DB 512 DUP (?) ;используем как DTA ;---устанавливаем DTA на начало области данных LEA DX,DATA_AREA ;DS:DX указывают на DTA MOV DI,DX ;сохраняем копию MOV AH,1AH ;функция установки DTA INT 21H ;устанавливаем DTA ;---открываем файл LEA DX,FCB ;DS:DX указывают на FCB MOV AH,0FH ;функция открытия файла INT 21H ;открываем файл CMP AL,0 ;проверка на ошибку JNE OPEN_ERROR ; ;---устанавливаем размер записи 256 байт LEA BX,FCB ;DS:DX указывают на FCB MOV AX,256 ;размер записи MOV DS:[BX]+14,AX ;посылаем в поле размера записи ;---чтение данных MOV CX,2 ;число читаемых записей NEXT_REC: MOV AH,14H ;функция чтения файла LEA DX,FCB ;DS:DX указывают на FCB INT 21H ;читаем одну запись CMP AL,0 ;все в порядке? JE CONTINUE ; CMP AL,2 ;проверка на ошибку JE READ_ERROR ; . . CONTINUE: ADD DI,256 ;увеличиваем указатель MOV DX,DI ;DX указывает на новую DTA MOV AH,1AH ;функция установки DTA INT 21H ;устанавливаем DTA LOOP NEXT_REC ;идем на чтение следующей записи ;---позднее, закрываем файл LEA DX,FCB ;DS:DX указывают на FCB MOV AH,10H ;функция закрытия файла INT 21H ;закрываем файл CMP AL,0FFH ;проверка на ошибку JE CLOSE_ERROR ; Метод дескриптора файлов: Функция 3FH прерывания 21H может читать данные из файла после- довательно. Эта функция используется для любого чтения из файла с помощью метода дескриптора файлов, включая файлы прямого доступа. Файл должен быть открыт функцией 3DH прерывания 21H с кодом 0 в AL, если он открывается только для чтения, и с кодом 2 - если он открывается для чтения и записи. При открытии файловый указатель автоматически устанавливается на первый байт файла. Функция чте- ния из файла указывает сколько байтов должно быть считано и после того как это сделано файловый указатель указывает на байт, сле- дующий за последним считанным байтом, подготавливая следующее обращение к функции. Отметим, что файловый указатель уникален для каждого файла - операции над другими файлами не меняют его пози- цию. Программа может создать небольшой временный буфер, размером, скажем, 512 байт, и постоянно вызывать функцию чтения, не забо- тясь о позиции файлового указателя. Другой метод состоит в считы- вании всего файла прямо в то место памяти, где он должен быть расположен. В этом случае надо просто потребовать, чтобы функция прочитала больше байтов, чем реально содержится в файле, так как чтение прекращается при достижении последнего байта файла. Однако Вам необходимо знать точную длину файла, чтобы знать где кончают- ся данные в буфере, в который Вы считали файл. Размер файла можно определить, сдвинув файловый указатель на конец файла. Это надо сделать сразу же после открытия файла. Поместите в AL код 2 и вызовите функцию 42H, для того, чтобы сдвинуть указатель на конец файла. CX и DX должны содержать 0, так как в противном случае указатель будет сдвинут с конца файла на величину, которая содержится в этих регистрах. При возврате DX:AX будут содержать новую позицию указателя, как смещение отно- сительно начала файла, т.е., в данном случае, длину файла. Не забудьте снова вернуть файловый указатель на начало файла, перед тем как читать его; это делается точно таким же образом, за иск- лючением того, что в AL надо поместить 0. Если при выполнении функции 42H возникает ошибка, то устанавливается флаг переноса, а в AX возвращается 1, если неверен номер функции, и 6 - если ука- зан неверный номер файла. Теперь программа готова для чтения файла. Надо поместить номер файла в BX, а требуемое число байтов в CX и выполнить прерывание. При возврате AX будет содержать число реально прочитанных байтов. Если AX равен нулю, то достигнут конец файла. При других ошибках устанавливается флаг переноса, а AX содержит 5 - при ошибке обо- рудования и 6 - если указан неверный номер файла. В следующем примере в буфер памяти считывается весь небольшой файл. Для у- добства буфер располагается в сегменте данных, что существенно увеличивает размер программы на диске. В своих программах лучше создавать буфер, используя технику распределения памяти, описан- ную в [1.3.1]. ;---в сегменте данных PATH DB 'A:FILENAME.EXT'0 ;строка пути к файлу DATA_BUFFER DB 1000 DUP (?) ;буфер данных HANDLE DW ? ;номер файла FILESIZE DW ? ;размер файла ;---открываем файл LEA DX,PATH ;DS:DX указывают на путь MOV AL,0 ;код открытия для чтения MOV AH,3DH ;функция открытия файла INT 21H ;открываем файл JC OPEN_ERROR ;проверка на ошибку MOV HANDLE,AX ;запоминаем номер файла ;---устанавливаем файловый указатель на конец файла MOV AH,42H ;функция установки указателя MOV AL,2 ;код для конца файла MOV BX,HANDLE ;номер файла MOV CX,0 ;смещение равно нулю MOV DX,0 ; INT 21H ;устанавливаем указатель JC POINTER_ERROR1 ;обработка ошибки MOV FILESIZE,AX ;запоминаем размер (меньше 64K) ;---возвращаем указатель на начало MOV AH,42H ;номер функции MOV AL,0 ;код для начала файла MOV CX,0 ;смещение равно нулю MOV DX,0 ; INT 21H ;устанавливаем указатель JC POINTER_ERROR2 ;обработка ошибки ;---читаем весь файл MOV AH,3FH ;номер функции чтения файла MOV BX,HANDLE ;номер файла MOV CX,FILESIZE ;число считываемых байтов LEA DX,DATA_BUFFER ;DS:DX указывают на буфер INT 21H ;читаем файл JC READ_ERROR ;обработка ошибки ;---позднее, закрываем файл MOV BX,HANDLE ;номер файла MOV AH,3EH ;функция закрытия файла INT 21H ;закрываем файл JC CLOSE_ERROR ;обработка ошибки 5.4.5 Запись в файлы прямого доступа. Физически файлы прямого доступа ничем не отличаются от после- довательных файлов, они отличаются только режимом доступа. Файл прямого доступа предполагает, что его данные организованы в виде записей фиксированной длины, таким образом положение каждой запи- си может быть вычислено (в последовательных файлах n-ный элемент ищется путем подсчета разделителей между элементами, начиная с начала файла). Операционная система автоматически выполняет эти вычтсления. Однако любая программа может выполнять эту работу сама, устанавливая файловый указатель на нужную позицию и считы- вая последовательно такое число байтов, которое образует запись. Высокий уровень. В [5.3.3] объяснен формат открытия файдов прямого доступа в Бейсике. В отличии от последовательного файла, файл прямого дос- тупа может читаться и записываться в одно и то же время, без закрытия и повторного его открытия. Оператор OPEN завершается числом, дающим размер записи файла. Например, OPEN "R", 1, "NEW- DATA", 20 устанавливает для файла NEWDATA размер записи в 20 байт (при этом файл открывается как файл #1). После того как файл открыт, его записи могут быть разбиты на составляющие переменные с помощью оператора FIELD. Оператор FIELD указывает сколько байтов записи отводится под каждую переменную. Например, запись длиной 20 байт может быть разбита оператором FIELD 1, 14 AS LASTNAME$, 2 AS DEPOSIT$, 4 AS ACCTNUM$. В этом операторе первая цифра 1 указывает, что данный оператор FIELD описывает разбиение записи для файла, открытого под номером #1. Данные располагаются в записи точно в том порядке, в каком они описаны в операторе FIELD. Опреаторы RSET и LSET сдвигают данные в полях, выравнивая их по правому (RSET) или левому (LSET) краю и заполняя остающиеся пустые места пробелами. Например, для того, чтобы вставить фамилию "SMITH" в 14-байтное поле с именем LASTNA- ME$, надо записать RSET LASTNAME$ = "SMITH", или если переменной N$ было присвоено значение "SMITH", то RSET LASTNAME$ = N$. Вмес- то RSET может быть использовано LSET. Когда впоследствии данные считываются из поля в переменную, то переменной присваиваются все 14 байтов. При использовании RSET программа удалит все лишние пробелы в начале строковой переменной, однако если будет исполь- зоваться LSET, то пробелы будут удаляться справа. Отметим, что все имена переменных в операторе FIELD относятся к строковым переменным. В файлах прямого доступа Бейсик рассмат- ривает все переменные - включая числовые - как строковые. Число- вая переменная должна быть преобразована в специальный вид, преж- де чем ее значение может быть присвоено полю, а когда она затем считывается из поля то необходимо обратное преобразование. Слово преобразование стоило бы заключить в кавычки, поскольку Бейсик на самом деле не меняет способ представления числа в памяти; он просто обрабатывает число особым образом. Числовые поля требуют двух байтов для целых чисел, четырех байтов - для чисел с обычной точностью и восьми байтов - для чисел с двойной точностью. В точности такое же число байт требуется для представления этих чисел в памяти. Для преобразования их в строковую форму надо использовать функции MKI$, MKS$ и MKD$, которые осуществляют преобразование число-строка для целых, вещественных и чисел с двойной точностью, соответственно. Обычно эти функции комбини- руются с операторами RSET или LSET, например, RSET = ACCTNUM$ = MKI$(X), где X - целая переменная, если полю ACCTNUM$ было отве- дено два байта в операторе FIELD. После того как поля заполнены операторами RSET и LSET, запись записывается на диск с помощью оператора PUT#. PUT #1, 245 поме- щает данные в запись номер 245, файла открытого под номером #1. Номер записи может быть опущен, в этом случае данные записываются в запись с номером на единицу больше, чем номер последней запи- санной записи (начиная с записи 1). Записывается вся запись цели- ком, даже если не все поля были заполнены данными. Отметим, что поля буфера не очищаются при выполении операции PUT, поэтому элементы данных, такие как текущая дата, могут помещаться в буфер только один раз, а затем они будут записаны во все записи, кото- рые будут записываться в течение данной сессии. Функция LOC возв- ращает номер последней записанной в файл записи. Если файл был открыт под номером #3, то напишите X = LOC(3). Функция LOF (длина файла) возвращает длину файла в байтах. Для определения числа записей, содержащихся в файле, надо разделить это значение на длину записи. Добавление 1 к этому значению дает номер записи, который надо использовать, чтобы добавить к файлу новые записи. Если файл был открыт под номером #2, а длина его записей равна 32 байтам, то требуемое значение вычисляется как RECORDNUM = LOF(2)/32 + 1. В следующем примере файл прямого доступа открывается с длиной записи 24 байта, причем запись разбита на три переменные. Пользо- ватель программы запрашивается о содержимом всех трех полей, а когда все они введены, то запись добавляется к файлу. В строке 120 вычисляется начальный номер записи. Отметим, что данные могут не записываться физически на диск каждый раз при выполнении опе- ратора PUT. В выходном буфере могут накапливаться несколько запи- сей, прежде чем это будет сделано. 100 OPEN "R", 1, "A:NEWDATA.DAT", 24 'открываем файл 110 FIELD 1, 18 AS LASTNAME$, 2 AS AGE$, 4 AS WEIGHT 120 R = LOF(1)/24 + 1 'номер последней записи + 1 130 CLS 'чистим экран 140 INPUT "Enter name:",N$ 'получаем имя (строка) 150 INPUT "Enter age:",A% 'получаем возраст (целое) 160 INPUT "Enter weight:",W! 'получаем вес (вещественное) 170 RSET LASTNAME$ = N$ 'помещаем в поле имя 180 RSET AGE$ = MKI$(A%) 'помещаем в поле возраст 190 RSET WEIGHT$ = MKS$(W!) 'помещаем в поле вес 200 PUT #1, R 'записываем запись 210 R = R + 1 'увеличиваем счетчик 220 PRINT: PRINT "Do another (y/n)?" 'запрос пользователя 230 C$ = INKEY$: IF C$ = "" THEN 220 'ожидаем ответа 240 IF C$ = "y" THEN CLS: GOTO 130 'если да, то на начало 250 CLOSE 'иначе закрываем файл Средний уровень. Как и все другие операции с файлами в MS DOS имеется два мето- да записи в файл прямого доступа, один с использованием управляю- щего блока файла, а другой с помощью дескриптора файла. В обоих случаях Вы должны создать буфер обмена данными, размер которого должен быть не меньше, чем размер записи. Метод управляющего блока файла: Откройте управляющий блок файла с помощью функции 0FH и пусть DS:DX указывают на него. После того как файл открыт поместите номер записи для прямого доступа в поле записи прямого доступа FCB. Затем вызовите функцию 22H прерывания 21H, которая пердаст данные из DTA в файловый буфер, созданный при создании FCB. Дан- ные могут не быть немедленно записаны на диск, если размер записи меньше чем размер буфера. Реальная запись на диск будет происхо- дить тогда, когда очередной вызов функции 22H заполнит буфер. При возврате из функции 22H AL будет содержать 00, если обмен прошел успешно. В противном случае в нем будет 1, если не хватает пространства на диске и 2 - если область переноса мала для того, чтобы записать одну запись (т.е. если размер буфера, установлен- ный системой меньше, чем тот, который указан в FCB). ;---в сегменте данных FCB DB 1, 'NEWDATA ', 25 DUP (0) DTA DB 256 DUP (?) ;---открываем файл и устанавливаем поля FCB MOV AH,0FH ;номер функции LEA DX,FCB ;DS:DX указывают на FCB MOV BX,DX ;копируем смещение для FCB INT 21H ;открываем файл MOV AX,256 ;размер записи MOV [BX]+14,AX ;помещаем в поле размера записи MOV AX,233 ;номер записи MOV [BX]+33,AX ;помещаем в поле номера записи MOV AX,0 ;обнуляем старший байт этого слова MOV [BX]+35,AX ; ;---перенос данных из DTA в файл MOV AH,22H ;номер функции записи с прямым доступом LEA DX,FCB ;DS:DX указывают на FCB INT 21H ;записываем данные CMP AL,0 ;проверка на ошибку JNE WRITE_ERROR ; ;---позднее, закрываем файл LEA DX,FCB ;DS:DX указывают на FCB MOV AH,10H ;функция закрытия файла INT 21H ;закрываем файл CMP AL,0FFH ;проверка на ошибку JE CLOSE_ERROR ; Часто программа работает сразу с набором записей прямого дос- тупа, передавая их в память и из памяти как единое целое. MS DOS предоставляет специальную функцию для этого при использовании метода FCB, называемую запись блока с прямым доступом. Это функ- ция 28H прерывания 21H. При входе DS:DX должны указывать на отк- рытый FCB, в котором поле записи прямого доступа должно быть равно номеру первой из записываемых записей набора. Эта функция совершенно аналогична вышеприведенному примеру. Единственное отличие (кроме номера функции) состоит в том, что в CX должно быть указано число записей в блоке (не путайте эти "блоки" с блоками по 128 записей, с помощью которых система находит требуе- мую запись - программа может читать любое число записей, начиная с любого места). В CX возвращается число реально прочитанных записей. AL будет содержать 0, если все записи успешно записаны, 1 - если не хва- тает пространства на диске, при этом не будет записана ни одна запись. В отличии от функции 22H эта функция автоматически увели- чивает поля текущей записи, текущего блока и записи прямого дос- тупа в FCB, так что они будут указывать на запись, следующую за последней прочитанной. Отметим, что если при выполнении этой функции установить CX = 0, то размер файла будет установлен в соответствии с числом записей, равным полю записи прямого досту- па, и таким образом можно резервировать для файла дисковое прост- ранство. Метод дескриптора файлов: При использовании для доступа метода дескриптора файлов систе- ма не различает последовательные файлы и файлы прямого доступа. Ваша программа должна вычислить позицию в файле, с которой начи- нается требуемая запись, и установить на нее файловый указатель. Файловый указатель позиционируется с помощью функции 42H прерыва- ния 21H. Поместите номер файла в BX, а смещение в файле в CX:DX (CX будет содержать старший байт значения). Затем поместите в AL кодовый номер от 0 до 2. При AL = 0, указатель будет установлен со смещением CX:DX байтов относительно начала файла; при AL = 1, указатель будет установлен со смещением CX:DX относительно теку- щей позиции, а при AL = 2, указатель будет установлен со смеще- нием CX:DX относительно конца файла (т.е. таким образом файл будет расширен). Отрицательные числа недопустимы в качестве сме- щений. При возврате DX:AX будут содержать новое положение указа- теля (старший байт в DX). Если устанавливается флаг переноса, то произошла ошибка. В этом случае AX будет содержать 1, если указан неверный код в AL и 6 - если указан неверный номер файла. После позиционирования файлового указателя запись прямого доступа записывается с помощью той же функции 40H прерывания 21H, которая использовалась для записи в последовательный файл. При входе BX содержит номер файла, а CX - число байтов, которое надо записать. При возврате AX будет содержать число реально записан- ных байтов. Если оно отличается от числа помещенного в CX, то вероятно диск полон (см. [5.1.4]). Как обычно, при возникновении ошибки устанавливается флаг переноса. В этом случае AX будет содержать 5 при ошибке накопителя и 6 - если указан неверный номер файла. Файловый указатель играет ту же роль для образа файла на дис- ке, что DTA для образа файла в памяти. Он может сдвигаться как угодно для доступа к различным частям файла. Будьте внимательны, манипулируя файловым указателем при работе с фалом прямого досту- па, содержимое любого поля любой записи может быть сразу прочита- но с диска и помещено в требуемое место в памяти. ;---в сегменте данных HANDLE DW ? ;номер файла FILEPATH DB 'A:NEWDATA',0 ;строка пути к файлу REC_BUFFER DB 30 DUP (?) ;буфер выводимых записей ;---открываем файл MOV AH,3DH ;номер функции MOV AL,1 ;код открытия для записи LEA DX,FILEPATH ;DS:DX указывают на путь INT 21H ;открываем файл JC OPEN_ERROR ;проверка на ошибку MOV HANDLE,AX ;сохраняем номер файла ;---вычисляем позицию записи и устанавливаем файловый указатель MOV AX,30 ;размер записи 30 байтов MOV CX,54 ;номер записи #54 (55-я запись) MUL CX ;теперь смещение для нее в DX:AX MOV CX,DX ;помещаем старшее слово в DX MOV DX,AX ;помещаем младшее слово в CX MOV AL,0 ;устанавливаем указатель на начало MOV AH,42H ;функция установки указателя MOV BX,HANDLE ;номер файла INT 21H ;устанавливаем указатель JC POINTER_ERROR ;проверка на ошибку ;---пишем запись с прямым доступом MOV AH,40H ;номер функции MOV BX,HANDLE ;номер файла MOV CX,30 ;размер записи LEA DX,REC_BUFFER ;DS:DX указывают на буфер INT 21H ;пишем запись JC WRITE_ERROR ;проверка на ошибку В отличии от метода FCB метод дескриптора файлов не предостав- ляет специальной функции для записи блока записей прямого досту- па. Однако Вашей программе необходимо только вычислить количество байтов, составляющих блок записей, которое должно быть записано. 5.4.6 Чтение из файлов прямого доступа. Чтение файлов прямого доступа является обратным процессом по отношению к их записи. MS DOS вычисляет позицию в файле на диске, затем считывает запись и помещает ее в память. Затем программа должна разделить запись на поля в точности того же размера, кото- рый был использован при конструировании записи. Не забудьте уда- лить символы пробела, добавленные при заподнении полей. Обсужде- ние записи данных в файлы прямого доступа [5.4.5] содержит инфор- мацию, которая поможет Вам лучше понять информацию данного разде- ла. Высокий уровень. Для чтения файла прямого доступа необходимо открыть файл и определить поля записи, как объяснено в разделе, относящемся к записи в файлы прямого доступа. Затем надо использовать оператор GET# для чтения определенной записи с диска. GET #1,23 считывает запись номер #23 из файла, открытого под номером #1. При чтении записи переменной, именованной в операторе FIELD, автоматически присваивается соответствующее значение из записи. Например, если оператор FIELD имеет вид FIELD 1, 20 AS X$, 2 AS Y$, то после выполнения оператора GET 1,23 переменной X$ будет присвоено зна- чение первых 20-ти байтов записи 23, а переменной Y$ - следующих 10-ти байтов. Операторы, аналогичные RSET и LSET для выделения полей данных отсутствуют. В случае числовых полей, напоминаем, что они должны быть преобразованы в строковый вид с помощью функций MKI$, MKS$ и MKD$. Для восстановления их оригинальных значений, с тем чтобы над ними можно было проводить операции и печатать их, надо преоб- разовать эти строки с помощью функций CVI, CVS и CVD. Если Y$ содержит целое число, то для выполнения обратного преобразования запишите Y% = CVI(Y$), при этом переменная Y% будет содержать значение, которое имела переменная перед тем как она была спе- циально обработана для записи в файл прямого доступа. Если Вы выведете строковое значение переменной, то увидите, что это число в интервале от 0 до 65535, закодированное в два символа ASCII. В данном примере открывается файл, созданный в примере пункта [5.4.5], и выводится содержимое любой из затребованных записей: 100 OPEN "A:NEWDATA" AS #1 LEN = 24 'открываем файл 110 FIELD 1, 18 AS LASTNAME$, 2 AS AGE$, 4 AS WEIGHT$ 120 CLS: INPUT "What is the record number";R 'запрос записи 130 IF R*24 > LOF(1) THEN BEEP: PRINT"No such record": GOTO 120 140 GET #1,R 'читаем запись из файла 150 PRINT LASTNAME$, CVI(AGE$), CVS(WEIGHT$) 'выводим ее 160 PRINT: PRINT "Do another (y/n)?" 'будем повторять? 170 C$ = INKEY$: IF C$ = "" THEN 170 'ожидаем ввода 180 IF C$ = "y" OR C$ = "Y" THEN 120 'повторяем, если надо 190 CLOSE 'иначе закрываем файл Средний уровень. Метод FCB доступа к файлам имеет две функции для чтения запи- сей с прямым доступом. С другой стороны, метод дескриптора файлов использует ту же функцию, что и для чтения последовательных фай- лов. Два метода доступа рассматриваются отдельно. Метод FCB: Функция 21H прерывания 21H читает одну запись из файла прямого доступа. Вторая функция, 27H, читает блок последовательных запи- сей. Создайте управляющий блок файла, как показано в [5.3.5] и откройте его [5.3.3]. После того как FCB открыт, введите в него значения полей размера записи (DW по смещению 14) и номера записи прямого доступа (DD по смещению 33). Если DS:DX указывают на первый байт FCB, то можно вызывать функцию 21H для чтения записи и запись будет помещена в паямть, начиная с первого байта DTA. Если запись успешно прочитана, то в AL будет возвращен 0. Однако при этом нет гарантии, что чтение прошло без ошибок, пос- кольку неверный размер записи может привести к тому, что части прилегающих записей будут считаны, как будто это одна запись. Если запрошена запись с номером большим, чем число записей в файле, то в AL будет возвращено 1 или 3. Если был возвращен код 3, то был считан самый конец файла и была прочитана часть записи данных. Если был возвращен код 1, то данные вообще не были счита- ны. Данный пример считывает одну запись и помещает ее в DTA: ;---в сегменте данных FCB DB 1,'OLDDATA ', 25 DUP (0) ;---открываем файл и устанавливаем поля FCB MOV AH,0FH ;номер функции LEA DX,FCB ;DS:DX указывают на FCB MOV BX,DX ;копируем смещение FCB INT 21H ;открываем файл MOV AX,55 ;размер записи 55 байтов MOV [BX]+14,AX ;помещаем в поле размера записи MOV AX,22 ;номер записи для чтения MOV [BX]+33,AX ;помещаем в поле номера записи MOV AX,0 ;обнуляем старшее слово этого поля MOV [BX]+35,AX ; ;---перенос данных из файла в DTA MOV AH,21H ;номер функции чтения с прямым доступом LEA DX,FCB ;DS:DX указывают на FCB INT 21H ;читаем данные, помещая их в DTA CMP AL,0 ;проверка на ошибку JNE READ_ERROR ; ;---позднее, закрываем файл MOV AH,10 ;номер функции закрытия файла LEA DX,FCB ;DS:DX указывают на FCB INT 21H ;закрываем файл Для чтения блока последовательных записей в память за один прием надо использовать функцию 27H прерывания 21H. Ее выполнение подготавливается в точности так же, как и функции 21H, за исклю- чением того, что вдобавок CX должен содержать число записей кото- рые надо прочитать за один прием. При возврате CX будет содержать число реально прочитанных записей. Значения возвращаемые в AL совпадают с теми, которые возвращаеются функцией 21H. В отличии от функции 21H поля FCB, в которых хранится информация о положе- нии записи (поле записи прямого доступа, текущего блока и текущей записи) автоматически увеличиваются, с тем чтобы они указывали на следующую несчитанную запись после выполнения функции. Отметим, что как в случае чтения одной, так и в случае чтения нескольких записей, поля текущего блока и текущей записи FCB устанавливаются по значению поля записи прямого доступа. Если Вы знаете значение текущего блока и текущей записи, а не соответст- вующий номер записи прямого доступа, то используйте функцию 24H прерывания 21H, чтобы она проделала вычисления за Вас. У этой функции нет входных регистров, надо только, чтобы DS:DX указывали на открытый FCB. При возврате поле записи прямого доступа будет заполнено значением, соответствующим установке двух других полей. Метод дескриптора файлов: В предыдущем разделе показано, как писать записи прямого дос- тупа с помощью метода дескриптора файлов. Процедура чтения из файла с прямым доступом подготавливается совершенно аналогичным образом, путем вычисления смещения в файле, на которое должен указывать файловый указатель. DS:DX должны указывать на буфер, в который будет помещена запись, после чего надо выполнить функцию 3FH прерывания 21H. При входе CX должен содержать размер записи, а BX - номер файла. ;---в сегменте данных HANDLE DB ? FILEPATH DB 'A:OLDDATA',0 REC_BUFFER DB 30 DUP(?) ;---открываем файл MOV AH,3DH ;номер функции MOV AL,0 ;код открытия для чтения LEA DX,FILEPATH ;DS:DX указывают на путь к файлу INT 21H ;открываем файл JC OPEN_ERROR ;проверка на ошибку MOV HANDLE,AX ;запоминаем номер файла ;---вычисляем позицию записи и устанавливаем файловый указатель MOV AX,30 ;размер записи MOV CX,54 ;читаем запись #54 (55-ю запись) MUL CX ;смещение записи в DX:AX MOV CX,DX ;помещаем старшее слово смещения в DX MOV DX,AX ;помещаем младшее слово смещения в CX MOV AL,0 ;устанавливаем указатель на начало файла MOV AH,42H ;функция установки указателя MOV BX,HANDLE ;номер файла INT 21H ;устанавливаем указатель JC POINTER_ERROR ;обработка ошибки ;---читаем запись с прямым доступом MOV AH,3FH ;номер функции MOV BX,HANDLE ;номер файла MOV CX,30 ;размер записи LEA DX,REC_BUFFER ;DS:DX указывают на буфер для записи INT 21H ;читаем запись JC READ_ERROR ;обработка ошибки ;---позднее, закрываем файл MOV BX,HANDLE ;номер файла MOV AH,3EH ;функция закрытия файла INT 21H ;закрываем файл JC CLOSE_ERROR ;проверка на ошибку 5.4.7 Проверка данных после операций чтения/записи. MS DOS может проверять правильность производимого обмена с диском прямо во время обмена. Ошибки происходят настолько редко, что средства проверки обычно не используются, чтобы не замедлять обмен с диском. Однако, если это необходимо, то имеется два спо- соба проверки. Один состоит во включении команды VERIFY = ON в файл CONFIG.SYS, который автоматически читается при загрузке операционной системы. Впоследствии, все дисковые операции будут проверяться. Это единственный способ проверки доступный в Бейси- ке. Второй метод состоит использовании специальной функции DOS для верификации только критических дисковых операций. Если проце- дура верификации обнаруживает ошибку, то она сообщает об условии критической ошибки, как описано в [7.2.5]. Средний уровень. Функция 2EH прерывания 21H включает и выключает проверку. Поместите в AL 1 - для включения верификации и 0 - для выключе- ния. DL также должно быть равно 0. Затем надо выполнить прерыва- ние. У этой функции нет выходных регистров. ;---включение верификации MOV AL,1 ;номер кода MOV DL,0 ;необходимый входной регистр MOV AH,2EH ;номер функции INT 21H ;включаем проверку Для определения текущего режима верификации надо вызвать функ- цию 54H прерывания 21H. У нее нет входных регистров. При возврате AL = 1, если проверка включена и AL = 0, если выключена. 5.4.8 Определение дисковых ошибок и восстановление после них. Дисковые операции настолько сложны, что имеется большое коли- чество возможных ошибок. Большинство дисковых ошибок обсуждаются вместе с операциями, при которых они могут происходить. В данном разделе они собраны вместе, чтобы помочь Вам при разработке про- цедуры общего назначения для восстановления после дисковых оши- бок. Дисковые ошибки бывают двух типов, которые мы будем называть мягкими (soft) и жесткими (hard). Мягкие ошибки возникают из-за неправильного запроса на доступ к файлу: запрошенный файл может отсутствовать или дисковое пространство может кончиться прежде, чем будет записан весь файл. С другой стороны, жесткие ошибки возникают при неверных последовательностях или временных несоот- ветствий при дисковых операциях, которые могут быть следствием неверного выравнивания или проблем с накопителем. В этом случае, лучше всего произвести сброс диска перед обработкой. Высокий уровень. В [7.2.5] объяснено как подготовить процедуру обработки оши- бок. Оператор ON ERROR GOSUB заставляет программу перейти на процедуру обработки ошибки при возникновении критической ошибки. Процедура прежде всего определяет кодовый номер ошибки в Бейсике, который для дисковых ошибок может быть одним из следующих: 52 Bad file number. (Неверный номер файла.) Файл не отк- рыт под тем номером, к которому идет обращение (#1, #2 и т.д.) 53 File not found. (Файл не найден.) Используется при выполнении операторов LOAD, KILL, NAME, FILES и OPEN. 54 Bad file mode. (Неверный режим доступа.) Попытка дос- тупа к файлу другим образом, по сравнению с тем, для чего он был открыт, например, попытка записи в после- довательный файл, открытый для чтения. 55 File already open. (Файл уже открыт.) Попытка открыть файл, который уже открыт, или уничтожить (KILL) файл, который еще не закрыт. 58 File already exists. (Файл уже существует.) Попытка переименовать файл (с помощью NAME) на имя, которое уже есть в каталоге. 61 Disk full. (Диск полон.) См. специальное обсуждение в [5.1.4], относящееся к этой ошибке. 62 Input past end. (Чтение за концом файла.) Попытка прочитать из последовательного файла больше перемен- ных, чем он содержит. Чтобы избежать этой ошибки ис- пользуйте функцию EOF, как объяснено в [5.4.4]. 63 Bad record number. (Неверный номер записи.) Попытка прочитать или записать запись с номером большим, чем число записей в файле. 64 Bad file name. (Неверное имя файла.) Используется операторами KILL, NAME и FILES. 67 Too many files. (Слишком много файлов.) В каталоге больше нет места для записи информации о файлах. Дру- гой возможный вариант состоит в том, что открытие еще одного файла приведет к тому, что будет превышено максимально допустимое число одновременно открытых файлов. 70 Disk is write-protected. (Диск защищен от записи.) 71 Disk is not ready. (Диск не готов.) Наиболее вероятно, не закрыт дисковод с дискетой. 72 Disk media error. (Диск поврежден.) Как правило, это сообщение выдается при повреждении дискеты, однако иногда оно появляется при сбоях оборудования. 74 Specified wrong disk in RENAME operation. (Указан неверный диск в операции RENAME.) 75 Path/file access error. (Ошибка доступа к файлу.) Попытка открыть подкаталог или метку тома, как файл. Или попытка писать в файл, который защищен от записи. Эта ошибка чаще всего выдается при попытке удалить текущий каталог. Появляется при операциях OPEN, NAME, MKDIR, CHDIR и RMDIR. 76 Path not found. (Путь не найден.) Неправильно указан путь или его не существует. Появляется при операциях OPEN, MKDIR, CHDIR и RMDIR. После того как процедура распознала ошибку, необходимо инфор- мировать об ошибке пользователя. Когда пользователь сообщит, что причина ошибки устранена, то оператор RESUME посылает программу назад на ту строку, где произошла ошибка. Оператор RESUME может сопровождаться номером строки, поэтому программа может вернуться к началу всей последовательности дисковых операций, независимо от того, в какой строке произошла ошибка (отметим, что файлы не закрываются при возникновении ошибки). В следующем примере прог- рамма позволяет восстановить ситуацию после ошибок, связанных с переполнением диска и защитой от записи: 100 ON ERROR GOSUB 5000 ' . . 600 ''' . . 5000 ''' 5010 IF ERR = 61 PRINT "Disk full": GOTO 5100 5020 IF ERR = 70 PRINT "Disk is write protected": GOTO 5100 . . 5100 PRINT "Correct the problem, then strike any key" 5110 C$ = INKEY$: IF C$ = "" THEN 5110 5120 RESUME 600 Средний уровень. Функция 1 прерывания 13H возвращает в AL байт, дающий статус дискового накопителя. Значение его битов следующее: биты 0-1 01 = неверная команда, или, если бит 3 = 1, то попытка обмена данными за границей 64K 10 = адресная метка не найдена 11 = попытка записи на защищенный от записи диск 2 1 = указанный сектор не найден 3 1 = переполнение DMA (потеря данных при обмене), или, если бит 0 = 1, то попытка обмена дан- ными за границей 64K 4 1 = данные прочитаны неверно, надо повторить 5 1 = ошибка контроллера 6 1 = ошибка операции поиска 7 1 = нет ответа от накопителя (тайм-аут) Каждая из функций обращения к диску MS DOS использует только некоторые из возможных кодов ошибок, а некоторые функции не сооб- щают об ошибке. Однако во всех случаях при возникновении ошибки устанавливается флаг переноса. Если произошла ошибка, то номер кода этой ошибки возвращается в AX. Вот коды, относящиеся к дис- ковым операциям: 1 Неверный номер функции 2 Файл не найден 3 Путь не найден 4 Уже открыто максимально допустимое число файлов 5 Отрицание доступа (ошибка оборудования) 6 Неверный номер файла 15 Указан неверный накопитель 16 Попытка удалить текущий каталог 17 Не то же устройство 18 Больше нет файлов (при поиске в каталоге с использова- нием джокеров) Восстановление после этих "мягких" ошибок несложно. Некоторые предупреждают Вас о программных ошибках. Другие возникают из-за ошибочных действий пользователя. Если же не отвечает сам накопи- тель, то произошла критическая ошибка. В разделе [7.2.5] показано как написать процедуру обработки критических ошибок. В MS DOS 3.0 введены расширенные коды ошибок. Они могут быть получены с помощью функции 59H прерывания 21H, когда флаг перено- са индицирует возникновение ошибки. Обсуждение этого вопроса см. в [7.2.5]. Глава 6. Принтер. Раздел 1. Управление работой принтера. MS DOS может работать с тремя параллельными устройствами (LPT1 - LPT3) и в этой главе показано как управлять ими. Последователь- ные принтеры управляются в точности так же, как и параллельные, за исключением способа, которым данные посылаются на принтер; эта информация приведена в разделе 1 главы 7. Каждое параллельное устройство имеет свой адаптер. Адаптер управляется тремя регист- рами ввода/вывода и адреса портов этих регистров различны для каждого адаптера. Область данных BIOS содержит базовые адреса для каждого адаптера. Базовый адрес соответствует младшему адресу группы из трех адресов портов. Базовый адрес для LPT1 - 0040:0008, для LPT2 - 0040:000A и т.д. Какой адаптер назначен какому номеру LPT - не определено , как видно из нижеприведенной таблицы. По этой причине программа, котрая прямо адресуется в параллельный порт, должна выискивать адреса, которые он исполь- зует. Отметим, что при инициализации базовому адресу присваивает- ся значение 0, когда соответствующий адаптер не установлен. Адаптер Выходных данных Статуса Управления Монохромная карта (PC/XT/AT) 3BCH 3BDH 3BEH Адаптер принтера PC/XT Адаптер принтера PCJr 378H 379H 37AH Последовательная/параллельная карта AT (установленная как LPT1) Последовательная/параллельная 278H 279H 27AH карта AT (установленная как LPT2) Регистр выходных данных - это тот адрес порта, через который проходит каждый байт данных, посылаемый в принтер. Регистр стату- са сообщает различную информацию о принтере; процессор может постоянно опрашивать его, чтобы распознать момент, когда все в порядке и можно посылать данные. Регистр статуса сообщает также, что произошла ошибка на принтере. Регистр управления инициализи- рует адаптер и управляет выводом данны