- •ПЕРЕЧЕНЬ СОКРАЩЕНИЙ
- •ПРЕДИСЛОВИЕ
- •Регистр флагов
- •Программа вывода строки
- •Примеры резервирования памяти
- •Регистровая адресация
- •Непосредственная адресация
- •Прямая адресация
- •Косвенная регистровая адресация
- •Адресация по базе
- •Пример программы задержки выполнения
- •Сложение, вычитание, умножение, деление
- •Рис. 15.1. Структурная схема ввода данных с клавиатуры
- •Пример программы для ввода с клавиатуры
- •Пример посимвольного ввода
- •Сортировка выбором
- •Сортировка вставками
- •Использование параметров в макрокомандах
- •Библиографический список
- •Приложение 1
- •Перечень команд
- •Приложение 2
zi – управляет включением в объектный файл номеров строк исходной программы, исполняемых потом отладчиком;
N – подавляет ввод перечня символических обозначений. Идущие подряд имена файлов определяют:
–первое имя – исходный текстовый файл;
–второе имя – указывает имя объектного файла (будет создан компилятором);
–третье имя – имя файла листинга;
знак « ; » – подавляет формирование файла с перекрестными ссылками.
Строка вызова компоновщика позволяет получить файл типа .ЕХЕ, например
LINK /CO P1, P2,
где ключ СО – передает в загрузочный файл символьную информацию, позволяющую отладчику выводить на экран полный текст исходной программы; P1 – имя файла, который получили после компоновки (объектного файла); P2 – имя, которое мы получим компоновщиком (загрузочного, исполняемого файла); знак « ; » – подавляет формирование файла с листингом компоновки или картой памяти (.MAP).
Использование командного файла с расширением .bat , включающего, например, следующие строки:
c:\tasm32 р1.asm c:\tlink /t р1.obj
р1 pause
позволяет сразу запустить на выполнение программу р1 при условии, что tasm и tlink находятся в корневом каталоге.
Пример программы типа .ЕХЕ, позволяющей выводить заданную строку, представленную массивом байт в mes длиной, заданной количеством символов в переменной meslen на экран монитора, представлен ниже.
Программа вывода строки
14
text |
segment |
'code' |
assume cs: text, ds: data |
|
|
;определения
stdout = 1 ;дескриптор стандартного вывода
stdin = 0 |
;дескриптор стандартного ввода |
cr = 13 |
;возврат каретки |
lf = 10 |
;перевод строки |
myproc |
proc |
mov АХ, data mov DS, AX
;вывод на экран строки текста
mov AH, 40h |
|
;номер функции вывода |
mov BХ, stdout |
|
;номер устройства вывода |
mov CХ, meslen |
|
;количество выводимых символов |
mov DХ, offset mes |
;адрес выводимой строки |
|
int 21h |
|
;программное прерывание (вывод) |
;завершение программы |
|
|
mov AХ, 4C00h |
|
|
int 21h |
|
|
myproc endp |
|
|
text |
ends |
|
;данные |
|
|
data |
segment |
|
mes |
db 'начало работы',cr,lf |
|
meslen |
equ $-mes |
|
data |
ends |
|
stack |
segment para stack 'stack' |
|
db 128 dup(?) |
|
|
stack |
ends |
|
end myproc |
|
|
В данном примере непосредственный вывод строки на монитор осуществляется программным прерыванием int 21h с номером функции 40h, заданным в аккумуляторе.
4. ОПИСАНИЕ ДАННЫХ
15
В предыдущем примере символьная строка, предназначенная для вывода на экран, представляла собой данные, которыми оперировала программа. Данные могут представлять собой числа, определяющие ход выполнения программы или участвующие в вычислениях; адреса подпрограмм, обработчиков прерываний или просто тех или иных полей программы; специальные коды и т. д. Кроме данных, определяемых в тексте программы, в программу часто входят зарезервированные поля, предназначенные для заполнения по ходу выполнения программы, например, результатами вычислений или путем чтения из файла Все эти данные и зарезервированные поля должны быть определены в составе сегмента данных программы (в принципе они могут быть определены и в сегменте команд). Для определения данных используются три основные директивы Ассемблера:
db (define byte, определить байт) – для записи байтов; dw (define word, определить слово) – для записи слов;
dd (define double, определить двойное слово) – для записи двойных слов.
Например, db 255, dw 6.5535, dd 100000000.
Имена данных необходимы для обращения к ним и могут включать латинские буквы, цифры (не в качестве первого знака имени) и некоторые специальные знаки, например, знаки подчеркивания, доллара ($) и коммерческого (@). Длину имени некоторые Ассемблеры ограничивают (например, Ассемблер MASM – 31 символом), другие нет, но в любом случае слишком длинные имена затрудняют чтение программы. Имена данных следует выбирать таким образом, чтобы они отражали назначение конкретного данного, например, counter – для счетчика или filename – для имени файла: counter dw 10000, filename db «a:\ myfile.001'.
Для записи числовых данных можно использовать различные системы счисления, но чаще других используются десятичная и шестнадцатеричная запись:
size dw 256 ; В ячейку size записывается десятичное число 256 setb7 db 80h ; В ячейку setb7 записывается шестнадцатеричное чис-
ло 80h
Примечание. Если мы говорим, что в какой-то ячейке записано десятичное число 256, мы имеем в виду не физическое содержимое ячейки, а лишь форму представления этого числа в исходном тексте программы. В слове с именем size фактически будет записан двоичный код 0000000100000000, яв-
16
ляющийся двоичным эквивалентом десятичного числа 256. Во втором случае в байте с именем setb7 будет записан двоичный эквивалент шестнадцатеричного числа 80h, который составляет 10 000 000.
Для резервирования места под массивы используется оператор dup (duplicate, дублировать), который позволяет «размножить» байт, слово или двойное слово заданное число раз:
rawdata dw 300 dup (1) ; Резервируются 300 слов, заполненных числом 1
string db 80 dup ('^') ;Резервируются 80 байтов, '^'
Присвоение данным символических имен позволяет обращаться к ним в программных предложениях, не заботясь о фактических адресах этих данных. Например, команда mov AX, size занесет в регистр АХ содержимое ячейки size (число 256) независимо от того, в каком месте сегмента данных эта ячейка определена и в какое место физической памяти она попала.
Программист, использующий язык Ассемблера, должен иметь представление о том, каким образом назначаются адреса ячейкам программы, и уметь работать не только с символическими обозначениями, но и со значениями адресов. Рассмотрим пример сегмента данных, в котором определяются данные различных типов. В левой колонке указано смещение данных (в шестнадцатеричной форме), вычисляемое
относительно начала сегмента. |
|
|
data segment |
|
|
0000h counter dw 10000 |
; используется 2 |
байта |
0002h pages db «Страница 1» |
; используется 10 байт |
|
000Ch numbers db 0, 1, 2, 3, 4 |
; используется 5 |
байт |
0011h page_addr dw pages |
|
|
data ends |
|
|
Сегмент данных начинается с данного по имени counter, которое описано как слово (2 байта) и содержит число 10 000. Очевидно, что его смещение равно 0. Поскольку это данное занимает два байта, следующее за ним данное pages получило смещение 2. Данное pages описывает строку текста длиной 10 символов и занимает в памяти столько же байтов, поэтому следующее данное numbers получило относительный адрес 2 + 10 = 12 = Ch. В поле numbers записаны пять байтовых чисел, поэтому последнее данное сегмента с именем page_addr размещается по адресу Ch + 5 = 11h. Ассемблер, транслируя сегмент (в данном случае сегмента данных), начинает отсчет его относительных ад-
17
ресов. Этот отсчет ведется в специальной переменной транслятора, которая называется счетчиком текущего адреса и имеет символическое обозначение знака доллара ($). По мере обработки полей данных их символические имена сохраняются в создаваемой Ассемблером таблице имен вместе с соответствующими им значениями счетчика текущего адреса. Введенные нами символические имена получают значения,
равные их смещениям. Таким образом, с точки зрения транслятора counter равно 0, pages – 2, numbers – Ch и т. д.
Адрес первого числа в массиве numbers равен просто numbers, адрес второго числа – numbers + 1, адрес третьего – numbers + 2 и т. д. Следующая команда прочитает последний элемент этого массива в регистр DL:
mov DL,numbers + 4
Какой смысл имело объединение ряда чисел в массив numbers? Никакого, если к этим числам мы все равно обращаемся по отдельности. Удобнее было объявить этот массив таким образом:
nmb0 db 0 nmbl db 1 nmb2 db 2 nmb3 db 3
nmb4 db 4
В этом случае для обращения к последнему элементу не надо вычислять его адрес, а можно воспользоваться именем nmb4.
Если мы хотим работать с числами, как с массивом, используя индексы отдельных элементов, то присвоение массиву общего имени представляется естественным. Получение последнего элемента массива по его индексу выполняется с помощью такой последовательности команд:
mov SI,4 ; Индекс элемента в массиве
mov DL, numbers[SI] ; Обращение по адресу (numbers + содержимое
SI)
Ассемблер выполняет проверку размеров используемых данных и не пропускает команды, в которых делается попытка обратиться к байтам, как к словам, или к словам, как к байтам. Предположим, что у нас описаны словные переменные:
nmb0 dw 0 ; занимает 2 байта nmbl dw 1 ; занимает 2 байта
18
nmb2 dw 2 ; занимает 2 байта
В этом случае перечисленные ниже команды (работа с переменными, как с байтом)
mov AL,nmb0 ; Переносим nmb0 в AL (размер AL равен 1 байт) mov АL,nmbl ; Переносим nmb1 в AL
mov АL,nmb2 ; Переносим nmb2 в AL
будут восприняты транслятором как грубая ошибка – несоответствие типов, он не будет создавать объектный файл.
Для таких случаев предусмотрен специальный атрибутивный оператор byte ptr (byte pointer, байтовый указатель), с помощью которого можно на время выполнения одной команды изменить размер операнда:
mov AL,byte ptr nmb0 mov АL,byte ptr nmbl mov АL,byte ptr nmb2
Эти команды транслятор рассматривает как правильные.
Часто возникает необходимость выполнить обратную операцию – к паре байтов обратиться, как к слову. Для этого надо использовать опе-
ратор word ptr: okey db 'OK'
mov AX,word ptr okey
Здесь оба байта из байтовой переменной okey переносятся в регистр АХ. При этом первый по порядку байт, т. е. байт с меньшим адресом, содержащий букву «О» (можно считать, что он является младшим в слове «OK»), отправится в младшую половину АХ – регистр AL, а второй по порядку байт, с буквой «К», займет регистр АН.
До сих пор речь шла о данных, которые, в сущности, являлись переменными в том смысле, что под них выделялась память и их можно было модифицировать. Язык Ассемблера позволяет также использовать константы, которые являются символическими обозначениями чисел и могут использоваться всюду в тексте программы, как нагляд-
ные эквиваленты этих чисел: maxsize = 0FFFFh
mov CX, maxsize mov CX, 0FFFFh
Последние две команды полностью эквивалентны.
Константами удобно пользоваться для определения длины текстовых строк:
mes db 'Ждите'
19
