пользуются при форматировании диска. MS DOS хранит четыре техни-
ческих параметра в памяти, в специальной таблице параметров,
называемой базой диска (disk base). Вектор прерывания 1EH указы-
вает на эту таблицу. Четыре значения хранятся в том порядке, в
котором они должны быть переданы контроллеру НГМД, начиная со
смещения 3. В следующей таблице показана командная последователь-
ность для трех операций, используемых в нижеприведенном примере.
В цепочках битов черех X обозначены биты, значение которых несу-
щественно, через H - номер головки, а через DD - номер накопите-
ля.

Операция # байта Функция Установка для головки 0
дорожки 15, сектора 1

Поиск 1 номер кода 00001111 1FH
2 головка и накопитель 00H
XXXXXHDD

Чтение 1 номер кода 01100110 66H
сектора 2 головка и накопитель 00H
XXXXXHDD
3 номер дорожки 0FH
4 номер головки 00H
5 номер сектора 01H
6 байтов в секторе 02H
7 конец дорожки 09H
8 длина сдвига 1AH
9 длина данных FFH

Запись 1 номер кода 01000101 45H
сектора 2-9 те же, что и для чтения сектора


Вы должны быть уверены, что контроллер НГМД готов прежде чем
Вы пошлете или прочитаете байт из регистра данных. Биты 7 и 6
регистра статуса предоставляют эту информацию. Вот значение битов
этого регистра:

биты 3-0 1 = накопитель D-A в режиме поиска
4 1 = контроллер НГМД выполняет команду чтения/записи
5 1 = контроллер НГМД не в режиме DMA
6 1 = регистр данных контроллер НГМД готов к приему
данных
0 = готов к посылке данных
7 1 = контроллер НГМД готов к посылке или приему данных

Перед началом дисковых операций неплохо проверить, что бит 6
равен нулю, индицируя что контроллер НГМД ожидает команду. Если
он ожидает посылки данных, то произошла ошибка. Когда байт данных
посылается в регистр данных, то бит 7 регистра статуса становится
равным нулю; продолжайте чтение регистра до тех пор, пока бит не
изменится обратно на 1, а затем посылайте следующий байт команды.
Аналогично, проверяйте этот бит статуса перед чтением байта ста-
туса в фазе результата. Нижеприведенный пример кончается двумя
процедурами, которые выполняют эти функции.
Когда операция поиска завершена, то контроллер НГМД инициирует
прерывание 6, прерывание от НГМД. Хотя так же просто можно узнать
об окончании операции поиска проверяя регистр статуса, в примере
это делается за счет обработки прерывания. Когда происходит пре-
рывание, то обработчик прерывания BIOS устанавливает бит 7 байта
статуса поиска в области данных BIOS, расположенного по адресу
0040:003E. Это единственный результат обработки прерывания. Можно
проверять этот байт до тех пор, пока бит 7 не будет установлен, а
затем переходить к следующему шагу операции чтения сектора.
Следующий шаг состоит в инициализации микросхеиы прямого дос-
тупа к памяти 8237. Эта микросхема занимается обменом данных
между периферийными устройствами и памятью, работой, которой
может заниматься также процессор. На самом деле, в PCjr, где нет
микросхемы DMA, контроллер НГМД посылает данные прямо в процес-
сор, который в свою очередь пересылает их в память. Тактовая
частота процессора адекватна этой задаче, однако при пересылке
данных все прерывания должны быть запрещены, с тем чтобы не
происходило потери данных. Это означает, что в PCjr при передаче
данных ввод с клавиатуры или из модема запрещен. Прерывания тай-
мера также игнорируются, однако впоследствии счетчик времени
суток обновляется специальной процедурой, использующей канал 1
микросхемы таймера 8253 для подсчета импульсов, прошедших за
время дисковых операций. Все остальные модели IBM PC имеют мик-
росхему DMA, поэтому процессор свободен при передаче данных.
IBM PC и XT используют 4-хканальную микросхему DMA 8237. Канал
0 предназначен для "освежения" памяти (memory refresh); он пос-
тоянно восстанавливает заряд ячеек оперативной памяти. Если Вы
будете работать по этому каналу, то это приведет скорее всего к
краху машины. Канал 2 предназначен для дисковых операций, а два
другие канала, с номерами 1 и 3, доступны (через разъемы расшире-
ния) для дополнительного оборудования. К сожалению, обмен па-


мять-память требует двух каналов и одним из них должен быть канал
0, поэтому такой обмен недоступен на IBM PC и XT. Однако AT имеет
7 каналов прямого доступа к памяти и DMA автоматически исполь-
зуется инструкциями MOVS, существенно увеличивая производитель-
ность.
Перед инициализацией канала программа должна послать в микрос-
хему код, сообщающий будет ли происходить чтение или запись в
контроллер НГМД. Этот однобайтный код равен 46H для чтения и 4AH
- для записи. Этот код должен быть послан в каждый из двух портов
с адресами 0BH и 0CH.
Каждый канал микросхемы 8237 использует три регистра. Один
16-битный регистр, регистр счетчика, содержит число передаваемых
байтов данных. Его величина должна быть на единицу меньше, чем
требуемое число байтов. Для канала 2 доступ к этому регистру
осуществляется через порт 05H; пошлите в него два последователь-
ных байта, причем сначала младший байт.

Остальные два регистра содержат адрес буфера в памяти, с кото-
рым будет происходить обмен данными. Этот адрес задается как
20-битное число, поэтому, например, адрес 3000:ABCD задается как
3ABCD. Младшие 16 битов посылаются в регистр адреса, который для
канала 2 имеет адрес порта 04H. Сначала посылается младший байт.
Старшие 4 бита идут в регистр страницы, который для канала 2
имеет адрес порта 81H. Когда байт посылается по этому адресу, то
имеют значение только 4 младших бита. Если буфер создается в
сегменте данных, то Вам нужно сложить значение DS и смещение
буфера для получения 20-битного значения. Сложение может привести
к переносу в значение регистра страницы. Например, если DS равен
1F00H, а смещение буфера - 2000H, то результирующий адрес будет
равен 1F00 + 2000 = 21000H.
После того как эти три регистра установлены, пошлите 2 в порт
с адресом 0AH, чтобы разрешить канал 2. Это оставляет микросхему
DMA в состоянии ожидания данных от накопителя, а программа должна
немедленно начать посылку командных байтов в контроллер НГМД. Вот
краткий перечень шагов при программировании микросхемы 8237:

1. Послать код чтения или записи.
2. Вычислить 20-битный адрес памяти буфера, в который будут пос-
ланы данные, и заслать его в регистры адреса и страницы канала 2.
3. Поместить значение числа передаваемых байтов (минус 1) в ре-
гистр счетчика канала 2.
4. Разрешить канал.

После посылки командных байтов, снова ожидайте прерывания и
обращайтесь с ним так же, как и после операции поиска. Затем
прочитайте байты статуса. Они таковы:

Операция # байта Функция

Поиск нет

Чтение 1 байт статуса 0
2 байт статуса 1


3 байт статуса 2
4 номер дорожки
5 номер головки
6 номер сектора
7 код байтов на сектор (0-3)

Запись 1-7 то же, что и для чтения

Вот значения битов трех байтов статуса:

Байт статуса 0:
биты 7-6 00 = нормальное завершение
01 = начато выполнение, не может завершиться
10 = неверная команда
11 = невыполнено, т.к. накопитель не подключен
5 1 = выполняется операция поиска
4 1 = ошибка накопителя
3 1 = накопитель не готов
2 номер выбранной головки
1-0 номер выбранного накопителя

Байт статуса 1:
бит 7 1 = номер затребованного сектора больше максимума
6 не используется (всегда 0)
5 1 = ошибка передачи данных
4 1 = переполнение данных
3 не используется (всегда 0)
2 1 = не может найти или прочитать сектор
1 1 = не может записать из-за защиты от записи
0 1 = отсутствует адресная метка при форматизации

Байт статуса 2:
бит 7 не используется (всегда 0)
6 1 = встречена адресная метка удаленных данных
5 1 = ошибка циклического контроля четности данных
4 1 = проблема с идентификацией дорожки
3 1 = условие команды сканирования удовлетворено
2 1 = условие команды сканирования не удовлетворено
1 1 = плохая дорожка
0 1 = отсутствует адресная метка

Как Вы видите большая часть информации относится к форматиро-
ванию диска, которое нас в настоящий момент не интересует. Однако
имеется еще четвертый байт статуса, который содержит полезную
информацию:

Байт статуса 3:
бит 7 1 = ошибка накопителя
6 1 = диск защищен от записи
5 1 = накопитель готов
4 1 = текущая позиция головки известна
3 1 = дискета двухсторонняя
2 номер выбранной головки
1-0 номер выбранного накопителя


Вы можете получить этот четвертый байт статуса, послав контролле-
ру НГМД команду "Определи статус накопителя" (Sense Drive Sta-
tus). Первый байт этой двухбайтной команды это число 4, а второй
байт содержит номер накопителя в битах 1 и 0, и номер головки в
бите 2. Единственным результатом этой операции является байт
статуса 3. Отметим, что после каждой дисковой операции, если Вы
используете процедуры DOS или BIOS, результирующие байты статуса
помещаются в область данных BIOS, начиная с адреса 0040:0042.
Операционная система хранит также байт статуса дискеты по адресу
0040:0041, значение битов которого следующее:

Значение бита Ошибка

80H нет ответа на присоединение накопителя
40H операция поиска неуспешна
20H ошибка контроллера НГМД
10H ошибка данных при чтении (ошибка CRC)
09H попытка прямого доступа за границу 64K
08H переполнение DMA
04H затребованный сектор не найден
02H не найдена адресная марка
01H послана неверная команда контроллеру НГМД

В заключение приводим полную процедуру чтения диска, которая
читает один сектор данных с дорожки 12, сектор 1, сторона 0 нако-
пителя A в 512-байтный буфер в сегменте данных. Семь байтов ста-
туса также считываются в отведенный буфер. Эта процедура предназ-
начена для IBM PC и XT. Вам необходимо воспользоваться техничес-
ким руководством по PCjr или AT, если Вы работаете на этих маши-
нах. На AT надо изменить циклы задержки, чтобы учесть большую
скорость процессора, и не забывать добавлять оператор JMP SHORT
$+2 между последовательными командами OUT, относящимися к одному
и тому же порту. Работа с фиксированным диском осуществляется
аналагично, поэтому Вы можете перенести изученные Вами концепции
на другие ситуации.

;---в сегменте данных
BUFFER DB 512 DUP(?)
STATUS_BUFFER DB 7 DUP(?)

SECTOR_READ PROC ;начало процедуры чтения одного сектора
;---включение мотора
STI ;прерывания должны быть разрешены
MOV DX,3F2H ;адрес регистра цифрового вывода
MOV AL,28 ;устанавливаем биты 2, 3 и 4
OUT DX,AL ;посылаем команду
;---ожидаем пока мотор наберет скорость (около 1/2 сек.)
MOV CX,3500 ;счетчик цикла задержки (для IBM PC и XT)
MOTOR_DELAY: LOOP MOTOR_DELAY ;ожидаем 1/2 секунды
;---выполняем операцию поиска
MOV AH,15 ;номер кода
CALL OUT_FDC ;посылаем контроллеру НГМД
MOV AH,0 ;номер накопителя


CALL OUT_FDC ;посылаем контроллеру НГМД
MOV AH,12 ;номер дорожки
CALL OUT_FDC ;посылаем контроллеру НГМД
CALL WAIT_INTERRUPT ;ожидаем прерывания от НГМД
;---ожидаем установки головки (25 мсек.)
MOV CX,1750 ;счетчик цикла задержки (для IBM PC и XT)
WAIT_SETTLE: LOOP WAIT_SETTLE ;ожидаем 25 мсек.
;---начинаем инициализацию микросхемы DMA
MOV AL,46H ;код чтения данных контроллера НГМД
OUT 12,AL ;посылаем код по двум адресам
OUT 11,AL ;
;---вычисляем адрес буфера
MOV AX,OFFSET BUFFER ;берем смещение буфера в DS
MOV BX,DS ;помещаем DS в BX
MOV CL,4 ;готовим вращение старшего нибла
ROL BX,CL ;вращаем младшие 4 бита
MOV DL,BL ;копируем DL в BL
AND DL,0FH ;чистим старший нибл в DL
AND BL,0F0H ;чистим младший нибл в BX
ADD AX,BX ;складываем
JNC NO_CARRY ;если не было переноса, то # страницы в DL
INC DL ;увеличиваем DL, если был перенос
NO_CARRY: OUT 4,AL ;посылаем младший байт адреса
MOV AL,AH ;сдвигаем старший байт
OUT 4,AL ;посылаем младший байт адреса
MOV AL,DL ;засылаем номер страницы
OUT 81H,AL ;посылаем номер страницы

;---конец инициализации
MOV AX,511 ;значение счетчика
OUT 5,AL ;посылаем младший байт
MOV AL,AH ;готовим старший байт
OUT 5,AL ;посылаем старший байт
MOV AL,2 ;готовим разрешение канала 2
OUT 10,AL ;DMA ожидает данные
;---получаем указатель на базу диска
MOV AL,1EH ;номер вектора, указывающего на таблицу
MOV AH,35H ;номер функции
INT 21H ;выполняем функцию
;---посылаем параметры чтения
MOV AH,66H ;код чтения одного сектора
CALL OUT_FDC ;посылаем контроллеру НГМД
MOV AH,0 ;номера головки и накопителя
CALL OUT_FDC ;посылаем контроллеру НГМД
MOV AH,12 ;номер дорожки
CALL OUT_FDC ;посылаем контроллеру НГМД
MOV AH,0 ;номер головки
CALL OUT_FDC ;посылаем контроллеру НГМД
MOV AH,1 ;номер записи
CALL OUT_FDC ;посылаем контроллеру НГМД
MOV AH,ES:[BX]+3 ;код размера сектора
CALL OUT_FDC ;посылаем контроллеру НГМД
MOV AH,ES:[BX]+4 ;номер конца дорожки


CALL OUT_FDC ;посылаем контроллеру НГМД
MOV AH,ES:[BX]+5 ;длина сдвига
CALL OUT_FDC ;посылаем контроллеру НГМД
MOV AH,ES:[BX]+6 ;длина данных
CALL OUT_FDC ;посылаем контроллеру НГМД
CALL WAIT_INTERRUPT ;ожидаем прерывание от НГМД
;---читаем результирующие байты
MOV CX,7 ;берем 7 байтов статуса
LEA BX,STATUS_BUFFER ;помещаем в буфер статуса
NEXT: CALL IN_FDC ;получаем байт
MOV [BX],AL ;помещаем в буфер
INC BX ;указываем на следующий байт буфера
LOOP NEXT ;повторяем операцию
;---выключение мотора
MOV DX,3F2H ;адрес регистра цифрового вывода
MOV AL,12 ;оставляем биты 3 и 4
OUT DX,AL ;посылаем новую установку
RET ;конец процедуры
SECTOR_READ ENDP

WAIT_INTERRUPT PROC ;ожидание прерывания от НГМД
;---управление статусом прерывания 6 в байте статуса BIOS
MOV AX,40H ;сегмент области данных BIOS
MOV ES,AX ;помещаем в ES
MOV BX,3EH ;смещение для байта статуса
AGAIN: MOV DL,ES:[BX] ;получаем байт
TEST DL,80H ;проверяем бит 7
JZ AGAIN ;до тех пор пока не установлен
AND DL,01111111B ;сбрасываем бит 7
MOV ES:[BX],DL ;заменяем байт статуса
RET
WAIT_INTERRUPT ENDP

OUT_FDC PROC ;посылаем байт из AH FDC
MOV DX,3F4H ;адрес порта регистра статуса
KEEP_TRYING: IN AL,DX ;получаем значение
TEST AL,128 ;бит 7 установлен?
JZ KEEP_TRYING ;если нет, то снова проверяем
INC DX ;указываем на регистр данных
MOV AL,AH ;передаваемое значение в AH
OUT DX,AL ;посылаем значение
RET
OUT_FDC ENDP

IN_FDC PROC ;получаем байт от FDC в AL
MOV DX,3F4H ;адрес порта регистра статуса
ONCE_AGAIN: IN AL,DX ;получаем значение
TEST AL,128 ;бит 7 установлен?
JZ KEEP_TRYING ;если нет, то проверяем снова
INC DX ;указываем на регистр данных
IN AL,DX ;читаем байт из регистра данных
RET
IN_FDC ENDP



    5.4.2 Чтение/запись определенных секторов.



Чтение или запись определенных секторов диска в основном ис-
пользуется при доступе к каталогам диска или его таблице размеще-
ния файлов, сектора для которых всегда расположены в одном и том
же месте. В то время как чтение секторов достаточно безобидно,
запись абсолютного сектора требует чтобы код был тщательно прове-
рен перед первым использованием. Ошибка может сделать каталог или
таблицу размещения файлов нечитаемыми, что эквивалентно разруше-
нию всех данных на диске.
Как DOS так и BIOS предоставляют функции для чтения и записи
определенных секторов. Однако они указывают сектора по-разному.
Для IBM PC, XT и PCjr процедура BIOS требует информации о номере
стороны (0 или 1), номере дорожки (0-39) и номере сектора (1-8).
Из-за ограничения максимального номера сектора равного 8 этот
метод практически бесполезен для этих машин. Однако для AT номер
сектора может меняться до 8, 9 или 15, а число дорожек может
меняться до 39 или 79. Функции DOS указывают сектор одним номе-
ром, который называется логическим номером сектора. Начиная с
наружного обода диска, секторам присваиваются последовательно
возрастающие номера. Этот метод может быть использован для дисков
произвольного размера и типа.
Отсчет логисеких секторов начинается со стороны 0 дорожки 0
сектора 1 и продолжается на стороне 1 с дорожки 0, после чего
переходит на сторону 0 дорожку 1 и т.д. (На больших фиксированных
дисках сначала проходится весь внешний цилиндр.) В зависимости от
того как был форматирован диск, при переходе на следующую дорожку
логический номер сектора увеличивается на определенную величину.
Для дискет емкостью 360K каждая дорожка (с учетом обеих сторон)
добавляет к логическому номеру 18. Однако вычисления немного
усложняются тем, что отсчет начинается с нуля. Таким образом
первый сектор на дорожке 3 стороны 2 должен иметь номер равный
3*18 для дорожек 0-2 плюс 9 для стороны 0 дорожки 3 плюс единица,
указывающая на первый сектор дорожки 3 стороны 1. Эта сумма равна
64. Логический номер сектора на 1 меньше этого числа. На рис. 5-4
сравнивается методы указания сектора DOS и BIOS.

Высокий уровень.

Бейсик не предоставляет прямого доступа к секторам диска. Надо
использовать следующую процедуру на машинном языке. В приложении
Г объясняется логика взаимодействия с этой процедурой. В примере
читается 9 секторов дорожки 3 стороны 1 дискеты емкостью 360K.
Сама процедура размещается в памяти, начиная с адреса сегмента
&H1000, а содержимое секторов размещается, начиная с сегментного
адреса &H2000 (напоминаем, что абсолютный адрес равен сегментному
адресу, умноженному на 16). Для того чтобы записать на диск со-
держимое этого буфера надо изменить в данных программы седьмой
байт с конца &H25 на &H26. Все остальное остается неизменным.

100 DEFINT A-Z 'все переменные будут целыми
110 DATA &H55, &H8B, &HEC, &H1E, &H8B, &H76, &H0C, &H8B
120 DATA &H04, &H8B, &H76, &H0A, &H8B, &H14, &H8B, &H76


130 DATA &H08, &H8B, &H0C, &H8B, &H76, &H06, &H8A, &H1C
140 DATA &H8E, &HD8, &H8B, &HC3, &H8B, &H00, &H00, &HCD
150 DATA &H25, &H59, &H1F, &H5D, &HCA, &H08, &H00

160 DEF SEG = &H1000 'помещаем процедуру по адресу &H10000
170 FOR N = 0 TO 38 'для каждого байта процедуры
180 READ Q: POKE N,Q 'читаем байт и помещаем его в память
190 NEXT 'следующий байт
200 READSECTOR = 0 'выполняем код, начиная с первого байта
210 BUFFER = &H2000 'буфер для данных имеет адрес &H20000
220 LOGICALNUMBER = 62 'логический номер сектора равен 62
230 NSECTORS = 9 'число считываемых секторов
240 DRIVE = 0 'номер накопителя (0 = A)
250 CALL READSECTOR (BUFFER, LOGICALSECTORS, NSECTORS, DRIVE)
260 'теперь сектора в памяти, начиная с адреса 2000:0000

Средний уровень.

BIOS использует функцию 2 прерывания 13H для чтения секторов и
функцию 3 прерывания 13H для записи секторов. В обоих случаях DL
должен содержать номер накопителя от 0 до 3, где 0 = A, 1 = B и
т.д., DH - номер головки (стороны), 0-1. CH должен содержать
номер дорожки от 0 до 39, а CL - номер сектора от 0 до 8. AL
содержит число секторов, которое необходимо считать. Допускается
сразу читать не более восьми секторов, что более чем достаточно
для большинства целей. ES:BX должны указывать на начало буфера в
памяти, куда будут помещаться данные или откуда они будут брать-
ся. При возврате AL будет содержать число прочитанных или запи-
санных секторов. Если операция успешна, то флаг переноса будет
равен нулю. Если он равен 1, то AH будет содержать байт статуса
дисковой операции, описанный в [5.4.8].

;---в сегменте данных
BUFFER DB 4000 DUP(?) ;создаем буфер

;---читаем сектора
MOV AX,SEG BUFFER ;ES:BX должны указывать на буфер
MOV ES,AX ;
MOV BX,OFFSET BUFFER ;
MOV DL,0 ;номер накопителя
MOV DH,0 ;номер головки
MOV CH,0 ;номер дорожки
MOV CL,1 ;номер сектора
MOV AL,1 ;число секторов для чтения
MOV AH,2 ;номер функции чтения
INT 13H ;

Прерывания DOS 25H и 26H читают и записывают абсолютные секто-
ра диска, соответственно. Надо поместить логический номер старто-
вого сектора в DX, а DS:BX должны указывать на буфер. CX содержит
число секторов для чтения или записи, а AL - номер накопителя,
где 0 = A, 1 = B и т.д. Процедуры портят все регистры, кроме


сегментных. При возврате регистр флагов остается на стеке, остав-
ляя стек невыровненным. Не забудьте вытолкнуть это значение со
стека сразу после возврата (в примере это значение выталкивается
в CX).

;---в сегменте данных
BUFFER DB DUP 5000(?) ;создаем буфер

;---читаем сектора
PUSH DS ;сохраняем регистры
MOV AX,SEG BUFFER ;DS:BX должны указывать на буфер
MOV DS,AX ;
MOV BX,OFFSET BUFFER ;
MOV DX,63 ;логический номер сектора
MOV CX,9 ;читаем всю дорожку
MOV AL,0 ;накопитель A
INT 25H ;функция чтения секторов
POP CX ;выталкиваем со стека флаги
POP DS ;восстанавливаем регистры
JNC NO_ERROR ;если нет ошибки, то на продолжение
CMP AH,3 ;проверка возможных ошибок
.
.
NO_ERROR: ;продолжение программы

Если при возврате флаг переноса равен 1, то произошла ошибка и
в этом случае AH и AL содержат два отдельных байта статуса ошиб-
ки. Если AH = 4, то указанный сектор не найден, а если AH = 2, то
диск неверно отформатирован. Если AH = 3, то была попытка записи
на дискету, защищенную от записи. Все остальные значения AH гово-
рят об аппаратной ошибке.

Низкий уровень.

Дисковые операции на низком уровне требуют прямого программи-
рования микросхем контроллера НГМД и прямого доступа к памяти.
Поскольку эти операции взаимосвязаны, то они рассматриваются
вместе в разделе [5.4.1].


    5.4.3 Запись в последовательные файлы.



С точки зрения программиста языки высокого уровня работают с
последовательными файлами порциями в одну единицу данных. Один
оператор "записывает" содержимое переменной в последовательный
файл, ограничивая ее парой возврат каретки/перевод строки. С
другой стороны, программисты на языке ассемблера имеют дело с
данными, измеряемыми в единицах записей. Они помещают данные в
буфер, который может содержать одну или несколько записей, добав-
ляя пары возврат каретки/перевод строки между элементами данных,
а не между записями. Некоторые элементы данных могут принадлежать
двум записям. Тогда для записи используется функция MS DOS, поз-
воляющая записать на диск одну или несколько записей. На всех


уровнях программирования DOS может не производить физической
записи на диск каждый раз, когда была подана команда вывода.
Вместо этого, в целях экономии, DOS ожидает пока его выходной
буфер будет заполнен, прежде чем записать данные на диск.
Отметим, что Бейсик автоматически добавляет в конец записывае-
мого им последовательного файла символ с кодом ASCII 26 (Ctrl-Z).
Это требование стандартных текстовых файлов. Функции DOS не до-
бавляют этот символ; Ваша программа должна сама записать его в
конец элемента данных. Файлы прямого доступа не ограничиваются
символом ASCII 26.

Высокий уровень.

Бейсик готовит файлы к последовательной записи, открывая файл
в режиме последовательного доступа оператором OPEN. Этот оператор
имеет две формы и какую из них Вы выбираете это дело вкуса. Фор-
маты этого оператора такие:

100 OPEN "MYFILE" FOR OUTPUT AS #1

или

100 OPEN "O", #1, "MYFILE"

Во второй форме буква "O" обозначает вывод (output). Символ #1
обозначает кодовый номер, по которому Вы будете впоследствии
обращаться к файлу в операторах доступа, таких как WRITE #1 или
INPUT #1. В обоих случаях открывается файл с именем MYFILE для
приема данных в последовательном режиме. Если файл с таким именем
не найден на диске, то оператор OPEN создаст его. Если же такой
файл существует, то он будет перезаписан, т.е. после его закрытия
он будет содержать только новые записанные в него данные. Чтобы
добавить данные в конец существующего последовательного файла, не
изменяя его предыдущего содержимого, нужно открыть его, используя
первую форму оператора OPEN в виде OPEN "MYFILE" FOR APPEND AS
#1. Более подробно об этом см. [5.3.3].
Данные записываются в файл с помощью операторов PRINT# и WRI-
TE#. Они имеют одинаковую форму:

100 PRINT #1, S$

или

100 WRITE #1, X

#1 относится к идентификационному номеру файла (дескриптору фай-
ла), присваиваемому ему оператором OPEN. В первом примере в файл
записывается значение строковой переменной, а во втором численное
значение, но можно любым из них записывать и то и другое. Числен-
ные значения записываются в последовательные файлы в строковом
виде, хотя они и берутся не из строковых переменных. Например,
232 является 2-хбайтным целым в строковой форме, однако если X =
232, то оператор PRINT #1, X помещает в файл три байта, используя
коды ASCII для цифр 2, 3 и 2.


Операторы PRINT# и WRITE# отличаются способом отделения эле-
ментов данных в файле. Какой из них более подходящий определяется
характеристиками данных. Основное различие между двумя оператора-
ми состоит в том, что WRITE# вставляет дополнительные ограничите-
ли между элементами данных. Рассмотрим случай, когда оператор
выводит несколько переменных в виде 100 PRINT #1, A$, Z, B$ или
100 WRITE #1, A$, Z, B$. В этом случае пара возврат каретки/пере-
вод строки будет помещена в файл только за последней из трех
переменных (отметим, что строковые и числовые переменные могут
быть перемешаны). Как же можно впоследствии выделить эти три
переменные? Если был использован оператор PRINT#, то никак. Все
три переменные будут объединены в непрерывную строку. Если же был
использован оператор WRITE#, то каждый элемент данных будет зак-
лючен в кавычки, а между ними будут стоять запятые. Затем, при
чтении этих элементов из файла, Бейсик будет автоматически уда-
лять кавычки и запятые, которые были добавлены оператором WRITE#.
Имеется еще ряд менее важных вопросов. Один из них состоит в
том, что вся проблема с ограничителями может быть снята, если
использовать для вывода каждой переменной отдельный оператор
PRINT# или WRITE#. В этом случае PRINT# будет отделять все эле-
менты парами возврат каретки/перевод строки, а WRITE# будет де-
лать то же самое, но по-прежнему каждый элемент будет заключен в
кавычки (что напрасно расходует файловое пространство). Более
того, для вывода строк, которые сами содержат кавычки, оператор
WRITE# использовать нельзя, поскольку первая же внутренняя кавыч-
ка будет при чтении ошибочно воспринята как признак конца пере-
менной. И, наконец, отметим, что когда в одном операторе выводит-
ся несколько переменных, то оба оператора форматируют данные в
точности так же, как они форматировались бы при выводе на терми-