Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Лаба 2 / КНИГА_АСМ.docx
Скачиваний:
2
Добавлен:
09.02.2024
Размер:
160.52 Кб
Скачать
    1. Работа с гибкими дисками.

Мы не будем здесь подробно рассматривать, как устроена дисковая подсистема компьютера, приведем только минимально необходимые сведения.

Любой диск разбит на дорожки, а дорожки, в свою очередь, разбиты на сектора. Стандартный формат сектора – 512 байт. Сектора имеют нумерацию, при этом сектор имеет физический номер и логический номер. Физическая нумерация секторов несколько более сложная и мы ее рассматривать не будем. При логической нумерации каждому сектору присваивается номер: 0 (сектор 0), 1 (сектор 1), …

На любом диске в секторе 0 располагается «загрузочная запись» (boot record), далее (в секторах 1, 2, …) располагаются две копии «таблицы размещения файлов» FAT (file allocate table), далее идет «корневой каталог» (root dir) и далее собственно информация (файлы). В лабораторной работе предполагается работа с FAT и корневым каталогом дискеты, формата 1,44 Мб.

Помимо понятия сектор, с дисками связано еще одно понятие – «кластер». Кластер – это минимальная часть диска, которая выделяется на данном диске под запись одного файла. Один кластер занимает на диске несколько смежных секторов, например, для дискеты 1,44 М, кластер занимает два смежных сектора. То есть, если мы создали файл размером 1 байт и записали его на нашу дискету, это файл займет один кластер (1024 байта на дискете). И если у нас есть файл, размером 1000 байт, он тоже займет на дискете один кластер. А если файл имеет размер 9,3 Кбайта? Тогда этот файл на дискете расположиться в десяти кластерах, причем необязательно подряд идущих. Именно для описания таких файлов, расположенных «не в подряд идущих» кластерах и создается FAT.

В FAT для каждого записанного на диск файла, содержится описание «цепочки кластеров», отведенных для этого файла. Каждый кластер имеет номер, причем нумерация кластеров начинается не с нуля, а с 2 (2, 3, 4, …). FAT состоит из элементов, в первые два из которых (элементы 0 и 1) записана служебная информация, а каждый следующий элемент FAT содержит описание одного соответствующего кластера (элемент 2 описывает кластер 2, …). Если, например, в элементе 2 записано число 5, то продолжение файла находится в кластере 5. Таким образом, если наш файл располагается на дискете в кластерах 2, 3, 7 и 12 (кластер 2 – начало файла, кластер 12 – конец файла), в FAT будет записана следующая информация:

элемент 2 – 3

элемент 3 – 7

элемент 7 – 12

элемент 12 – информация о том, что это последний кластер данного файла.

Для гибких дисков используется FAT, каждый элемент которой имеет формат 12 бит (FAT12). Сделано это для того, чтобы FAT занимала на дискете как можно меньше места. В то же время, очевидно, что работа с такими нестандартными 1,5 байтными элементами создает для программиста довольно серьезные трудности. Для дискеты 1,44 М первая копия FAT располагается в секторах 1 – 9, вторая копия FAT – в секторах 10 – 18. Структура FAT для дискеты 1,44 М приведена на рис. 3.1.

байт

……….

Рис. 3.1

Здесь:

- служебная информация

- четный элемент FAT

- нечетный элемент FAT

В общем случае элемент FAT (описание кластера) может содержать следующую информацию:

000h - кластер свободен;

002h – ff0h - кластер занят, а записанное в нем число задает следующий кластер цепочки;

ff1h – ff7h - кластер испорчен (плохой);

ff8h – fffh - кластер занят и является последним кластером цепочки.

Алгоритмы работы с FAT будут приведены ниже.

Корневой каталог для дискеты 1,44 М занимает сектора 19-33 и содержит описания всех файлов и поддиректорий, расположенных в корневой директории. Один элемент каталога занимает 32 байта и имеет следующую структуру:

байта (байтов)

описание

0 – 7

Имя файла в ASCII кодах (большими буквами!!). Если в байте 0 записан 0, это означает, что данный элемент каталога свободен и никогда не использовался. Соответственно и все последующие элементы каталога тоже будут свободны. Если в байте 0 записано 0e5h – это означает, что данный файл с диска удален. Примечание: данные сведения приведены для DOS, для Windows действуют иные правила, но здесь они не рассматриваются.

8 – 10

Расширение файла (большими буквами!!).

11

Атрибуты файла (формат смотри ниже)

12 – 21

Не используются

22 –23

Время создания (формат смотри ниже)

24 –25

Дата создания (формат смотри ниже)

26 –27

Номер начального кластера цепочки

28 –31

Размер файла в байтах

Формат байта атрибутов:

бит 0 = 1 - файл только для чтения;

бит 1 = 1 - скрытый файл;

бит 2 = 1 - системный файл;

бит 3 = 1 - это не файл, а метка тома;

бит 4 = 1 - это поддиректория;

бит 5 = 1 - архивный файл.

Формат времени создания:

hhhhhmmmmmmsssss (байт 23: hhhhhmmm, байт 22: mmmsssss) – здесь h – часы, m – минуты, s – пары секунд.

Формат даты создания:

yyyyyyymmmmddddd (байт 25: yyyyyyym, байт 24: mmmddddd) – здесь yгод, вычисляемый по формуле y = текущий год – 1980, mмесяц, dдень.

Например, в 23 байте элемента каталога записано 85h, в 22 байте – 0eh, в 25 байте – 2ch, и в 24 байте – 55h. Тогда:

850eh = 1000010100001110b = 16 часов 40 минут 14*2 секунд

2c55h = 0010110001010101b = (22+1980) = 2002 год 2 месяц (февраль) 21 число.

Обратите внимание на то, что чем меньше число, записанное в поле год, тем более старым будет файл.

Для работы с дисками можно использовать прерывания DOS int 25h и int 26h (имеются и другие).

Прерывание int 25h считывает с указанного диска, начиная с заданного сектора, требуемое количество секторов и помещает эту информацию в созданный в программе буфер. Прерывание int 26h записывает информацию из буфера в заданные сектора на указанном диске.

Входные данные для этих прерываний одинаковы:

В dx – номер начального сектора, в cx – количество считываемых секторов, в ds:bx – начальный адрес буфера, в al – номер дисковода (0 – А, 1 – В, 2 – С,…). Прерывания int 25h и int 26h имеют особенность: возврат из обработчиков этих прерываний производится командой ret (а не iret, как это бывает обычно). В результате у нас получается «неправильная вершина стека». Иногда, хотя и редко, это приводит к неправильной работе программы. Найти же в этом случае причину неправильной работы исключительно трудно. Поэтому проще сразу исключить эту причину, поставив сразу за командой int 25h (int 26h) команду выталкивания из стека (pop) в какой-нибудь ненужный регистр (фиктивное выталкивание). Таким образом, если, допустим, надо прочитать содержимое корневого каталога дискеты 1,44 М, можно использовать такой фрагмент:

buf db 512*15 dup (0) ; создали буфер 15 секторов по 512 байт

.

.

.

mov dx, 19

mov cx, 15

mov bx, offset buf

mov al, 0

int 25h

pop cx

После того, как содержимое корневого каталога считано в буфер, мы работаем с этим буфером, например, так:

  1. устанавливаем si (или di или…) на начало буфера;

  2. проверяем содержимое байта, расположенного по этому адресу;

  3. если в этом байте 0, заканчиваем проверку (больше в корневом каталоге ничего нет!) и уходим на пункт 6 (скорее всего на выход или на вывод какой-либо информации на экран);

  4. если в этом байте e5h – это удаленный файл, прибавляем к si (или di или…) 32 (увеличиваем адрес на 32, переходя тем самым на следующий элемент корневого каталога) и идем на пункт 2;

  5. если в байте не 0 и не e5h, значит это описание какого-то файла или поддиректории или метка тома. Производим необходимые действия, прибавляем к si (или di или…) 32 и идем на пункт 2.

  6. ……………

Примечание: Этот алгоритм рассчитан на DOS. Длинные имена файлов и удаленные файлы Windows могут приводить к неправильным результатам. Самый простой способ: взять чистую дискету или отформатировать дискету под DOS и записать на нее только файлы с короткими именами.

Во многих заданиях к данной лабораторной работе требуется выводить на экран различные числа. Например, количество файлов в корневом каталоге или номер начального кластера файла. При этом у некоторых студентов возникает проблема перевода числа в форму, пригодную для вывода на экран. Для того чтобы вывести на экран в привычном человеку десятичном виде какое-либо двоичное число надо разбить это число на отдельные десятичные цифры и перевести каждую цифру в ASCII – код. Разбиение на отдельные десятичные цифры удобно организовать с помощью операции деления (в какой бы системе счисления мы не делили бы наше число на 10, в остатке мы получим младшую десятичную цифру этого числа). Например, 123/10 = частное 12, остаток 3; 12/10 = частное 1, остаток 2. Вот так мы и разбили число 123 на отдельные десятичные числа. Перевести десятичную цифру в ее ASCII – код очень просто, достаточно прибавит к этой цифре 30h (ASCII – код цифры 0). Пусть, например, в результате работы программы, в регистре al получено количество файлов в корневом каталоге. Следующий фрагмент выведет это число на экран в десятичном виде (максимальное число в регистре al255, то есть не более трех десятичных цифр):

strok db ‘Всего файлов =’, 0, 0, 0, 0ah, 0dh,’$’

; это заготовка под выводимую строку, вместо нулей мы подставим ASCII – коды

; наших трех цифр, 0ah и 0dh переведут курсор (после вывода) в начало следующей ; строки экрана, а «доллар в апострофах» задает конец выводимой строки.

.

.

.

; разбиваем на десятичные цифры и переводим их в ASCII - код

mov si, offset strok ; в si начальный адрес заготовки

mov cl, 10

mov ah, 0

div cl ; делим ax на cl, остаток в ah, частное в al

add ah, ‘0’ ; в ah младшая цифра, переводим ее в ASCII – код

mov [si + 16], ah ; и отправляем ее в заготовку на место правого нуля

mov ah, 0

div cl

add ah, ‘0’ ; в ah средняя цифра, переводим ее в ASCII – код

mov [si + 15], ah ; и отправляем ее в заготовку на место среднего нуля add al, ‘0’ ; в al старшая цифра, переводим ее в ASCII – код

mov [si + 14], al ; и отправляем ее в заготовку на место левого нуля

; выводим строку на экран

mov ah, 9

mov dx, offset strok

int 21h

Здесь программист должен быть внимателен, так как команда деления может приводить к прерыванию по ошибке деления (смотри описание команды div в разделе 2.8). В приведенном выше фрагменте возникновение такого прерывания исключено.

Иногда надо выводить число на экран в шестнадцатеричном виде. Разбить двоичное число на отдельные шестнадцатеричные цифры просто - достаточно разбить это число на отдельные тетрады. Для перевода шестнадцатеричной цифры в ASCII – код можно, например, исследовать эту цифру и, если она находится в диапазоне от 0 до 9 включительно, прибавить к ней ASCII – код цифры 0, в противном случае, прибавить ASCII – код буквы А (латинской) минус 10. Пусть надо вывести на экран в шестнадцатеричной форме число из регистра al:

strok db ‘Всего файлов =’, 0, 0, 'h', 0ah, 0dh,’$’

; это заготовка под выводимую строку, вместо нулей мы подставим ASCII – коды

; наших двух цифр, 0ah и 0dh переведут курсор (после вывода) в начало следующей ; строки экрана, а «доллар в апострофах» задает конец выводимой строки.

.

.

.

mov si, offset strok

; разбиваем на шестнадцатеричные цифры и переводим их в ASCII - код

mov ah, al ; сохраняем дубликат нашего числа

shr al, 4 ; выделяем старшую тетраду (цифру) сдвигая число вправо на 4 разряда

cmp al, 9

ja m1

add al, ‘0’

jmp m2

m1: add al, ‘A’-10

m2: mov [si + 14], al ; отправляем ASCII – код на место левого нуля заготовки

and ah, 0fh ; выделяем младшую тетраду (цифру)

cmp ah, 9

ja m3

add ah, ‘0’

jmp m4

m3: add ah, ‘A’-10

m4: mov [si + 15], ah ; отправляем ASCII – код на место правого нуля заготовки

mov ah, 9

mov dx, offset strok

int 21h

Рассмотрим алгоритм работы с FAT. Например, если надо установить цепочку кластеров, в которые записан файл, можно действовать следующим образом:

  1. Считать в буфер корневой каталог;

  2. Найти в нем элемент, описывающий искомый файл;

  3. Из этого элемента взять номер начального кластера цепочки;

  4. Считать в буфер (обычно другой!) FAT;

  5. По известному номеру кластера найти в FAT элемент, описывающий этот кластер, и взять из него номер следующего кластера цепочки;

  6. Повторять пункт 5, пока не дойдем до последнего кластера цепочки.

Основное неудобство возникает при выполнении пункта 5 и связано оно с тем, что формат элемента FAT (1,5 байта) плохо согласуется с форматами регистров процессора.

Пусть у нас есть на диске какой-то довольно большой файл, занимающий кластеры 2, 3, 4, 5, … Описание цепочки кластеров в FAT для данного файла показано на рис. 3.2. Здесь во втором элементе FAT записано 003, в третьем – 004, …

байт

0

3

4

0

0

0

0

5

6

0

0

0

………

рис. 3.2

Если же посмотреть на эту же информацию, допустим, в отладчике, мы увидим следующую картину (жирным выделено содержимое четных элементов FAT):

хх хх хх 03 40 00 05 60 00 …..

Возникает вопрос, а как по известному номеру кластера N найти в FAT соответствующий этому кластеру элемент? Если N четный, то смещение элемента, относительно начала FAT, можно вычислить по формуле:

смещение элемента = N + N/2.

При нечетном N формула слегка меняется:

смещение элемента = N + (N-1)/2.

Отсюда можно воспользоваться, например, таким фрагментом:

; пусть N находится в регистре bp

mov si, bp ; дубликат N в si

mov di, bp ; дубликат N в di

mov bx, offset buf ; в bx начальный адрес буфера, в который считана FAT

and bp, 1 ; если получился ноль – N четный

jnz nechet

; четный N

shr di, 1 ; делим (сдвигом вправо на разряд) N пополам

add si, di ; получаем смещение элемента в si

mov ax, [bx+si] ; элемент в ax

and ax, 0fffh ; убираем старшую тетраду, принадлежащую нечетному ; элементу, формируя в ax содержимое искомого элемента

jmp m1

nechet:

; нечетный N

dec di ; N-1

shr di, 1 ; (N-1)/2

add si, di

mov ax, [bx+si]

shr ax, 4 ; убираем младшую тетраду, принадлежащую четному ; элементу, формируя в ax содержимое искомого элемента

m1: ; …………….

Приведем пример программы, выводящей на экран названия всех файлов и поддиректорий в корневом каталоге, начинающиеся с буквы t.

code segment

assume cs: code, ds: code

org 100h

start:

jmp begin

buf db 512*15 dup (0)

mess1 db 0ah, 0dh,’$’ ; для перевода курсора в начало новой строки экрана

nomber db 0

mess2 db ‘В каталоге нет файлов, начинающихся с буквы t’, 0ah, 0dh, ‘ $’

mess3 db ‘ Для выхода из программы нажмите любую клавишу$’

begin:

; считываем корневой каталог в буфер

mov dx, 19

mov cx ,15

mov bx, offset buf

mov al, 0

int 25h

pop bx

; настраиваем si на начало буфера

mov si, offset buf

m2:

mov al, [si] ; читаем первый байт очередного элемента каталога

cmp al, 0 ; файлов больше нет?

je exit1 ; да, выходим

cmp al, 0e5h ; удаленный файл?

jne m1 ; нет, проверяем дальше

add si, 32

jmp m2 ; раз удален, переходим к следующему элементу каталога

m1:

cmp al, ‘T; начинается с Т?

je m3 ; да, идем на вывод названия на экран

add si, 32

jmp m2 ; нет, переходим к следующему элементу каталога

m3:

; наращиваем счетчик

Соседние файлы в папке Лаба 2