Как вывести массив в ассемблере
Вывод строковых массивов
Уважаемые читатели, вслед за выводом чисел на экран мы поговорим о том как выводят строки, а также затронем массивы в Assembler. Урок будет небольшим и очень понятным.
Немного информации…
Прежде чем перейти к листингу сегодняшней темы, все же стоит отметить: как таковых массивов в Assembler нет, есть нечто похожее. И это нам, программистам, удобнее всего называть как массив. Обычно понятия массивов используют в таких языках как C++, Си и т.д.
В наших следующих статьях мы будем разбирать ассемблеровские вставки для C++, а сегодня мы просто ознакомимся с последовательностью символов.
Основная программа
Те, кто уже не первую статью читают, знают, что все эти строки нужны, так как мы работаем на masm32, а также подключаем стандартные библиотеки.
Идем дальше:
В разделе переменные мы объявляем уже знакомые нам переменные для вывода на экран(первые 3 строчки в разделе data).
Далее идет переменная CRLF, она нам понадобится для перевода строки. Как она работает вы увидите чуть дальше.
А вот затем идет объявления так называемых массивов (массивы first и second), в данном случае массивы у нас строковые, и каждый символ занимает 1 байт, об этом говорит запись BYTE после имени массивов. В кавычках мы записали некоторый текст. По сути так объявляются массивы, и ничего сложного в понимании нет.
Далее мы выведем на экран наши массивы:
Вывод осуществляется практически как и в предыдущей статье, за исключением одного: команда offset, ранее нам незнакомая, берет адрес 1 символа в массиве, и затем, мы записываем его в соответствующий регистр, это нужно чтобы при выводе мы могли указать адрес массива.
В функции WriteConsoleA записываем регистр, который содержит этот адрес, а затем записываем число, сколько символов хотим вывести, в данном примере для 1 массива это 50, а для второго 10.
И еще кое что: вы, наверное, заметили, что мы трижды использовали функцию вывода. Так вот, во 2 функции мы как раз используем переменную CRLF, которую по сути и выводим. Предварительно в эту переменную мы записали 0d0ah, это говорит Assembler, что мы хотим перейти на новую строку.
Повторим запуск программы
Открываем командную строку, переходим в папку BIN(напомню, что файл нужно поместить туда, где хранится файл запуска amake.bat) с помощью команды cd BIN.
Далее прописываем amake.bat sixth(вы, естественно, напишите свое имя файла). А затем, запускаем файл sixth.exe
И вот, что должно выйти:
На этом мы закончим, ваши вопросы оставляйте в комментариях. У нас есть еще много того, о чем можно говорить, и в будущем примеры только усложнятся.
Дадим формальное определение:
массив — структурированный тип данных, состоящий из некоторого числа элементов одного типа.
Для того чтобы разобраться в возможностях и особенностях обработки массивов в программах на ассемблере, нужно ответить на следующие вопросы:
· Как описать массивв программе?
· Как инициализировать массив, то есть как задать начальные значения его элементов?
· Как организовать доступк элементам массива?
· Как организовать массивыс размерностью более одной?
· Как организовать выполнениетиповых операций с массивами?
Описание и инициализация массива в программе
Специальных средств описания массивов в программах ассемблера, конечно, нет. При необходимости использовать массив в программе его нужно моделировать одним из следующих способов:
1. Перечислением элементов массива в поле операндов одной из директив описания данных. При перечислении элементы разделяются запятыми. К примеру:
;массив из 5 элементов.Размер каждого элемента 4 байта:
2. Используя оператор повторения dup. К примеру:
;массив из 5 нулевых элементов.
;Размер каждого элемента 2 байта:
Такой способ определения используется для резервирования памяти с целью размещения и инициализации элементов массива.
3. Используя директивы labelиrept. Пара этих директив может облегчить описание больших массивов в памяти и повысить наглядность такого описания. Директиваreptотносится к макросредствам языка ассемблера и вызывает повторение указанное число раз строк, заключенных между директивой и строкой endm. К примеру, определим массив байт в области памяти, обозначенной идентификаторомmas_b. В данном случае директиваlabelопределяет символическое имяmas_b, аналогично тому, как это делают директивы резервирования и инициализации памяти. Достоинство директивыlabelв том, что она не резервирует память, а лишь определяет характеристики объекта. В данном случае объект — это ячейка памяти. Используя несколько директивlabel, записанных одна за другой, можно присвоить одной и той же области памяти разные имена и разный тип, что и сделано в следующем фрагменте:
В результате в памяти будет создана последовательность из четырех слов f1f0. Эту последовательность можно трактовать как массив байт или слов в зависимости от того, какое имя области мы будем использовать в программе —mas_bилиmas_w.
4. Использование цикла для инициализации значениями области памяти, которую можно будет впоследствии трактовать как массив.
5. Посмотрим на примере листинга 2, каким образом это делается.
Листинг 2 Инициализация массива в цикле
mes db 0ah,0dh,’Массив- ‘,’$’
mas db 10 dup (?) ;исходный массив
xor ax,ax ;обнуление ax
mov cx,10 ;значение счетчика цикла в cx
mov si,0 ;индекс начального элемента в cx
go: ;цикл инициализации
mov mas[si],bh ;запись в массив i
inc si ;продвижение к следующему элементу массива
loop go ;повторить цикл
;вывод на экран получившегося массива
mov ah,02h ;функция вывода значения из al на экран
add dl,30h ;преобразование числа в символ
mov ax,4c00h ;стандартный выход
end main ;конец программы
Доступ к элементам массива
При работе с массивами необходимо четко представлять себе, что все элементы массива располагаются в памяти компьютера последовательно.
Само по себе такое расположение ничего не говорит о назначении и порядке использования этих элементов. И только лишь программист с помощью составленного им алгоритма обработки определяет, как нужно трактовать эту последовательность байт, составляющих массив. Так, одну и ту же область памяти можно трактовать как одномерный массив, и одновременно те же самые данные могут трактоваться как двухмерный массив. Все зависит только от алгоритма обработки этих данных в конкретной программе. Сами по себе данные не несут никакой информации о своем “смысловом”, или логическом, типе. Помните об этом принципиальном моменте.
Эти же соображения можно распространить и на индексы элементов массива. Ассемблер не подозревает об их существовании и ему абсолютно все равно, каковы их численные смысловые значения.
Для того чтобы локализовать определенный элемент массива, к его имени нужно добавить индекс. Так как мы моделируем массив, то должны позаботиться и о моделировании индекса. В языке ассемблера индексы массивов — это обычные адреса, но с ними работают особым образом. Другими словами, когда при программировании на ассемблере мы говорим об индексе, то скорее подразумеваем под этим не номер элемента в массиве, а некоторый адрес.
Давайте еще раз обратимся к описанию массива. К примеру, в программе статически определена последовательность данных:
Пусть эта последовательность чисел трактуется как одномерный массив. Размерность каждого элемента определяется директивой dw, то есть она равна2байта. Чтобы получить доступ к третьему элементу, нужно к адресу массива прибавить6. Нумерация элементов массива в ассемблере начинается с нуля.
То есть в нашем случае речь, фактически, идет о 4-м элементе массива — 3, но об этом знает только программист; микропроцессору в данном случае все равно — ему нужен только адрес.
В общем случае для получения адреса элемента в массиве необходимо начальный (базовый) адрес массива сложить с произведением индекса (номер элемента минус единица) этого элемента на размер элемента массива:
база + (индекс*размер элемента)
Архитектура микропроцессора предоставляет достаточно удобные программно-аппаратные средства для работы с массивами. К ним относятся базовые и индексные регистры, позволяющие реализовать несколько режимов адресации данных. Используя данные режимы адресации, можно организовать эффективную работу с массивами в памяти. Вспомним эти режимы:
· индексная адресация со смещением — режим адресации, при котором эффективный адрес формируется из двух компонентов:
o постоянного (базового)— указанием прямого адреса массива в виде имени идентификатора, обозначающего начало массива;
o переменного (индексного)— указанием имени индексного регистра.
;поместить 3-й элемент массива mas в регистр ax:
· базовая индексная адресация со смещением — режим адресации, при котором эффективный адрес формируется максимум из трех компонентов:
o постоянного(необязательный компонент), в качестве которой может выступать прямой адрес массива в виде имени идентификатора, обозначающего начало массива, или непосредственное значение;
o переменного (базового)— указанием имени базового регистра;
o переменного (индексного)— указанием имени индексного регистра.
Этот вид адресации удобно использовать при обработке двухмерных массивов. Пример использования этой адресации мы рассмотрим далее при изучении особенностей работы с двухмерными массивами.
Напомним, что в качестве базового регистра может использоваться любой из восьми регистров общего назначения. В качестве индексного регистра также можно использовать любой регистр общего назначения, за исключением esp/sp.
Микропроцессор позволяет масштабировать индекс. Это означает, что если указать после имени индексного регистра знак умножения “*” с последующей цифрой 2, 4 или 8, то содержимое индексного регистра будет умножаться на 2, 4 или 8, то есть масштабироваться.
Применение масштабирования облегчает работу с массивами, которые имеют размер элементов, равный 2, 4 или 8 байт, так как микропроцессор сам производит коррекцию индекса для получения адреса очередного элемента массива. Нам нужно лишь загрузить в индексный регистр значение требуемого индекса (считая от 0). Кстати сказать, возможность масштабирования появилась в микропроцессорах Intel, начиная с модели i486. По этой причине в рассматриваемом здесь примере программы стоит директива .486. Ее назначение, как и ранее использовавшейся директивы.386, в том, чтобы указать ассемблеру при формировании машинных команд на необходимость учета и использования дополнительных возможностей системы команд новых моделей микропроцессоров.
В качестве примера использования масштабирования рассмотрим листинг 3, в котором просматривается массив, состоящий из слов, и производится сравнение этих элементов с нулем. Выводится соответствующее сообщение.
Листинг 3. Просмотр массива слов с использованием
.data ;начало сегмента данных
mes1 db ‘не равен 0!$’,0ah,0dh
mes2 db ‘равен 0!$’,0ah,0dh
mas dw 2,7,0,0,1,9,3,6,0,8 ;исходный массив
.486 ;это обязательно
mov ds,ax ;связка ds с сегментом данных
xor ax,ax ;обнуление ax
mov cx,10 ;значение счетчика цикла в cx
mov esi,0 ;индекс в esi
mov dx,mas[esi*2] ;первый элемент массива в dx
cmp dx,0 ;сравнение dx c 0
je equal ;переход, если равно
not_equal: ;не равно
mov ah,09h ;вывод сообщения на экран
mov ah,02h ;вывод номера элемента массива на экран
inc esi ;на следующий элемент
dec cx ;условие для выхода из цикла
jcxz exit ;cx=0? Если да — на выход
jmp compare ;нет — повторить цикл
mov ah,09h ;вывод сообщения mes3 на экран
mov ah,09h ;вывод сообщения mes2 на экран
inc esi ;на следующий элемент
dec cx ;все элементы обработаны?
mov ax,4c00h ;стандартный выход
end main ;конец программы
Еще несколько слов о соглашениях:
· Если для описания адреса используется только один регистр, то речь идет о базовой адресациии этот регистр рассматривается какбазовый:
;переслать байт из области данных, адрес
которой находится в регистре ebx:
· Если для задания адреса в команде используется прямая адресация(в виде идентификатора) в сочетании с одним регистром, то речь идет обиндексной адресации. Регистр считаетсяиндексным, и поэтому можно использовать масштабирование для получения адреса нужного элемента массива:
;сложить содержимое eax с двойным словом в памяти
;по адресу mas + (ebx)*4
· Если для описания адреса используются два регистра, то речь идет о базово-индексной адресации. Левый регистр рассматривается как базовый, а правый — как индексный. В общем случае это не принципиально, но если мы используем масштабирование с одним из регистров, то он всегда являетсяиндексным. Но лучше придерживаться определенных соглашений.
· Помните, что применение регистров ebp/bpиesp/spпо умолчанию подразумевает, что сегментная составляющая адреса находится в регистреss.
Заметим, что базово-индексную адресацию не возбраняется сочетать с прямой адресацией или указанием непосредственного значения. Адрес тогда будет формироваться как сумма всех компонентов.
;адрес операнда равен [mas+(ebx)+(ecx)*2]
;адрес операнда равен [(ebx)+8+(ecx)*4]
Но имейте в виду, что масштабирование эффективно лишь тогда, когда размерность элементов массива равна 2, 4 или 8 байт. Если же размерность элементов другая, то организовывать обращение к элементам массива нужно обычным способом, как описано ранее.
Рассмотрим пример работы с массивом из пяти трехбайтовых элементов (листинг 4). Младший байт в каждом из этих элементов представляет собой некий счетчик, а старшие два байта — что-то еще, для нас не имеющее никакого значения. Необходимо последовательно обработать элементы данного массива, увеличив значения счетчиков на единицу.
Листинг 4. Обработка массива элементов с нечетной длиной
MODEL small ;модель памяти
STACK 256 ;размер стека
.data ;начало сегмента данных
N=5 ;количество элементов массива
mas db 5 dup (3 dup (0))
main: ;точка входа в программу
xor ax,ax ;обнуление ax
mov dl,mas[si] ;первый байт поля в dl
inc dl ;увеличение dl на 1 (по условию)
mov mas[si],dl ;заслать обратно в массив
add si,3 ;сдвиг на следующий элемент массива
Ручной ввод-вывод массивов
Добрый день! Посоветуйте пожалуйста какую-нибудь литературу, где имеется подробное описание ввода и вывода информации (одномерных массивов с возможностью введения размерности, двумерных массивов). Я прочитал книгу П. Абеля, но там не было подходящей информации, и так как книга была переведена с английского в тысяча девятьсот- лохматом году, в ней имеется множество опечаток касательно кода, и достаточно неточностей, относительно пояснений.. Ввод-вывод строки я уже освоил благо(благо на форуме имеется достаточно информации по этому, да и из универа что-то запомнилось), а вот с массивами запара..
Добавлено через 2 минуты
желательно для масма
Ввод, сложение, вывод массивов
Написать программу на языке Ассемблера, которая позволяет ввести с клавиатуры два массива чисел.
Ввод двух массивов, вывод суммы элементов с заданным индексом
Доброго времени суток, нужна помощь в написании программы на TASMе, суть задачи: необходимо ввести.
Решето Эратосфена. Добавить ручной ввод количества простых чисел и вывод итога в файл
Помогите добавить возможность ручного ввода исходного количества используемых чисел (а то у меня.
Ввод-вывод массивов
Доброго всем времени суток! по долгу учебы приходиться возвращаться к забытому программированию.
Как вывести массив в ассемблере
Справочная система по языку Assembler
Типы данных
Рис. 1. Основные типы данных микропроцессора
Рис. 2. Основные логические типы данных микропроцессора
Отметим, что “Зн” на рис. 2 означает знаковый бит.
После всего сказанного было бы логичным возникновение у читателя вопроса: как описать эти простые типы данных ассемблера, а затем и воспользоваться ими в программе? Ведь любая программа предназначена для обработки некоторой информации, поэтому вопрос о том, как описать данные с использованием средств языка обычно встает одним из первых.
TASM предоставляет очень широкий набор средств описания и обработки данных, который вполне сравним с аналогичными средствами некоторых языков высокого уровня.
Директивы резервирования и инициализации данных простых типов имеют формат, показанный на рис. 3.
Рис. 3. Директивы описания данных простых типов
Для иллюстрации данного принципа рассмотрим листинг 1, в котором определим сегмент данных. В этом сегменте данных приведено несколько директив описания простых типов данных.
Теперь наша цель — посмотреть, как выглядит сегмент данных программы листинга 1 в памяти компьютера. Это даст нам возможность обсудить практическую реализацию обозначенного нами принципа размещения данных. Для этого запустим отладчик TD.EXE, входящий в комплект поставки TASM. Результат показан на рис. 4.
Рис. 4. Окно дампа памяти для программы листинга 1
Разберемся более подробно с тем, как определить данные этих типов в программе и организовать работу с ними.
Массивы
Описание и инициализация массива в программе
Доступ к элементам массива
Давайте еще раз обратимся к описанию массива. К примеру, в программе статически определена последовательность данных:
В общем случае для получения адреса элемента в массиве необходимо начальный (базовый) адрес массива сложить с произведением индекса (номер элемента минус единица) этого элемента на размер элемента массива:
база + (индекс*размер элемента)
Этот вид адресации удобно использовать при обработке двухмерных массивов. Пример использования этой адресации мы рассмотрим далее при изучении особенностей работы с двухмерными массивами.
Напомним, что в качестве базового регистра может использоваться любой из восьми регистров общего назначения. В качестве индексного регистра также можно использовать любой регистр общего назначения, за исключением esp/sp.
В качестве примера использования масштабирования рассмотрим листинг 3, в котором просматривается массив, состоящий из слов, и производится сравнение этих элементов с нулем. Выводится соответствующее сообщение.
Заметим, что базово-индексную адресацию не возбраняется сочетать с прямой адресацией или указанием непосредственного значения. Адрес тогда будет формироваться как сумма всех компонентов.
Но имейте в виду, что масштабирование эффективно лишь тогда, когда размерность элементов массива равна 2, 4 или 8 байт. Если же размерность элементов другая, то организовывать обращение к элементам массива нужно обычным способом, как описано ранее.
Рассмотрим пример работы с массивом из пяти трехбайтовых элементов (листинг 4). Младший байт в каждом из этих элементов представляет собой некий счетчик, а старшие два байта — что-то еще, для нас не имеющее никакого значения. Необходимо последовательно обработать элементы данного массива, увеличив значения счетчиков на единицу.
Двухмерные массивы
С представлением одномерных массивов в программе на ассемблере и организацией их обработки все достаточно просто. А как быть если программа должна обрабатывать двухмерный массив? Все проблемы возникают по-прежнему из-за того, что специальных средств для описания такого типа данных в ассемблере нет. Двухмерный массив нужно моделировать. На описании самих данных это почти никак не отражается — память под массив выделяется с помощью директив резервирования и инициализации памяти.
Непосредственно моделирование обработки массива производится в сегменте кода, где программист, описывая алгоритм обработки ассемблеру, определяет, что некоторую область памяти необходимо трактовать как двухмерный массив.
При этом вы вольны в выборе того, как понимать расположение элементов двухмерного массива в памяти: по строкам или по столбцам.
Если последовательность однотипных элементов в памяти трактуется как двухмерный массив, расположенный по строкам, то адрес элемента (i, j) вычисляется по формуле
(база + количество_элементов_в_строке * размер_элемента * i+j)
Например, пусть имеется массив чисел (размером в 1 байт) mas(i, j) с размерностью 4 на 4
(i= 0. 3, j = 0. 3) :
В памяти элементы этого массива будут расположены в следующей последовательности:
23 04 05 67 05 06 07 99 67 08 09 23 87 09 00 08
Эффективный адрес mas(2, 3) = mas + 4 * 1 * 2 + 3 = mas + 11
Посмотрите на представление массива в памяти и убедитесь, что по этому смещению действительно находится нужный элемент массива.
В программе это будет выглядеть примерно так:
В качестве законченного примера рассмотрим программу поиска элемента в двухмерном массиве чисел (листинг 5). Элементы массива заданы статически.
Типовые операции с массивами
Для демонстрации основных приемов работы с массивами лучше всего подходят программы поиска или сортировки.
Рассмотрим одну такую программу, выполняющую сортировку массива по возрастанию (листинг 6).
В основе программы лежит алгоритм, похожий на метод пузырьковой сортировки. Эта программа не претендует на безусловную оптимальность, так как существует целая теория, касающаяся подобного типа сортировок. Перед нами стоит другая цель — показать использование средств ассемблера для решения подобного рода задач.
В программе два цикла.
Внешний цикл определяет позицию в массиве очередного элемента, с которым производится попарное сравнение элементов правой части массива (относительно этого элемента). За каждую итерацию внешнего цикла на месте этого очередного элемента оказывается меньший элемент из правой части массива (если он есть).
В остальном программа достаточно проста и на языке высокого уровня заняла бы около десятка строк.
Структуры
По определению структура — это тип данных, состоящий из фиксированного числа элементов разного типа.
Описать структуру в программе можно только один раз, а определить — любое количество раз.
Описание шаблона структуры
Описание шаблона структуры имеет следующий синтаксис:
Как мы уже отметили при описании шаблона, память не выделяется, так как это всего лишь информация для транслятора.
Местоположение шаблона в программе может быть поизвольным, но, следуя логике работы однопроходного транслятора, он должен быть расположен до того места, где определяется переменная с типом данной структуры. То есть при описании в сегменте данных переменной с типом некоторой структуры ее шаблон необходимо поместить в начале сегмента данных либо перед ним.
Рассмотрим работу со структурами на примере моделирования базы данных о сотрудниках некоторого отдела.
Для простоты, чтобы уйти от проблем преобразования информации при вводе, условимся, что все поля символьные.
Определим структуру записи этой базы данных следующим шаблоном:
Определение данных с типом структуры
Для использования описанной с помощью шаблона структуры в программе необходимо определить переменную с типом данной структуры. Для этого используется следующая синтаксическая конструкция:
Для примера определим несколько переменных с типом описанной выше структуры.
Методы работы со структурой
Рис. 5. Синтаксис адресного выражения в операторе обращения к полю структуры
Давайте представим, что сотрудников не четверо, а намного больше, и к тому же их число и информация о них постоянно меняются. В этом случае теряется смысл явного определения переменных с типом worker для конкретных личностей.
Язык ассемблера разрешает определять не только отдельную переменную с типом структуры, но и массив структур.
К примеру, определим массив из 10 структур типа worker :
Дальнейшая работа с массивом структур производится так же, как и с одномерным массивом. Здесь возникает несколько вопросов:
Как быть с размером и как организовать индексацию элементов массива?
Как выполнить копирование поля из одной структуры в соответствующее поле другой структуры? Или как выполнить копирование всей структуры? Давайте выполним копирование поля nam третьего сотрудника в поле nam пятого сотрудника:
Наличие в языке следующих двух типов данных, наверное, объясняется стремлением “хозяйки” максимально эффективно использовать рабочую площадь стола (оперативной памяти) при приготовлении еды или для размещения продуктов (данных программы).
Объединения
Объединение — тип данных, позволяющий трактовать одну и ту же область памяти как имеющую разные типы и имена.
Отличие объединений от структур состоит, в частности, в том, что при определении переменной типа объединения память выделяется в соответствии с размером максимального элемента.
Обращение к элементам объединения происходит по их именам, но при этом нужно, конечно, помнить о том, что все поля в объединении накладываются друг на друга.
Одновременная работа с элементами объединения исключена. В качестве элементов объединения можно использовать и структуры.
Какие из этих указателей можно применять в конкретной ситуации, зависит от режима адресации ( use16 или use32 ) и режима работы микропроцессора.
Так вот, описанный в листинге 7 шаблон объединения позволяет нам облегчить формирование и использование указателей различных типов.
Когда вы будете работать в защищенном режиме микропроцессора и использовать 32-разрядные адреса, то аналогичным способом можете заполнить и использовать описанное выше объединение.
Записи
Наша “хозяйка-программист” становится все более экономной. Она уже хочет работать с продуктами на молекулярном уровне, без любых отходов и напрасных трат.
Подумаем, зачем тратить под некоторый программный индикатор со значением “включено-выключено” целых восемь разрядов, если вполне хватает одного? А если таких индикаторов несколько, то расход оперативной памяти может стать весьма ощутимым.
Когда мы знакомились с логическими командами, то говорили, что их можно применять для решения подобной проблемы. Но это не совсем эффективно, так как велика вероятность ошибок, особенно при составлении битовых масок.
Запись — структурный тип данных, состоящий из фиксированного числа элементов длиной от одного до нескольких бит.
При описании записи для каждого элемента указывается его длина в битах и, что необязательно, некоторое значение.
Суммарный размер записи определяется суммой размеров ее полей и не может быть более 8, 16 или 32 бит.
Если суммарный размер записи меньше указанных значений, то все поля записи “прижимаются” к младшим разрядам.
Компилятор TASM, кроме стандартных средств обработки записей, поддерживает также и некоторые дополнительные возможности их обработки.
Описание записи
Описание шаблона записи имеет следующий синтаксис (рис. 6):
Здесь:
представляет собой последовательность описаний отдельных элементов записи согласно синтаксической диаграмме (см. рис. 6):
Рис. 6. Синтаксис описания шаблона записи
При описании шаблона память не выделяется, так как это всего лишь информация для транслятора ассемблера о структуре записи.
Так же, как и для структур, местоположение шаблона в программе может быть любым, но при этом необходимо учитывать логику работы однопроходного транслятора.
Определение экземпляра записи
Для использования шаблона записи в программе необходимо определить переменную с типом данной записи, для чего применяется следующая синтаксическая конструкция (рис. 7):
Рис. 7. Синтаксис описания экземпляра записи
Анализируя эту синтаксическую диаграмму, можно сделать вывод, что инициализация элементов записи осуществляется достаточно гибко. Рассмотрим несколько вариантов инициализации.
Если вы составите и исследуете в отладчике тестовый пример с данным определением записи, то увидите, что все поля переменной типа запись flag обнуляются. Это происходит несмотря на то, что в определении записи заданы начальные значения полей.
Если требуется частичная инициализация элементов, то они заключаются в угловые ( и > ) или фигурные ( < и >) скобки.
Различие здесь в том, что в угловых скобках элементы должны быть заданы в том же порядке, что и в определении записи. Если значение некоторого элемента совпадает с начальным, то его можно не указывать, но обязательно обозначить его запятой. Для последних элементов идущие подряд запятые можно опустить.
К примеру, согласиться со значениями по умолчанию можно так:
Изменить значение поля i2 можно так:
Применяя фигурные скобки, также можно указать выборочную инициализацию полей, но при этом необязательно обозначать запятыми поля, со значениями по умолчанию которых мы согласны:
Работа с записями
Теперь у вас есть вся информация о средствах ассемблера для работы с записями.
Вы также поняли, что непосредственно обратиться к элементу записи невозможно. Чтобы произвести обработку интересующего нас элемента, нужно сначала выделить, сдвинуть его, при необходимости, к младшим разрядам, выполнить необходимые действия и поместить его обратно на свое место в записи. Поэтому, чтобы вам не изобретать каждый раз велосипед, далее мы опишем типовые алгоритмы осуществления этих операций над элементами записи.
Ваша задача — закодировать эти алгоритмы тем или иным способом в соответствии с требованиями задачи.
Выделение элемента записи:
В результате этих действий элемент записи будет локализован в начале рабочего регистра и далее с ним можно производить любые действия.
Работа с элементом записи:
Как мы уже выяснили, с элементами записи производятся любые действия, как над обычной двоичной информацией.
Единственное, что нужно отслеживать, — это размер битового поля. Если, к примеру, размер поля увеличится, то впоследствии может произойти случайное изменение соседних полей битов. Поэтому желательно исключить изменение размера поля.
Помещение измененного элемента на его место в запись:
В заключение еще раз проанализируйте тип записи и особенности работы с ним. При этом обратите внимание на то обстоятельство, что мы нигде явно не просчитываем расположение битов. Поэтому если понадобится изменить размер элемента или его начальное значение, достаточно внести изменения в экземпляр записи или в описание ее типа; функциональную часть программы, работающую с этой записью, трогать не нужно.
Записи: дополнительные возможности обработки
Понимая важность для эффективного программирования такого типа данных, как запись, разработчики транслятора TASM, начиная с версии 3.0, включили в систему его команд две дополнительные команды на правах директив.
Последнее означает, что эти команды внешне имеют формат обычных команд ассемблера, но после трансляции они приводятся к одной или нескольким машинным командам.
Введение этих команд в язык TASM повышает наглядность работы с записями, оптимизирует код и уменьшает размер программы.
Эти команды позволяют скрыть от программиста действия по выделению и установке отдельных полей записи (мы их обсуждали выше).
Для установки значения некоторого поля записи используется команда setfield с синтаксисом:
Для выборки значения некоторого поля записи используется команда getfield с синтаксисом:
Важно отметить, что setfield не производит предварительной очистки элемента, в результате после логического сложения командой or возможно наложение старого содержимого элемента и нового устанавливаемого значения. Поэтому требуется предварительно подготовить поле в записи путем его обнуления.
В качестве примера применения команд setfield и getfield рассмотрим листинг 9.
Запомните это описание. Когда вы освоите работу с макрокомандами и в дальнейшей своей работе столкнетесь с необходимостью работать с регистром флагов, то у вас буквально “зачешутся” руки, чтобы написать соответствующую макрокоманду. Эта макрокоманда, если вы не забудете хорошо ее оттестировать, избавит вас от многих трудно обнаруживаемых ошибок.