Функция 1 прерывание int 10h
Входные параметры:в ch(биты 4-0) верхняя строка развертки курсора, в cl(биты 4-0) нижняя строка развертки курсора.
Подпрограмма позволяет установить конфигурацию (размер по вертикали) курсора. Стандартно (для цветных мониторов) верхняя граница курсора равна 6, а нижняя – 7. Если взять верхнюю границу равной 0, а нижнюю оставить равной 7, курсор будет максимально большим. Это же прерывание позволяет гасить курсор (делать его невидимым). Для этого надо взять ch = 20, cl = 0.
Пример:
Mov ah, 1
Mov ch, 20
Mov cl, 0
Int 10h
Фрагмент программы с такими параметрами гасит на экране курсор. Однако если Вы хотите скрыть курсор, советуем сделать это в самый последний момент, когда вся программа уже отлажена. Скрытый курсор может мешать процессу отладки, так как зачастую ошибка заключается в неправильном позиционировании курсора (за пределами экрана).
Функция 2 прерывание int 10h
Входные параметры:в dhномер строки, в dlномер столбца, в bhномер видеостраницы (для нас всегда 0).
Подпрограмма устанавливает курсор в заданную позицию.
ПРИМЕР: Установить курсор в центр экрана.
mov dx, 0c28h ; 12-я строка (0сh), 40-й столбец (28h).
Mov bh, 0
Mov ah, 2
Int 10h
Функция 3 прерывание int 10h
Входные параметры:в bhномер видеостраницы (для нас 0)
Прерывание возвращает текущие координаты и конфигурацию курсора: в dhвозвращается строка, в dl– столбец, в ch– верхняя строка развертки, в cl– нижняя строка развертки.
ПРИМЕР:запомнить текущую позицию курсора в переменной cursor
Mov ah, 3
Mov bh, 0
Int 10h
Mov cursor, dx
Функция 6 прерывание int 10h
Входные параметры: сх – координаты левого верхнего угла прямоугольной области экрана (ch –строка, cl –столбец), dx –координаты правого нижнего угла (dh –строка, dl –столбец), al –на сколько строк прокручивать заданное окно (при al = 0все заданное окно очищается), bh -атрибуты для заполнения освобождающихся строк.
Прокрутка заданной прямоугольной области экрана (окна) на заданное число строк вверх. Такая процедура называется "скроллинг".
ПРИМЕР: Очистить экран.
mov cx, 0; левый верхний угол экрана. Строка=0, столбец=0.
mov dx, 184fh ; правый нижний угол экрана. Строка=24 (18h), столбец=79 (4fh)
mov bh,7; белый по черному
mov ax, 600h ; функция 6. Очистить весь экран.
int 10h
Функция 9 прерывание int 10h
Входные параметры: bh - номер видеостраницы (у нас 0), bl - атрибуты символа, al - ASCII-код выводимого символа, сх - число повторений.
Выводит заданный символ в текущую позицию курсора. Курсор при этом не перемещается. В сх помещается число х ( х >=1 ). При выводе символ распространяется на х позиций вправо от курсора. То есть если х =1, то будет напечатан один символ, при х =2 - два символа (одинаковых) и. т. д. Если cx =0, ничего выводиться не будет!!! Коды 7, 8, 0ah и 0dh являются управляющими.
ПРИМЕР: Забить верхнюю строку экрана символом "*". Вывод произвести черным по белому.
mov ah, 2 ; устанавливаем курсор
mov bh, 0; видеостраница 0
mov dx, 0 ; левый верхний угол экрана (строка = столбец =0)
Int 10h
mov ah, 9 ; вывод символа
mov bh, 0 ; видеостраница 0
mov bl, 70h ; черным по белому
mov al, '*'
mov cx, 80 ; заполняем всю строку
Int 10h
Функция 0eh прерывания int 10h
Входные параметры: al -ASCII-код выводимого символа
Выводит символ в текущую позицию курсора, после чего курсор сдвигается на позицию вправо. Символ выводится с текущими атрибутами.
Очень часто в программах приходится программным образом организовывать задержку. Например, если у нас по экрану летает символ, без задержки он будет летать настолько быстро, что мы увидим только некое мерцание. Есть несколько способов организации такой задержки.
Способ 1.Пустой цикл.
mov cx, 0ffffh; в cxмаксимально возможное число
zero_loop: loop zero_loop
Увы, для современных процессоров эта задержка окажется слишком мала, мы ее даже не заметим. Приходиться делать вложенный пустой цикл. Например, так:
Mov bx, 400
M1: mov cx, 0ffffh
M2: loop m2
Dec bx
Jnz m1
Недостаток заключается в том, что здесь время задержки зависит от производительности процессора. То есть, для конкретного компьютера ее приходится подбирать, уменьшая или увеличивая наше число 400.
Способ 2.
Mov ah, 86h
Mov cx, 4
Mov dx, 0
Int 15h
Эта функция прерывания int 15hотрабатывает задержку, заданную (в микросекундах) в регистровой паре cx:dx. В приведенном выше примере задержка составит порядка 218 микросекунд, то есть примерно четверть секунды. Недостаток заключается в том, что некоторые операционные системы (в частности, Windows NT) эту задержку «не понимают» (программа выполняется, а задержки нет).
Способ 3.Работа с системными часами, расположенными в области переменных BIOS.
Функция 0 прерывания 1ah возвращает в cx:dxтекущее число тиков таймера. Таймер тикает примерно 20 раз в секунду. Отсюда, если мы реализуем такой фрагмент:
Mov ah, 0
Int 1ah
Mov bx, dx
Add bx, 10
mwait:
Mov ah, 0
Int 1ah
Cmp dx, bx
jb mwait; если меньше, снова идем на mwait
то получим задержку на полсекунды.
В качестве примера работы с экраном и клавиатурой, приведем программу, демонстрирующую тривиальное передвижение на экране принятого с клавиатуры символа. Программа очищает экран, ждет нажатия любой символьной клавиши, после чего принятый символ пролетает по 12-й строке от левого края экрана к его центру, где и останавливается. Затем программа снова ждет нажатия символьной клавиши и все повторяется сначала. Выход из программы по ESC.
Code segment
Assume cs:code, ds:code
Org 100h
start:
Jmp begin
x db 0;координата курсора по Х, исходный столбец 0
Simb db 0
begin:
; прячем курсор
Mov ah, 1
Mov ch, 20
Mov cl, 0
Int 10h
; чистим экран красным цветом, символ будет черным
Mov cx, 0
Mov dx, 184fh
Mov bh, 40h
Mov ax, 600h
Int 10h
m1:
; ждем нажатия клавиши
Mov ah, 0
Int 16h
; отсекаем функциональные клавиши
Cmp al, 0
Je m1
; проверяем на ESC
Cmp al, 1bh
Je exit
; сохраняем принятый символ
Mov simb, al
; выводим пробел в центр экрана (стираем старый символ)
Mov ah, 2
Mov dx, 0c28h
Mov bh, 0
Int 10h
Mov ah, 0eh
mov al, ' '
Int 10h
m2:
; устанавливаем курсор для вывода символа
Mov ah, 2
Mov bh, 0
mov dh, 12 ; символ летит по 12-й строке
Mov dl, x
Int 10h
; выводим символ в текущую позицию курсора
Mov ah, 9
Mov cx, 1
Mov bl, 40h
Mov bh, 0
Mov al, simb
Int 10h
; задержка
Mov ah, 0
Int 1ah
Mov bx, dx
Add bx, 2
m3:
Mov ah, 0
Int 1ah
Cmp dx, bx
Jb m3
; увеличиваем Х координату курсора на единицу
Inc x
; проверка на выход за центр экрана (40-й столбец)
Cmp x, 41
Jne m4
mov x, 0; восстанавливаем исходное значение Х координаты курсора
jmp m1; пошли на ожидание нажатия клавиши
m4:
; стираем символ в текущей позиции (пробелом)
Mov ah, 0eh
mov al, ' '
Int 10h
jmp m2; пошли на вывод символа в следующей позиции
exit:
Mov ah, 4ch
Int 21h
Code ends
End start
ЗАДАНИЯ К ЛАБОРАТОРНОЙ РАБОТЕ «Клавиатура и экран»
1. Программа очищает экран. В центре экрана печатается звездочка. С помощью клавиш-стрелок можно перемещать эту звездочку по всему экрану, не выходя, однако, за его пределы. В любой момент на экране находится только одна звездочка. Выход из программы по нажатию клавиши TAB.
2. Программа очищает экран. При нажатии любой символьной клавиши на экране появляется соответствующий символ, который в дальнейшем можно распространять по экрану с помощью клавиш-стрелок. В любой момент можно сменить символ, нажав другую символьную клавишу. Выход из программы по нажатию клавиши ESC.
3. Программа очищает экран и вырезает в центре экрана инверсное окно разумных размеров. Далее, при нажатии любой символьной клавиши её отображение должно появляться внутри этого окна. Таким образом, окно постепенно заполняется вводимыми с клавиатуры символами. Заполнение окна происходит в общепринятом порядке: слева –направо – сверху - вниз. Выход из программы по нажатию клавиши INS или при полном заполнении окна.
4. Программа очищает экран. Вводимый с клавиатуры символ появляется одновременно во всех четырех углах экрана и с разумной скоростью слетается по диагоналям в центр экрана, где и застывает. При следующем нажатии любой из символьных клавиш все повторяется. Выход из программы по нажатию клавиши F10.
5. По экрану (хаотично!) летают два шарика (нули), зеркально отражаясь от его границ. К границе шарики подлетают под углом 45 градусов. Нажатие клавиши SPACE останавливает движение шариков, а последующее нажатие любой другой клавиши снова его возобновляет. Выход из программы по нажатию клавиши ESC или при столкновении шариков.
6. Программа очищает экран и вырезает в его центре инверсное окно разумных размеров. Вводимый с клавиатуры символ полностью заполняет это окно. При вводе следующего символа всё повторяется. Выход из программы по нажатию F9.
7. Программа очищает экран и вырезает в его центре инверсное окно разумных размеров. Вводимый с клавиатуры символ должен, с разумной скоростью, двигаться вокруг этого окна (по его кромке). При вводе следующего символа всё повторяется. Выход из программы по нажатию F8.
8. После запуска программы по границам очищенного экрана бежит «змей», например из 8-ми звездочек или нулей. С помощью клавиш-стрелок направление движения «змея» можно менять на 90 градусов от текущего направления. При достижении границы экрана «змей» автоматически заворачивает на 90 градусов вправо, то есть переходит на первоначальную траекторию. Выход из программы по нажатию клавиши SPACE.
9. Программа очищает экран. При вводе символа последний полностью заполняет собой границы экрана. Следующий вводимый символ полностью заполняет собой внешние границы ещё "чистой" области экрана. И так далее. Выход из программы по нажатию клавиши DEL или при полном заполнении экрана.
10. Программа очищает экран и рисует вертикальную инверсную линию, проходящую через весь экран. Эта линия должна иметь 2-3 разрыва шириной 1-2 строки. Затем программа просит игрока задать номер строки. После того как номер введен, по соответствующей строке из-за левой границы экрана вылетает шарик (например, ноль) и летит по горизонтали через экран (с разумной скоростью). Если шарик попал в разрыв программа, хвалит игрока, если не попал - ругает. Программа предоставляет игроку пять попыток. Выход из программы по нажатию клавиши ESC или после пяти попыток.
11. Программа очищает экран. Внешние границы экрана заполняются звездочками. Начиная с левого верхнего угла по верхней кромке экрана выводится фамилия одного из членов бригады. Так как фамилия выводится в ту же строку что и звездочки, то в соответствующих позициях (и только в них) звездочки отсутствуют. При нажатии клавиши R вся эта картинка начинает с разумной скоростью сдвигаться по часовой стрелке. То есть фамилия вместе со звездочками вращается вокруг экрана, при этом в любой момент времени границы экрана должны быть полностью заполнены, а остальная часть экрана должна быть чистой. Клавиша S останавливает вращение, клавиша R- возобновляет, клавиша ESC- выход из программы.
12. Программа очищает экран и выводит на него набираемую на клавиатуре информацию, но не в общепринятом порядке (слева – направо – сверху – вниз), а сверху – вниз – слева - направо. То есть сначала заполняется крайний левый столбец, затем следующий слева столбец и так далее. Соответственно меняется направление работы управляющих клавиш:ENTER, BACKSPACE, TAB и клавиш-стрелок. Выход из программы по нажатию клавиши ESC.
Примечание:СКЭН и ASCII коды для различных клавиш можно найти в различных справочниках. Очень хороша для этих целей маленькая программа code.exe.
Работа с гибкими дисками.
Мы не будем здесь подробно рассматривать, как устроена дисковая подсистема компьютера, приведем только минимально необходимые сведения.
Любой диск разбит на дорожки, а дорожки, в свою очередь, разбиты на сектора. Стандартный формат сектора – 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 | Расширение файла (большими буквами!!). |
Атрибуты файла (формат смотри ниже) | |
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получено количество файлов в корневом каталоге. Следующий фрагмент выведет это число на экран в десятичном виде (максимальное число в регистре al –255, то есть не более трех десятичных цифр):
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, …
байт | |
……… |
рис. 3.2
Если же посмотреть на эту же информацию, допустим, в отладчике, мы увидим следующую картину (жирным выделено содержимое четных элементов FAT):
хх хх хх 034000 056000 …..
Возникает вопрос, а как по известному номеру кластера 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:
; наращиваем счетчик
Inc nomber
; выводим название файла на экран, начиная с текущей позиции курсора
mov cx, 11; число повторений цикла (8 – название, 3 – расширение)
mov ah, 0eh; функция
m4:
mov al, [si]; в alзаносим ASCII – код очередного выводимого символа
int 10h; выводим символ, курсор сам сдвигается на позицию вправо
inc si; теперь si адресует следующий выводимый символ
Loop m4
; переводим курсор в начало следующей строки экрана
Mov ah, 9
Mov dx, offset mess1
Int 21h
add si, 21; 11 мы уже прибавили к siв цикле при выводе названия файла
jmp m2; переходим к исследованию следующего элемента каталога
exit1:
Cmp nomber, 0
jne exit ; были файлы, начинающиеся с Т
; выводим сообщение, что искомых файлов не было
Mov ah, 9
Mov dx, offset mess2
Int 21h
exit:
; выводим сообщение, с просьбой нажать любую клавишу
Mov ah, 9
Mov dx, offset mess3
Int 21h
Mov ah, 7
int 21h; ждем, когда клавишу нажмут
Mov ah, 4ch
Int 21h
Code ends
End start
В заключение данного раздела приведем варианты заданий для лабораторной работы.
ЗАДАНИЯ К ЛАБОРАТОРНОЙ РАБОТЕ
1. Программа анализирует диск и выводит на экран общее число кластеров на диске, число свободных кластеров, число занятых кластеров и число плохих кластеров.
2. Программа анализирует диск и выводит на экран общее количество файлов в корневом каталоге, количество системных файлов, количество скрытых файлов, количество файлов "только для чтения" и количество подкаталогов.
3. Программа выводит на экран список файлов из корневого каталога. При нажатии клавиши S программа сортирует этот список по алфавиту.
4. Программа выдает на экран цепочку кластеров, которую занимает заданный файл. Имя файла задается в программе с клавиатуры.
5. Найти самый большой файл в корневом каталоге и вывести на экран его имя и размер в килобайтах.
6. Поиск файла в корневом каталоге и, если он есть, выдача на экран номера его начального кластера. Имя файла задается в программе с клавиатуры.
7. Программа выводит на экран список файлов из корневого каталога. При нажатии клавиши U программа сортирует этот список по размеру
8. Вывести на экран названия всех файлов из корневого каталога и номера их начальных кластеров.
9. Программа выводит на экран список файлов (без подкаталогов) из корневого каталога, отмечая для каждого файла, является ли он скрытым или нет. При нажатии клавиши С все скрытые файлы становятся "открытыми" и наоборот, причем не только на экране, но и на диске.
10. После запуска программа выводит на экран меню: "Удалить" и "Восстановить". Производится выбор одного из пунктов этого меню, например путем нажатия соответствующей клавиши. Затем программа запрашивает ввод буквы и, если был выбран пункт "Удалить", удаляет из корневого каталога все файлы, имя которых начинается с этой буквы. Если был выбран пункт "Восстановить, программа восстанавливает в корневом каталоге все удаленные файлы, используя для этого введенную букву.
11. Определить есть ли в корневом каталоге файлы с расширением EXE и, если таковые имеются, вывести на экран их список.
12. Найти в корневом каталоге самый "старый" файл и вывести на экран его имя и дату создания.