SiteSearch
logo

Документация и статьи по Ассемблеру, Reversing:

 

Курс практических работ по программированию на языке Ассемблера
 

В этот раздел включено описание восьми практических работ по курсу «Программирование на языке Ассемблера». Каждая практическая работа сопровождается заданием, краткими теоретическими сведениями и примерами. Практические работы построены таким образом, что каждая из них не должна занимать у среднего студента более чем два академических часа самостоятельной работы, а после выполнения всего курса практических работ студент должен приобрести базовые знания по программированию на языке Ассемблера.
В предлагаемых практических работах используется общедоступное аппаратное (IBM – совместимая ЭВМ с процессором Intel 80286 и выше) и программное (комплекс программ Borland Pascal 7.0, в состав которого входит пакет Turbo Assembler) обеспечение, поэтому они легко могут быть приспособлены к выполнению работ в любой компьютерной лаборатории.

Курс практических работ рассчитан на студентов специальности 071900 «Информационные системы» и может быть применен для обучения студентов смежных специальностей.

Практическая работа №1.

Тема: Архитектура процессора Intel 8086.

Цель работы: Изучение структуры процессора Intel 8086, и основ его программирования.

Задание: Используя представленный выше теоретический материал, имеющуюся литературу и предоставленные преподавателем обучающее программное обеспечение студент должен изучить структуру микропроцессора Inlet 8086. В результате выполнения практической работы студент должен знать:

- структуру микропроцессора Intel 8086;
- назначение входящих в него блоков и устройств;
- регистры микропроцессора и их основное назначение;
- структуру и назначение регистра флагов;
- структуру памяти и особенности принципа сегментации;
- принцип прерываний;
- режимы адресации;
- последовательность написания программ на языке Ассемблера;
- назначение программ, входящих в пакет Turbo Assembler.

Практическая работа №2.

Тема: Структура EXE- и COM- программы. Вывод на экран.

Цель работы: Изучение структуры EXE- и COM-программы. Получение навыков вывода данных на экран монитора.

Задание: Разработать EXE-программу вывода символа на экран, и COM- программу вывода на экран строки.

Структура программы. Исходный программный модуль состоит из команд и директив. Команды управляют работой процессора, а директивы указывают ассемблеру и редактору связей, каким образом следует объединять, команды для создания модуля, который и станет работающей программой.

Команда на языке ассемблера состоит не более чем из четырех полей. Ниже приведен формат и пример команды с указанием названия его полей:

Метка Код операции Операнды ; Комментарий

MET: MOVE AX, BX ; Пересылка

Единственное обязательное поле – поле кода операции, определяющее команду, которую должен выполнить микропроцессор. Поле операндов определяется кодом операции и содержит дополнительную информацию о команде. Метка содержит в символическом виде адрес команды, что необходимо для организации переходов (ссылок). Поле комментария служит для удобства программиста и компилятором игнорируется.
Оператор директивы состоит из символического имени, кода псевдооперации, поля операндов и комментария. Структура директивы аналогична структуре команды. Второе поле – код псевдооперации определяет смысловое содержание директивы. Как и команды, у директивы есть операнды, причем их может быть один или несколько и они отделяются друг от друга запятыми. Допустимое число операндов в директиве определяется кодом псевдооперации. Например:

DB 0, 0, 0, 0, 0

END START

Директива может быть помечена символическим именем и содержать поле комментария. Символическое имя, стоящее в начале директивы распределения памяти, называется переменной. В отличие от метки команды после символического имени директивы двоеточие не ставиться.
Программа на языке ассемблера состоит из программных модулей, содержащихся в различных файлах. Каждый модуль, в свою очередь, состоит из операндов или директив ассемблера и заканчивается директивой END. Метка, стоящая после кода псевдооперации END, является точкой входа в программу.
Каждый модуль разбивается на отдельные части директивами сегментации, определяющими начало и конец сегмента. Любой сегмент начинается директивой начала сегмента – SEGMENT и заканчивается директивой конца сегмента – ENDS. В начале директив сегментации ставится имя сегмента.
Каждый сегмент может быть также разбит на части. В общем случае информационные сегменты SS, ES и DS состоят из определений данных а программный сегмент CS – из команд и директив, группирующих команды в блоки.
Программный сегмент может разбиваться на части директивами определения процедур – некоторых выделенных блоков программы. Как и для определения сегмента, имеются две такие директивы – директива начала PROC и директива конца ENDP. Процедура имеет имя, которое должно включаться в обе директивы, процедуры в сегменте могут располагаться последовательно одна за другой, могут быть также вложенными одна в другую.
Принципиальной особенностью COM-программы является то, что в отличие от EXE-программы, которая содержит отдельные сегменты данных, стека и кода, COM-программ, в большинстве случаев, может содержать лишь один основной сегмент (сегмент кода), в котором размещаются и код и данные и стек. Кроме того, EXE-программа, в отличие от COM-программы, содержит так называемый EXE-заголовок (256 байт), при помощи которого загрузчик выполняет настройку ссылок на сегменты в загруженном модуле, а так как и COM- и EXE- программа должна загружаться с адреса PSP:0100h (100h=256), COM-программа должна содержать в начале сегмента кода директиву позволяющую осуществить такую загрузку (ORG 100H).

Более подробно со структурой программ на языке ассемблера можно познакомиться в разделе «4.9. Структура программы на языке Ассемблера».

Ниже приведены примеры, иллюстрирующие основные особенности структуры EXE- и COM-программ, написанных на языке ассемблера:

Пример 2.1. Структура EXE-программы:

;Определение сегмента стека

STAK SEGMENT STACK

DB 256 DUP (?)

STAK ENDS

;Определение сегмента данных

DATA SEGMENT

SYMB DB '#' ;Описание переменной с именем SYMB

;типа Byte и со значением «#»

. . . ;Определение других переменных

DATA ENDS

;Определение сегмента кода

CODE SEGMENT

ASSUME CS:CODE,DS:DATA,SS:STAK

;Определение подпрограммы

PROC1 PROC

. . . ;Текст подпрограммы

PROC1 ENDP

START: 

XOR AX,AX ;Точка входа в программу START

MOV BX, data ;и обязательная инициализация

MOV DS,BX ;регистра DS в начале программы

CALL PROC1 ;Пример вызова подпрограммы

. . . ;Текст программы

MOV AH,4CH ;Операторы завершения программы

INT 21H

CODE ENDS

END START

Пример 2.2. Структура COM-программы:

;Определение сегмента кода

CODE SEGMENT

ASSUME CS:CODE,DS:CODE,SS:CODE

ORG 100H ;Начало необходимое для COM-программы

START:

. . . ;Текст программы

MOV AH,4CH 

INT 21H ;Операторы завершения программы

;===== Data =====

BUF DB 6 ;Определение переменной типа Byte

. . . ;Определение других переменных

CODE ENDS

END START

Вывод на экран. Вывод информации в ассемблерных программах осуществляется обычно при помощи сервисных функций DOS (прерывание 21h) или BIOS (прерывание 10h). Процесс вывода состоит в следующем:
- определенные регистры микропроцессора загружаются выводимой информацией или адресом буфера, содержащего выводимую информацию;
- в регистр AH заносится номер используемой для операции вывода функции;
- инициируется прерывание.

Ниже представлен перечень функций прерывания 21h и 10h, использующихся для вывода информации.

Прерывание 21h.

Функция 02h

Вывод на дисплей.

Вход: AH=02h

DL=выводимый символ

Выход: нет

Описание: Посылает символ из DL на стандартный вывод. Обрабатывает символ Backspace (ASCII 8), перемещая курсор влево на одну позицию и оставляя его в новой позиции.

Функция 05h

Вывод на принтер.

Вход: AH=05h

DL= символ, записываемый на стандартный принтер

Выход: нет

Описание: Посылает символ в DL на стандартное устройство принтера, обычно LPT1. Команда DOS MODE может перенаправить этот вывод в последовательный порт.

Функция 09h

Выдать строку.

Вход: AH=09h

DS:DX=адрес строки, заканчивающейся символом '$'.

Выход: нет

Описание: Строка, исключая завершающий ее символ '$', посылается на стандартный вывод. Символы Backspace обрабатываются как в функции 02h. Обычно, чтобы перейти на новую строку, включают в текст пару CR/LF (ASCII 13H и ASCII 0aH). Строки, содержащие '$', можно выдать через 40h (BX=0), которая посылает символ в DL на стандартное устройство принтера, обычно LPT1.

Прерывание 10h.

Функция 02h

Вход: AH=02h

BH = видео страница

DH,DL = строка, колонка (считая от 0)

Выход: нет

Описание: Устанавливает курсор в позицию DH,DL. Установка курсора на строку 25 делает курсор невидимым.

Функция 09h

Писать символ/атрибут в текущей позиции курсора.

Вход: AH=09h

BH = номер видео страницы

AL = записываемый символ

CX = счетчик (сколько экземпляров символа записать)

BL = видео атрибут (текст) или цвет (графика)

Выход: нет

Описание: Выводит на экран в текущую позицию курсора символ с заданным атрибутом.

Функция 0ah

Писать символ в текущей позиции курсора.

Вход: AH=0ah

BH = номер видео страницы

AL = записываемый символ

CX = счетчик (сколько экземпляров символа записать)

Выход: нет

Описание: Выводит на экран в текущую позицию курсора заданный символ.

Функция 13h

Вывод строки.

Вход: AH=13h

ES:BP – выводимая строка

CX = длина строки (подсчитываются только символы)

DH,DL = позиция (строка, колонка) начала вывода

BH = номер страницы

AL = код подфункции:

0=атрибут в BL; курсор без изменения

1=атрибут в BL; курсор – в конец строки

2=формат строки: char,attr,...; курсор без изменения

3=формат строки: char,attr,...; курсор – в конец строки

Выход: нет

Описание: Выдает строку в позиции курсора. Символы 0dH (CarRet), 0aH (LineFeed), 08H (backspace) и 07H (Beep) трактуются как команды управления и не высвечиваются.

Некоторые функции прерывания 10h используют для вывода атрибут символа. Для адаптеров цветной графики в текстовом режиме атрибут определен следующим образом:

practic_1

foreground – цвет переднего плана (от 0 до 0fH)

brt – интенсивность: 1=передний план яркий

background – фоновый цвет (от 0 до 7)

fgB – мерцание: 1=передний план мерцает

Видеоадаптер поддерживает следующие цвета:

00H черный
01H синий
02H зеленый
03H голубой
04H красный
05H розовый
06H коричневый
07H серый
08H темно-серый
09H ярко-синий
0aH светло-зеленый
0bH светло-голубой
0cH светло-красный
0dH светло-розовый
0eH желтый
0fH белый

Вычислить значение атрибута можно, используя следующее выражение:

(фон * 16) + передний план + (128 для мерцания)

Приведенный ниже фрагмент программы иллюстрирует процесс вывода строки на экран.

MOV AH,09H ;Выбор функции прерывания

MOV DX,OFFSET STR ;Занесение в DX адреса выводимой строки

INT 21H

. . .

STR DB 10,13,'Hello$' ;Описание строки

Практическая работа №3

Тема: Циклы. Ввод с клавиатуры.

Цель работы: Получение навыков организации циклических структур в ассемблерных программах. Ввод символов с клавиатуры.

Задание: Разработать программу, ввода строковых данных с клавиатуры. В веденной строке удалить пробелы, все строчные символы «а» заменить на прописные «А» и вывести результирующую строку на экран.

Циклы в ассемблерных программах. Организовать циклическую структуру в ассемблерной программе можно двумя способами. Во-первых, используя команды сравнения (CMP) и передачи управления (Jcc), и, во-вторых, используя специальные команды организации циклов (LOOP).
Команда сравнения CMP сравнивает два числа, вычитая второе из первого, также как и команда SUB, но не сохраняя результат. После выполнения команды флаги состояния устанавливаются в соответствии с результатом операции.
Команды перехода можно разделить на две группы: команды условного и безусловного перехода. Безусловный переход – это такой переход, который передает управление без сохранения информации возврата всякий раз, когда выполняется. Ему соответствует команда JMP, имеющая двухбайтное смещение.
Условный переход проверяет текущее состояние регистра флагов, чтобы определить, передать управление или нет. Все условные переходы имеют однобайтовое смещение. (Более подробно команды условного перехода см. «8. Команды сравнения и передачи управления»)

Ниже приведен фрагмент программы, вычисляющей в цикле сумму цифр от 0 до 9.

MOV AH,0 ;занести в AH первую цифру «0»

MOV AL,0 ;подготовить регистр результата

met: ADD AL,AH;прибавить к результату очередную цифру из AL

INC AH ;увеличить AH на единицу

CMP AH,10;сравнить значение в AH со значением «10»

JNE met ;если AH<>10 то осуществить переход на MET

В приведенном примере в качестве счетчика используется регистр AH и необходимо на каждой итерации цикла самостоятельно производить инкремент счетчика и его сравнение с заданной величиной 10. Для упрощения подобных ситуаций в ассемблере предусмотрены специальные команды для организации циклов. Все команды цикла используют регистр CX в качестве счетчика цикла. Простейшая из них – команда LOOP. Она в конце каждой итерации уменьшает содержимое CX на 1 и передает управление на метку (указанную в команде), если содержимое CX не равно 0. Если вычитание 1 из CX привело к нулевому результату, выполняется следующая команда.

Команда LOOPNE (цикл пока не равно) выходит из цикла, если установлен флаг нуля или если в регистре CX получился 0. Команда LOOPE (цикл пока равно) выполняет обратную к описанной проверку флага нуля: цикл здесь завершается, если регистр CX достиг 0 или если не установлен флаг 0.

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

MOV AH,0 ;занести в AH первую цифру «0»

MOV AL,0 ;подготовить регистр результата

MOV CX,10;количество суммируемых цифр

met: ADD AL,AH;прибавить к результату очередную цифру из AL

LOOP met ;если CX<>0 то осуществить переход на MET

Ввод с клавиатуры. Процесс ввода информации в ассемблерных программах осуществляется аналогично выводу:

- в регистр AH заносится номер функции ввода;

- инициируется прерывание, после выполнения которого определенные регистры процессора содержат либо введенную информацию, либо адрес буфера с введенной информацией.

Ниже описан ряд функций прерывания 21h, используемых для ввода информации с клавиатуры.

Функция 01h

Ввод с клавиатуры.

Вход: AH=02h

Выход: AL= символ, полученный с клавиатуры

Описание: Считывает (ожидает) символ со стандартного входного устройства. Отображает этот символ на стандартное выходное устройство (эхо). Ввод расширенных клавиш ASCII (F1-F12, PgUp, курсор и т.п.) требует двух обращений к этой функции. Первый вызов возвращает AL=0. Второй вызов возвращает в AL расширенный код ASCII.

Функция 07h

Нефильтруемый консольный ввод без эха.

Вход: AH=07h

Выход: AL= символ, полученный с клавиатуры

Описание: Считывает (ожидает) символ со стандартного входного устройства и возвращает этот символ в AL. Не фильтрует. Не проверяет на Ctrl-Break, backspace и т.п. Необходимо вызывать дважды для ввода расширенного символа ASCII.

Функция 08h

Консольный ввод без эха.

Вход: AH=08h

Выход: AL= символ, полученный с клавиатуры

Описание: Считывает (ожидает) символ со стандартного входного устройства и возвращает этот символ в AL. При обнаружении Ctrl-Break выполняется прерывание INT 23H. Необходимо вызывать дважды для ввода расширенного символа ASCII.

Функция 0ah

Буферизированный ввод строки.

Вход: AH=0ah

DS:DX=адрес входного буфера (смотри ниже)

Выход: буфер содержит ввод, заканчивающийся символом CR (ASCII 0dH)

Описание: При входе буфер по адресу DS:DX должен быть оформлен следующим образом:

practic_2

MAX - максимально допустимая длина ввода (от 1 до 254) При выходе буфер заполнен данными следующим образом:
practic_3

LEN - действительная длина данных без завершающего CR (здесь – 0dH).

Символы считываются со стандартного ввода вплоть до CR (ASCII 0dH) или до достижения длины MAX-1. Если достигнут MAX-1, включается консольный звонок для каждого очередного символа, пока не будет введен возврат каретки CR (нажатие Enter). Второй байт буфера заполняется действительной длиной введенной строки, не считая завершающего CR. Последний символ в буфере – всегда CR (который не засчитан в байте длины). Символы в буфере (включая LEN) в момент вызова используются как «шаблон». В процессе ввода действительны обычные клавиши редактирования: Esc выдает "\" и начинает с начала, F3 выдает буфер до конца шаблона, F5 выдает "@" и сохраняет текущую строку как шаблон, и т.д. Большинство расширенных кодов ASCII игнорируются. При распознавании Ctrl-Break выполняется прерывание INT 23H (буфер остается неизменным).

Функция 0ch

Ввод с очисткой

Вход: AH=0ch

AL= номер функции ввода (01H, 06H, 07H, 08H или 0aH)

Выход: нет

Описание: Очищает буфер опережающего ввода стандартного ввода, а затем вызывает функцию ввода, указанную в AL. Это заставляет систему ожидать ввод очередного символа. Следующие значения допустимы в AL: 01H Ввод с клавиатуры; 06H Ввод с консоли; 07H Нефильтрующий без эха; 08H Ввод без эха; 0aH Буферизованный ввод.

Приведенный ниже фрагмент программы иллюстрирует буферизированный ввод строки:

MOV AH,0AH ;занесение в AH номера функции

LEA DX,BUF ;загрузка DX адресом буфера BUF

INT 21H

. . .

BUF DB 30,00,30 DUP ('$'),’$’

При организации буфера BUF (размер буфера 30 байт), он весь заполняется символами «$», что удобно, если введенную строку в дальнейшем необходимо выводить на экран или в файл (при условии, конечно, что для ввода буфер будет использоваться лишь однократно).

Практическая работа №4

Тема: Ввод чисел. Перевод чисел в различные системы счисления.

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

Задание: Разработать программу перевода чисел из десятичной системы счисления в двоичную, восьмеричную и шестнадцатеричную. Числа должны вводиться в десятичной системе счисления, а выводятся – в двоичной, восьмеричной и шестнадцатеричной.

Ввод числовой информации. Ввод числовой информации в ассемблерную программу обычно осуществляется в два этапа:

- ввод строки содержащей число;
- перевод строки в число.

Ввод строк рассматривался в предыдущих практических работах.
Для разработки алгоритма перевода введенной строки в число проанализируем структуру числа в позиционной системе счисления (в такой системе счисления вес цифры определяется ее местоположением в числе):

2398=2*1000+3*100+9*10+8=2*103+3*102+9*101+8*100

Таким образом, для перевода строки в число из введенной строки «2398» необходимо последовательно выделять цифры и производить суммирование произведений этих цифр и множителей соответствующих позиции цифры в числе. Если буфер для ввода строки был организован, например, следующим образом:

BUF 05,00,05 DUP (?)

то после ввода строки «2398» он будет выглядеть (в шестнадцатеричной системе счисления) так:

05,04,32,33,39,38,0d

где первый байт – размер буфера, второй – количество введенных символов (без завершающего символа CR), третий, четвертый, пятый, шестой и седьмой – коды символов «2», «3», «9», «8» и «CR» соответственно. Легко заметить, что для того чтобы из кода цифры получить саму цифру необходимо из соответствующего кода вычесть 30h (шестнадцатеричный код нуля). Затем, последовательно в цикле (второй байт – количество введенных символов) выбирая цифры, формировать соответствующий множитель, вычислять произведение и производить суммирование. Нижеследующий фрагмент программы иллюстрирует описанный алгоритм (символы рассматриваются справа налево).

;Ввод числа в виде строки

MOV AH,0AH ;в AH номер функции

LEA DX,BUF ;DS:DX адрес буфера для ввода

INT 21H

;Перевод строки в число, результат в DI

MOV DI,0

LEA BX,BUF+1 ;в BX адрес второго элемента буфера

MOV CX,[BX] ;в CX количество введенных символов

XOR CH,CH

MOV SI,1 ;в SI множитель

MET: PUSH SI ;сохраняем SI (множитель) в стеке

MOV SI,CX ;в SI помещаем номер текущего символа

MOV AX,[BX+SI];в AX помещаем текущий символ

XOR AH,AH

POP SI ;извлекаем множитель (SI)из стека

SUB AX,30H ;получаем из символа (AX) цифру

MUL SI ;умножаем цифру (AX)на множитель (SI)

ADD DI,AX ;складываем с результирующим числом

MOV AX,SI ;помещаем множитель (SI) в AX

MOV DX,10 

MUL DX ;увеличиваем множитель (AX) в 10 раз

MOV SI,AX ;перемещаем множитель (AX) назад в SI

LOOP MET ;переходим к предыдущему символу

Перевод чисел в различные системы счисления. В большинстве случаев перевод из одной системы счисления в другую осуществляется последовательным делением, в нашем случае, при переводе из десятичной системы счисления в двоичную, восьмеричную и шестнадцатеричную алгоритм можно значительно упростить, заменив деление сдвигом.
Перевод из десятичной системы счисления в двоичную осуществляется последовательными сдвигами на один бит вправо. Таким образом, значение очередного бита можно вычислить, проанализировав флаг переноса CF (если CF=1 то анализируемый бит был равен 1, и если CF=0, то анализируемый бит – 0).
Перевод из десятичной системы счисления в восьмеричную осуществляется последовательными сдвигами на три бита вправо. После очередного сдвига все биты кроме трех младших обнуляются (например, наложением маски командой AND). Таким образом, в регистре получается восьмеричная цифра, для получения ее символьного отображения к значению в регистре необходимо прибавить код нуля(30h)
Перевод из десятичной системы счисления в шестнадцатеричную осуществляется последовательными сдвигами на четыре бита вправо. После обнуления всех битов кроме четырех младших в регистре получается десятичный эквивалент шестнадцатеричной цифры (число от 0 до 15). Для его представления в шестнадцатеричной символьной форме необходимо организовать таблицу соответствия, которая в простейшем случае представляет собой следующую строку «0123456789ABCDEF». При перекодировании значение десятичного эквивалента используется как смещение в таблице относительно ее начала (перекодировка может осуществляться при помощи команды XLAT).

Практическая работа №5

Тема: Подпрограммы. Работа с файлами через описатели.

Цель работы: Получить навыки организации подпрограмм и навыки работы с файлами через описатели.

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

Замечания:

1) Имя файла должно вводится с клавиатуры.
2) Если файл с введенным именем не существует, то программа должна выдать запрос о необходимости создания файла.

Подпрограммы. Как уже было сказано выше, программный сегмент может разбиваться на части директивами определения подпрограмм (процедур). Для определения процедур используются две директивы – директива начала PROC и директива конца ENDP. Процедура должна иметь имя, которое включается в обе директивы. В сегменте кода процедуры могут располагаться последовательно, а могут быть вложенными одна в другую.
Существует несколько разновидностей команды вызова процедуры CALL. Все команды вызова безусловны и используются для передачи управления процедуре. Внутрисегментный вызов NEAR CALL используется для передачи управления процедуре, находящейся в том же сегменте. Межсегментный вызов FAR CALL используется для передачи управления процедуре, находящейся в другом сегменте или даже программном модуле.

Командам CALL соответствуют команды возврата RET. Все возвраты – косвенные переходы, поскольку извлекают адрес перехода из вершины стека. Внутрисегментный возврат извлекает из стека одно слово и помещает его в регистр IP, а межсегментный возврат извлекает из стека два слова, помещая слова из меньшего адреса в регистр IP, а слово из большего адреса – в регистр CS. Оператор RET может иметь операнд, который представляет собой значение, прибавляемое микропроцессором к содержимому указателя стека SP после извлечения адреса возврата. Пример организации процедуры приведен в практической работе №2 (см. Пример №1).

Работа с файлами. Если программы, написанные на языках высокого уровня могут открыть файл без выполнения подготовительных действий (они выполняются автоматически), то ассемблерные программы должны создать специальные области данных, которые используются при операциях ввода/вывода. Используется два метода доступа к файлам: метод управляющего блока файла (FCB) и метод дескриптора файла. С помощью метода FCB можно получить доступ только к файлам, находящимся в текущем каталоге. Метод дескриптора файла позволяет получить доступ к любому файлу, независимо от того, какой каталог является текущим.

Начиная с DOS версии 2.0, в набор функций прерывания 21h включены UNIX-подобные файловые функции. Идея их состоит в том, что, когда программа открывает файл, DOS возвращает 16-битовое значение «описателя файла» (дескриптора файла) (handle). После этого, когда программа читает, позиционирует, пишет или закрывает файл, она ссылаетесь на него через описатель. Одно из самых больших удобств – то, что можно обращаться к некоторым устройствам так, как будто это дисковые файлы, через зарезервированные описатели DOS:

Таблица 5.1. Предопределенные описатели DOS
practic_4

Ниже приведен перечень наиболее часто используемых функций прерывания 21h для работы с файлами через описатели.

Функция 3cH

Создать файл.

Вход: AH=3ch

DS:DX=адрес строки ASCIIZ с именем файла

CX=атрибут файла

Выход: AX=код ошибки, если CF установлен и описатель файла, если ошибки нет

Описание: Файл создается в указанном (или умалчиваемом) оглавлении и открывается в режиме доступа «чтение/запись». Если файл уже существует, то при открытии файл усекается до нулевой длины. Если атрибут файла – «только чтение», открытие отвергается (атрибут можно изменить функцией 43H).

Функция 5bH

Создать новый файл (не должен существовать).

Вход: AH=5bh

DS:DX=адрес строки ASCIIZ с именем файла

CX=атрибут файла

Выход: AX=код ошибки, если CF установлен и описатель файла, если ошибки нет

Описание: Этот вызов идентичен функции 3ch, с тем исключением, что он вернет ошибку, если файл с заданным именем уже существует.

Функция 5aH

Создать уникальный файл.

Вход: AH=5ah

DS:DX=адрес строки ASCIIZ с путем (заканчивается \)

CX=атрибут файла

Выход: AX=код ошибки, если CF установлен и описатель файла, если ошибки нет

DS:DX (не изменяется) становится полным ASCIIZ-именем нового файла.

Описание: Открывает (создает) файл с уникальным именем в оглавлении, указанном строкой ASCIIZ, на которую указывает DS:DX. Описание пути должно быть готово к присоединению в его конец имени файла. Необходимо обеспечить минимум 12 байт в конце строки. После возврата строка DS:DX будет дополнена именем файла. DOS создает имя файла из шестнадцатеричных цифр, получаемых из текущих даты и времени. Если имя файла уже существует, DOS продолжает создавать новые имена, пока не получит уникальное имя.

Функция 3dH

Открыть файл.

Вход: AH=3dh

DS:DX=адрес строки ASCIIZ с именем файла

AL=режим открытия

Выход: AX=код ошибки, если CF установлен и описатель файла, если ошибки нет

Описание: В момент открытия файл должен существовать. Файл открывается в выбранном режиме доступа (AL = 0 – для чтения; AL = 1 – для записи; AL = 2 – для чтения и записи) и указатель «чтения/записи» устанавливается в 0.

Функция 3eH

Закрыть файл.

Вход: AH=3eh

BX=описатель файла

Выход: AX= код ошибки, если CF установлен

Описание: BX содержит описатель файла (handle), возвращенный при открытии. Файл, представленный этим описателем, закрывается, его буфер сбрасываются, а оглавление обновляется корректными размером, временем и датой.

Функция 41H

Удалить файл.

Вход: AH=41h

DS:DX=адрес строки ASCIIZ с именем файла

Выход: AX=код ошибки, если CF установлен

Описание: Имя файла не может содержать обобщенные символы («?» и «*»). Файл удаляется из заданного оглавления заданного диска. Если файл имеет атрибут только чтение, то перед удалением необходимо изменить этот атрибут через функцию 43H.

Функция 42H

Установить указатель чтения/записи (можно также узнать размер файла).

Вход: AH=42h

BX=описатель файла

CX:DX=смещение указателя: (CX * 65536) + DX

AL=0 переместить к началу файла + CX:DX

AL=1 переместить к текущей позиции + CX:DX

AL=2 переместить к концу файла - CX:DX

Выход: AX=код ошибки, если CF установлен

DX:AX=новая позиция указателя файла (если нет ошибки)

Описание: Перемещает логический указатель чтения/записи к нужному адресу, с которого начнется очередная операция чтения или записи. Вызов с AL=2, CX=0, DX=0 возвращает длину файла в DX:AX. DX здесь старшее значащее слово: действительная длина (DX * 65536) + AX.

Функция 3fH

Читать из файла/устройства.

Вход: AH=3fh

BX=описатель файла

DS:DX=адрес буфера для чтения данных

CX=число считываемых байт

Выход: AX=код ошибки, если CF установлен

AX=число действительно прочитанных байт

Описание: CX байт данных считываются из файла или устройства с описателем, указанным в BX. Данные читаются с текущей позиции указателя чтения/записи файла и помещаются в буфер вызывающей программы, адресуемый через DS:DX.

Всегда необходимо сравнивать возвращаемое значение AX (число прочитанных байт) с CX (запрошенное число байт):

- если AX=CX, (и CF сброшен) – чтение было корректным без ошибок;

- если AX=0 – достигнут конец файла (EOF);

- если AX<CX (но ненулевой), то возможны два варианта: при чтении с устройства – входная строка имеет длину AX байт; при чтении из файла – в процессе чтения достигнут EOF.

Функция 40H

Писать в файл/устройство.

Вход: AH=40h

BX=описатель файла

DS:DX=адрес буфера, содержащего данные

CX=число записываемых байт

Выход: AX=код ошибки, если CF установлен

AX=число действительно записанных байт

Описание: CX байт данных записывается в файл или на устройство с описателем, заданным в BX. Данные берутся из буфера, адресуемого через DS:DX и записываются, начиная с текущей позиции указателя чтения/записи файла. Необходимо всегда сравнивать возвращаемое значение AX (число записанных байт) с CX (запрошенное число байт для записи): если AX = CX, запись была успешной; если AX < CX, встретилась ошибка (скорее всего, переполнение).

Некоторые функции в качестве параметра используют атрибут файла. Атрибут - это один байт битовых флагов, связанный с каждым файлом и находящийся в элементе оглавления для файла. В атрибуте определены следующие биты:
practic_5

R- только чтение (нельзя обновлять или удалять);
H- скрытый;
S- системный;
V- метка тома;
D- элемент подоглавления;
A- архивный;
x- не используются.

ASCIIZ строка, содержащая имя файла, имеет вид: «‘d:\путь\имя_файла’,0». Если диск и/или путь опущены, они принимаются по умолчанию. После вызова функции описатель файла должен быть сохранен для последующих операций. Количество описателей в системе регламентируется файлом CONFIG.SYS.

Приведенный ниже пример иллюстрирует процесс работы с файлом через описатели в ассемблерной программе.

;Создание файла

MOV AH,3CH

MOV CX,0 

LEA DX,BUF ;DS:DX – адрес ASCIIZ строки с именем

INT 21H

JC NO_CREATE ;Проверка флага переноса

. . . ;Работа с файлом

NO_CREATE:

. . .

BUF DB ‘d:\Users\1.txt’,0

Практическая работа №6

Тема: Работа с файлами, используя DTA. PSP. Окружение.

Цель работы: Научиться работать с файлами используя DTA. Получить навыки использования в ассемблерных программах информации содержащейся в PSP и Окружении DOS.

Задание: Создать текстовый файл "Dir.Txt", содержащий перечень файлов в текущем каталоге. Примерный формат файла "Dir.Txt":

D:\USERS\ASM\EXAMPLE\PRG2

.

..

FILE2.ASM

FILE2.COM

FILE2.OBJ

Замечания:

1) Файл «Dir.txt» можно создавать через описатели.
2) Получение пути по которому была запущена программа (первая строка в файле «Dir.Txt») можно осуществить, используя окружение DOS. Для этого, во-первых, необходимо получить адрес PSP (функция 062H прерывания 21H), во-вторых, найти в нем адрес окружения DOS. В-третьих, получив из окружения DOS строку, содержащую путь и имя запущенного файла, выделить из нее путь к текущему каталогу.
3) На следующем этапе производится поиск первого совпадающего с маской «*.*» файла и его имя записывается в файл «Dir.Txt». Перед началом поиска необходимо правильно установить атрибут файла в CX для сравнения. Далее производится поиск следующего совпадающего с маской файла, используя функцию 04FH прерывания 21H. Если такой файл найден, то его имя записывается в «Dir.Txt», иначе осуществляется выход из программы.

Структура DTA. Как было сказано ранее, используются два метода доступа к файлу: метод управляющего блока файла (FCB) и метод дескриптора файла. В любом случае программа при работе с файлами должна указывать место в памяти, куда будут помещаться принимаемые данные или откуда будут извлекаться выводимые. Обычно временный буфер устанавливается размером в одну запись и бывает удобно описать его как строковую переменную в сегменте данных.

Буфер, используемый методом FCB доступа к файлам, называется областью обмена с диском или DTA. На этот буфер указывает условный указатель, который хранится операционной системой и который может быть изменен программой. В документации этот указатель на DTA часто сам называют DTA. Указатель на DTA устанавливается специальной функцией DOS и после того как он установлен все функции чтения/записи автоматически обращаются к нему. Это означает, что сами функции не должны содержать адрес временного буфера.

Для установки указателя на DTA используется функцию 1AH прерывания 21H (DS:DX должны указывать на первый байт DTA). Функция 2FH прерывания 21H сообщает текущую установку указателя DTA (при возврате ES:BX содержат сегмент и смещение DTA).

Префикс программного сегмента PSP обеспечивает каждую программу 128-байтным встроенным DTA, начиная со смещения 80H и до 9FH. Программа может использовать его при нехватке памяти. Первоначально указатель на DTA указывает именно на этот буфер, поэтому если программа будет использовать его, то нет нужды устанавливать указатель. Этот буфер по умолчанию особенно удобно использовать с COM файлами, где DS указывает на начало префикса программного сегмента. Для файлов EXE может потребоваться небольшой добавочный код, чтобы использовать DTA по умолчанию. 

Таблица 6.1 Структура DTA.
practic_6

Формат FileTime:

practic_7

Примечание: после извлечения года к нему необходимо прибавить 1980.

Существует ряд функций для работы с файлами, используя DTA. Наиболее употребимые из них приведены ниже.

Функция 1ah

Установить адрес DTA.

Вход: AH=1aH

DS:DX=адрес для DTA

Выход: нет

Описание: Устанавливает адрес DTA.

Функция 2fh

Дать текущий DTA.

Вход: AH=2fH

Выход: ES:BX=адрес для DTA

Описание: Возвращает адрес начала области ввода-вывода (DTA). Поскольку DTA глобальна для всех процессов, в рекурсивной процедуре (например, при проходе по дереву оглавления) может потребоваться сохранить адрес DTA, а впоследствии восстановить его посредством функции 1aH.

Функция 4eh

Найти 1-й совпадающий файл.

Вход: AH=4eH

DS:DX=адрес строки ASCIIZ с именем файла

CX=атрибут файла для сравнения

Выход: AX=код ошибки, если CF установлен

DTA=заполнена данными (если не было ошибки)

Описание: DS:DX указывает на строку ASCIIZ в форме: "d:\путь\имяфайла",0. Если диск и/или путь опущены, они подразумеваются по умолчанию. Обобщенные символы * и ? допускаются в имени файла и расширении. DOS находит имя первого файла в оглавлении, которое совпадает с заданным именем и атрибутом, и помещает найденное имя и другую информацию в DTA.

Функция 4fh

Найти следующий совпадающий файл.

Вход: AH=4fH

DS:DX= адрес данных, возвращенных предыдущей 4eH.

Выход: AX=код ошибки, если CF установлен

DTA=заполнена данными (если не было ошибки)

Описание: DS:DX указывает на 2bH-байтовый буфер с информацией, возвращенной функцией 4eH (либо DTA, либо буфер, скопированный из DTA).Необходимо использовать эту функцию после вызова 4eH. Следующее имя файла, совпадающее по обобщенному имени и атрибуту файла, копируется в буфер по адресу DS:DX вместе с другой информацией (см. функцию 4eH о структуре файловой информации в буфере, заполняемом DOS).

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

;Установить адрес DTA

MOV AH,01AH

LEA DX,FDTA

INT 21H

. . .

;Наити первый совпадающий файл

MOV AH,4EH

LEA DX,MASKA

MOV CX,10H

INT 21H

JC EXIT

NEXT:

. . .

;Найти следующий совпадающий файл

MOV AH,4FH

MOV CX,10H

LEA DX,MASKA

INT 21H

JNC NEXT

EXIT:

. . .

;========== DTA =========

FDTA DB 15H DUP (?)

FATTR DB ?

FTIME DW ?

FDATA DW ?

FSIZE DD ?

FNAME DB 0DH DUP ('$')

;========================

MASKA DB '*.*',0

Структура PSP. Префикс программного сегмента PSP (Program Segment Prefics) – специальная область оперативной памяти размером 256 (100h) байт. PSP может использоваться в программе для определения имен файлов и параметров из командной строки, введенной при запуске программы на выполнение, объема доступной памяти, переменных окружения системы и т.д. После загрузки программы в память сегментные регистры DS и ES указывают на начало PSP этой программы.

Таблица 6.2 Структура PSP.

practic_8

Окружение DOS. DOS поддерживает область памяти, содержащую набор строк ASCIIZ, которые могут использоваться приложениями для получения некоторой системной информации и для передачи данных между программами. Эта область памяти называется окружением DOS.

Структура окружения DOS:

имя_1=значение_1«0»

имя_2=значение_2«0»

. . .

имя_N=значение_N«0»

«0»

«xxxx»

EXEC_string_1«0»

. . .

EXEC_string_NN«0»

«0»

Здесь «0» - это символ ASCII NUL (00H), а «xxxx» - 16-битовое двоичное значение (количество дополнительных строк).

Окружение не превышает 32K байт и начинается на границе параграфа. Смещение 2cH в PSP текущей программы содержит номер параграфа окружения. Используя окружение, можно найти нужное имя' серией сравнений строк ASCIIZ, пока не достигнута пустая строка (нулевой длины), что указывает конец окружения.

В последних версиях DOS, за концом официального окружения помещается дополнительная строка, которая содержит диск и путь, с которых была загружена программа. Вслед за последней строкой ASCIIZ окружения находится нулевой байт, указывающий конец официального окружения. Следующие два байта содержат 16-битовый двоичный счетчик дополнительных строк (обычно 0001H). Вслед за значением счетчика находится строка ASCIIZ, содержащая путь и имя файла. Это в точности та строка, которая использовалась функцией DOS 4bH (EXEC) для загрузки и запуска программы.

Практическая работа №7

Тема: Работа с прерываниями: перехват и восстановление.

Цель работы: Изучить алгоритм переопределения и восстановления прерываний.

Задание: Разработать программу переопределения прерывания 05h (клавиша PrintScreen).

Замечания:

1) Перед загрузкой нового вектора прерывания необходимо сохранить старый вектор (функция 35h прерывания 21h)
2) Новый обработчик прерывания должен быть FAR-процедурой.
3) Для проверки, новая процедура обработки прерывания 05h должна выводить в динамик сигнал (прерывание 21H). В основной программе необходимо организовать большой цикл, например, выводящий на экран символы (прерывание 21h использовать нельзя, можно использовать, например, прерывание 10h). Таким образом, при нажатии на PrintScreen во время этого цикла компьютер должен издавать сигнал.

Прерывания и их переопределение. Иногда необходимо выполнить одну из набора специальных процедур, если в системе или в программе возникают определенные условия, например, нажата клавиша на клавиатуре. Действие, стимулирующее выполнение одной из таких процедур, называется прерыванием. Существует два общих класса прерываний: внутренние и внешние. Первые инициируются состоянием ЦП или командой, а вторые - сигналом, подаваемым от других компонентов системы.

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

Последовательность прерывания состоит в следующем:

- текущее значение регистра Flags включается в стек;
- текущее значение регистра CS включается в стек;
- текущее значение регистра IP включается в стек;
- сбрасываются флаги IF и TF.

Новое содержимое IP и CS определяет начальный адрес выполняемой процедуры прерывания (обслуживание прерывания). Возврат в прерванную программу осуществляется командой, которая извлекает из стека содержимое для IP, CS и регистра флагов (обычно это команда IRET).

Адреса подпрограмм обслуживания прерываний (вектора прерываний) хранятся в таблице векторов прерываний. Таблица векторов прерываний располагается по адресу 0000:0000 и представляет собой массив из 256 элементов, каждый элемент которого занимает 4 байта и представляет собой начальный адрес процедуры обработки прерывания.

Иногда в программе возникает необходимость переопределения (перехвата) прерываний (например, выполнение дополнительных действий при нажатии определенной клавиши клавиатуры). Процесс перехвата прерываний состоит в следующем:

- подготавливается FAR-процедура – новый обработчик прерываний (должна заканчиваться командой IRET);
- сохраняется старый вектор прерывания (функция 35h прерывания 21h)
- адрес нового обработчика заносится в таблицу векторов прерываний (функция 25h прерывания 21h);
- в конце программы происходит восстановление первоначального обработчика прерываний.

Функция 35h

Вход: AH=35H

AL=номер прерывания (00H до 0ffH)

Выход: ES:BX=адрес обработчика прерывания

Описание: Возвращает значение вектора прерывания для INT (AL), то есть загружает в BX 0000:[AL*4], а в ES - 0000:[(AL*4)+2].

Функция 25h

Вход: AH=25H

AL=номер прерывания (00H до 0ffH)

DS:DX=вектор прерывания (адрес подпрограммы)

Выход: нет

Описание: Устанавливает значение элемента таблицы векторов прерываний для прерывания с номером AL равным DS:DX. Это равносильно записи 4-байтового адреса в 0000:(AL*4), но, в отличие от прямой записи, в момент записи прерывания будут заблокированы.

Ниже приведен фрагмент программы, иллюстрирующий установку нового вектора прерывания вместо обработчика PrintScreen (05h).

CODE SEGMENT

ASSUME CS:CODE,DS:DATA,SS:STACK

;Процедура – новый обработчик прерывания

PRNSCR PROC FAR

. . .

IRET

PRNSCR ENDP

START: 

XOR AX,AX ;Обычное начало для EXE-программы

MOV BX,DATA 

MOV DS,BX

. . .

;Установка нового вектора

LEA DX,CS:PRNSCR ;В DX – смещения нового обработчика 

PUSH DS ;Сохранение сегментного регистра DS

PUSH CS ;Следующие две строки: загрузка регистра DS

POP DS ;значением из CS через стек

MOV AH,25H ;номер функции

MOV AL,5 ;номер прерывания

INT 21H ;установка нового прерывания

POP DS ;восстановление DS

. . .

CODE ENDS

Практическая работа №8

Тема: Порты ввода-вывода. Обмен данными с внешним устройством.

Цель работы: Получить навыки низкоуровнего программирования портов ввода вывода. Изучение алгоритма обмена данными с печатающим устройством.

Задание: Разработать программу вывода заданной информации на печатающее устройство.

Команды ввода и вывода. Любое устройство ввода-вывода имеет один или несколько встроенных регистров, каждый из которых имеет соответствующий адрес. Всего существует 65536 адресов ввода-вывода. Из них 512 адресов назначены системному каналу ввода-вывода и могут использоваться различными адаптерами. Другие 256 адресов используются на системной плате для управления подключенными туда устройствами ввода-вывода.

Команда IN пересылает данные из устройства ввода-вывода в регистр AL. Если адрес устройства находится в пределах 0-255, то он может содержаться в команде как непосредственное значение. Если адрес больше 255, то он сообщается косвенно и содержится в регистре DX.

Команда OUT записывает содержимое регистра AL в регистр устройства ввода-вывода, адрес которого указывается также как и в команде IN. В случае использования микропроцессора Intel x86 команды IN и OUT, могут пересылать слова в устройства ввода вывода. Источником и приемником в этом случае является регистр AX.

Параллельный интерфейс ввода-вывода. Адаптер параллельного печатающего устройства имеет два выводных порта и один порт ввода состояния. Порты печатающего устройства, в случае наличия отдельной платы для печатающего устройства, имеют адреса от 378h до 37Ah для LPT1 и от 278h до 27Ah для LTP2.

Выводной 8-битовый порт данных по адресу 378h (278h) выдает данные печатающему устройству, посылая на него символ в коде ASCII, помещаемый в этот порт.

Второй порт данных, расположенный по адресу 37Ah (27Ah), имеет пять выводных бит, управляющих работой и инициализацией печатающего устройства:

- бит строба (бит 0) инициирует передачу данных в печатающее устройство: для того чтобы в него поступил символ, нужно установить бит строба равным «1», а затем снова равным «0»;
- если бит 1 установлен в «1», то при выводе строки на устройство, после символа возврата каретки CR будет выводится символ перевода строки LF;
- если установить бит 2 в «1», то произойдет сброс печатающего устройства;
- установка бита 3 в «1» означает выбор данного устройства;
- бит 4 управляет линией прерывания от печатающего устройства: установка его в «1» означает разрешение на прерывание;
- остальные биты (биты 5,6 и 7) не задействованы и должны быть равны «0».
Третий порт параллельного интерфейса, расположенный по адресу 379h (279h), является портом ввода состояния печатающего устройства и содержит пять информационных бит:
- бит 3, будучи установленным в «0», сигнализирует об ошибке вывода;
- бит 4 сообщает о том, что данное устройство выбрано для работы;
- если бит 5 установлен в 1, это означает конец бумаги;
- бит 6, будучи установленным в «0», сигнализирует о готовности устройства принимать следующий символ;
- бит 7 порта показывает состояние занятости печатающего устройства: если он равен «1», то печатающее устройство готово принимать следующий символ, если же он равен «0», то устройство либо занято, либо находится в нерабочем состоянии;
- остальные биты (биты 0,1 и 2) не задействованы и должны быть равны «0».

Алгоритм вывода символа на принтер обычно состоит в выполнении следующей последовательности шагов:

- запись очередного выводимого символа в выводной порт данных, расположенный по адресу 378h (278h);
- проверка занятости принтера и ожидание его освобождения: опрос бита 7 порта ввода состояния принтера, расположенного по адресу 379h (279h);
- выдача строба данных: установка и сброс бита 0 второго порта вывода, расположенного по адресу 37Ah (27Ah).

Пример процедуры вывода символа на печатающее устройство:

PRINT PROC

MOV DX,BASE ;Порт вывода данных на принтер

OUT DX,AL ;Занесение символа в порт данных принтера

INC DX ;Адрес порта состояния принтера

WAIT:IN AL,DX ;Опрос состояния принтера

TEST AL,80H ;Проверка разряда занятости принтера

JZ WAIT

INC DX

MOV AL,0DH ;Установка разряда готовности данных

OUT DX,AL

MOV AL,0CH ;Сброс разряда готовности данных

OUT DX,AL

RET

PRINT ENDP

Список литературы

1. Пильщиков В.Н. Программирование на языке Ассемблера IBM PC – М.: «Диалог МИФИ», 1996 – 286 с.
2. Борзенко А.Е. IBM PC: устройство, ремонт, модернизация – М: ТОО фирма «Компьютер пресс», 1995 – 297 с.
3. Белицкий Я. Турбо Ассемблер. Версия 2.0. / Пер. с польского, Учеб. пособ. для ВУЗов – М: «Машиностроение», 1994 – 365 с.
4. Абель П. Язык Ассемблера для IBM PC и программирование / Пер. с анг. Ю.В. Сальникова – М: Высш. шк., 1992 – 447 с.
5. Бредли Д. Программирование на языке Ассемблера для ПЭВМ фирмы IBM / Пер. с анг. В.В. Скугарева – М: Радио и связь, 1988 – 488 с.
6. Использование Turbo Assembler при разработке программ / Сост. А.А. Чекатков. – Киев: «Диалектика», 1995 – 288 с.
7. Макроассемблер MASM – М: Радио и связь, 1994 – 241 с.
8. Григорьев В.Л. Микропроцессор i486. Архитектура и программирование. (в 4-х книгах) – М: Пранал, 1993.
9. Злобин В.К., Григорьев В.Л. Программирование арифметических операций в микропроцессорах: Учеб. пособие для тех. ВУЗов – М: Высш. шк., 1991 – 301 с.
 

[Главная] [Документация] [Теория] [Практика] [Рассылки] [Книги] [Исходники] [Reversing] [Друзья] [Архив] [Диски] [Новости]

Посещаемость сайта такова:
На деревню дедушке

Hosted by uCoz

Rambler's Top100
РЕГИСТРАТУРА.РУ: бесплатная автоматическая регистрация в каталогах ссылок и поисковых машинах, проведение рекламных кампаний в Интернете, привлечение на сайт целевых посетителей.

Hosted by uCoz