Реклама:

Программа InFilBuf.s, представленная в листинге В.8, являет собой пример произвольного файлового ввода-вывода. Она допускает, что файл состоит из произвольного числа символьных строк, каждая из которых потенциально может отличаться по длине от других. Сначала эта программа считывает файл и формирует таблицу, в которой запись п отражает положение начала строки п в файле. Впоследствии можно запросить конкретную символьную строку, отыскать запись для нее в таблице и считать с помощью системных вызовов 1 seek и read. Имя файла при стандартном вводе указывается в первой вводимой строке. Программа состоит из нескольких относительно независимых кодовых фрагментов, которые можно адаптировать для иных целей.

Листинг В.8. Программа, реализующая буферизованный и произвольный доступ к файлу

#include "../syscalnr.h" ! 1

bufsiz =512 ! 2

.SECT .TEXT ! 3

infbufst: ! 4

MOV BP.SP ! 5

MOV Dl.linein ! 6

PUSH _GETCHAR ! 7

1: SYS ! 8

CMPB AL, *\n* ! 9

JL 9f ! 10

JE If ! 11

STOSB ! 12

JMP lb ! 13

1: PUSH 0 ! 14

PUSH linein ! 15

PUSH _0PEN ! 16

SYS ! 17

CMP AX.O ! 18

JL 9f ! 19

MOV (fiIdes),AX ! 20

MOV SI.linh+2 ! 21

MOV BX.O ! 22

1: CALL fillbuf ! 23

CMP CX.O ! 24

JLE 3f ! 25

2: MOVB AL,'\гГ ! 26

REPNE SCASB ! 27

JNE lb ! 28

INC (count) ! 29

MOV AX.BX ! 30

SUB AX.CX

! 31

XCHG SI.DI

! 32

STOS

! 33

XCHG SI.DI

! 34

CMP CX.O

! 35

JNE 2b

! 36

JMP lb

! 37

9: MOV SP.BP

! 38

PUSH linein

! 39

PUSH errmess

! 40

PUSH PRINTF

! 41

SYS

! 42

PUSH EXIT

! 43

PUSH EXIT

! 44

SYS

! 45

3: CALL getnum

! 46

CMP AX.O

! 47

JLE 8f

! 48

MOV BX,(curlin)

! 49

CMP BX.O

! 50

JLE 7f

! 51

CMP BX,(count)

! 52

JG 7f

! 53

SHL BX.l

! 54

MOV AX,linh-2(BX)

! 55

MOV CX.linh(BX)

! 56

PUSH 0

! 57

PUSH 0

! 58

PUSH AX

! 59

PUSH (fildes)

! 60

PUSH LSEEK

! 61

SYS

! 62

SUB CX.AX

! 63

PUSH CX

! 64

PUSH buf

! 65

PUSH (fildes)

! 66

PUSH READ

! 67

SYS

! 68

ADD SP.4

! 69

PUSH 1

! 70

PUSH WRITE

! 71

SYS

! 72

ADD SP.14

! 73

JMP 3b

! 74

8: PUSH scanerr

! 75

PUSH PRINTF

! 76

SYS

! 77

ADD SP.4

! 78

JMP 3b

! 79

7: PUSH 0

! 80

PUSH EXIT

! 81

SYS

! 82

fillbuf:

! 83

PUSH bufsiz

! 84

PUSH buf

! 85

PUSH (fildes)

! 86

PUSH READ

! 87

SYS

! 88

ADD SP.8

! 89

MOV CX.AX

! 90

ADD BX.CX

! 91

MOV Dl.buf

! 92

RET

! 93

getnum:

! 94

MOV Dl.linein

! 95

PUSH GETCHAR

! 96

1: SYS

! 97

CMPB AL,'\n'

! 98

JL 9b

! 99

JE If

! 100

STOSB

! 101

JMP lb

! 102

1: MOVB (DI).1 '

! 103

PUSH curl in

! 104

PUSH numfmt

! 105

PUSH linein

! 106

PUSH SSCANF

! 107

SYS

! 108

ADD SP.10

! 109

RET

! 110

.SECT .DATA

! Ill

errmess:

! 112

.ASCIZ "Open %s failed\n"

! 113

numfmt: .ASCIZ

! 114

scanerr:

! 115

.ASCIZ "Type a number.\n"

! 116

.ALIGN2

! 117

.SECT .BSS

! 118

linein: .SPACE 80

! 119

fildes: .SPACE2

! 120

linh: .SPACE 8192

! 121

curl in: .SPACE4

! 122

buf: .SPACE bufsiz+2

! 123

count: .SPACE2

! 124

В первых пяти строках кода определяются номера системных вызовов и размер буфера, а указатель базы, как обычно, настраивается на вершину стека. В строках 6-13 из стандартного ввода считывается имя файла, а затем оно сохраняется в символьной строке с меткой linein. Если имя файла не закрыто новой строкой, выводится сообщение об ошибке, а процесс заканчивается с ненулевым состоянием. Все эти действия отражены в строках 38-45. Обратите внимание, что адрес имени файла помещается в стек в строке 39, адрес сообщения об ошибке-в строке 40. Само сообщение об ошибке (представленное в строке ИЗ) представляет собой запрос на строку $s в формате PRINTF. Здесь же выполняется вставка содержимого строки linein.

В случае успешного копирования имени файла его открытие производится в строках 14-20. Если вызов open завершается с ошибкой, возвращаемое значение становится отрицательным, и для отображения сообщения об ошибке производится переход к метке 9 в строке 28. Если вызов проходит успешно, возвращаемым значением является дескриптор файла, который сохраняется в переменной fildes. Этот дескриптор понадобится при последующем выполнении вызовов read и 1 seek.

Далее файл считывается блоками по 512 байт, каждый из которых сохраняется в буфере buf. На самом деле под буфер выделяется на два байта больше необходимого объема (512 байт), но сделано это лишь для того, чтобы показать способ размещения символической константы и целого числа в одном выражении (строка 123). Аналогичным образом, в строке 21 в регистр SI загружается адрес следующего элемента массива linh, в результате на дне массива остается машинное слово с нулевым значением. Регистр ВХ получает адрес файла первого непрочитанного символа файла, а значит, в строке 22 перед первым наполнением буфера он инициализируется нулем.

За наполнение буфера отвечает подпрограмма fі 11 buf, размещенная в строках 83-93. После введения в стек аргументов read делается запрос на системный вызов, который помещает число фактически считанных символов в регистр АХ. Это число копируется в СХ, и впоследствии по значению этого регистра можно будет узнать число оставшихся в буфере символов. Положение в файле первого непрочитанного символа хранится в регистре ВХ, и в строке 91 значение СХ прибавляется к значению ВХ. В строке 92 дно буфера помещается в DI; таким образом, осуществляется подготовка к просмотру буфера на предмет следующего символа новой строки.

После возврата из fill buf в строке 24 проводится проверка того, были ли какие-либо символы фактически считаны. При отрицательном ответе происходит переход из цикла чтения с буферизацией ко второй части программы в строке 25.

После этого начинается просмотр буфера. Символ \п загружается в регистр AL в строке 26, а уже в следующей строке это значение просматривается в цикле REP SCASB и сравнивается с буферизованными символами. Выход из цикла может произойти в двух случаях: когда в регистре СХ оказывается нулевое значение или когда просматриваемый символ оказывается символом новой строки. При установленном нулевом флаге последним просмотренным символом оказывается \п, а положения в файле текущего символа (расположенного после перевода строки) сохраняется в массиве linh. Далее происходит приращение счетчика, а положение в файле определяется по значению ВХ и числу символов, оставшихся в СХ (строки 29-31 кода). В строках 32-34 выполняется сохранение, но так как для команды ST0S целевым адресом является не регистр SI, а регистр DI, перед и после вызова команды ST0S эти регистры меняются местами. В строках 35-37 производится проверка оставшихся в буфере данных, после чего в зависимости от значения СХ выполняется переход.

По достижении конца файла в нашем распоряжении оказывается полный список положений в файле начальных элементов строк. Так как массив 1 і nh начинается с нулевого слова, нам известно, что первая строка начинается с адреса О, следующая строка находится в положении linh + 2, и т. д. Размер строки п можно вычислить путем вычитания начального адреса строки п из начального адреса строки n + 1.

В оставшейся части программы считывается номер символьной строки, эта строка передается в буфер, после чего она выводится при помощи вызова write. Все необходимые для выполнения этих операций данные имеются в массиве linh, где каждая n-ная запись содержит начальное положение строки п в файле. Если номер запрошенной символьной строки равен нулю или выходит за пределы допустимого диапазона, программа завершается путем перехода к метке 7.

Эта часть программы начинается с вызова подпрограммы getnum (строка 46 кода). Она считывает символьную строку из стандартного ввода и сохраняет в буфере linein (строки 95-103 кода). Далее подготавливается вызов SSCANF. Принимая во внимание обратный порядок следования аргументов, в стек сначала помещается адрес буфера curl in, где можно разместить целочисленное значение, затем адрес форматной строки для целочисленного представления numfmt, и, наконец, адрес буфера linein, в котором содержится число в десятичном представлении. Если это возможно, системная подпрограмма SSCANF помещает двоичное значение в curl in. В случае ошибки она возвращает нулевое значение АХ. Проверка возвращаемого значения проводится в строке 48; при ошибке программа генерирует сообщение об ошибке посредством метки 8.

Если подпрограмма getnum возвращает действительное целочисленное значение в curl in, оно сначала копируется в ВХ. Далее это значение проверяется на предмет принадлежности к допустимому диапазону (строки 49-53 кода). Если обнаруживается, что номер строки выходит за пределы этого диапазона, происходит выход (EXIT).

Далее необходимо определить конечное положение данной строки в файле и число считываемых файлов; с этой целью значение ВХ умножается на 2 посредством сдвига влево (SHL). В строке 55 положение текущей символьной строки в файле копируется в регистр АХ. Затем положение следующей символьной строки помещается в регистр СХ, и на его основе вычисляется число байтов в текущей строке.

Для произвольного чтения данных из файла применяется вызов 1 seek; он устанавливает смещение к байту, который предполагается прочесть в следующую очередь. Подпрограмма 1 seek выполняется относительно начала файла, и в этой связи в строке 57 в стек помещается нулевой аргумент. Следующим аргументом является смещение в файле. По определению этот аргумент является длинным (32-разрядным) целым, поэтому сначала в стек вводится пулевое слово, а затем - значение АХ (строки 58 и 59 кода); таким образом формируется 32-разрядное целочисленное значение. Далее в строке 62 в стек отправляются дескриптор файла и код LSEEK, а также выполняется вызов. Возвращаемое значение LSEEK определяет текущее положение в файле, а найти его можно в комбинации регистров DX : АХ. Если число умещается в рамки машинного слова (а размер файла не превышает 65 536 байт, по-другому быть и не может), адрес помещается в АХ; следовательно, если вычесть значение этого регистра из СХ (строка 63), мы получим число байтов, которые необходимо прочесть для помещения строки в буфер.

Все остальное очень просто. В строках 64-68 строка считывается из файла, а затем при помощи дескриптора 1 файла в строках 70-72 она записывается в файл стандартного вывода. Следует иметь в виду, что после частичной очистки стека, которая выполняется в строке 69, значения счетчика и буфера в нем остаются. Наконец, в строке 73, указатель стека сбрасывается полностью, после чего осуществляются обратный переход к метке 3 и очередной вызов getnum.

Таблицы диспетчеризации || Оглавление || Вопросы и задания-l