- •Лекция №1. Основные понятия. Первая программа для dos и под Windows. Представление данных в компьютере.
- •Сам ассемблер — программа, которая переводит текст с языка, понятного человеку, в язык, понятный процессору, получает объектный модуль;
- •Компоновщик (linker), который создает исполнимые файлы из одного или нескольких объектных модулей, полученных после запуска ассемблера;
- •Дополнительные вспомогательные программы — компиляторы ресурсов, расширители dos и тому подобное (см. Табл.).
- •Написать её в блокноте, сохранять в *.Asm файл;
- •Из командной строки вызвать ассемблер tasm32.Exe (tasm.Exe);
- •Из командной строки вызвать линковщик tlink32.Exe (tlink.Exe);
- •Процессоры и их регистры
- •Процессоры x86
- •Регистры процессора
- •Описание регистров
- •Задача: найти старшую часть расширенного регистра (eax, ebx, ..., esi, edi)?
- •0030:4012 (Всё шестнадцатиричное)
- •4) Нельзя использовать сегментный регистр cs в качестве операнда назначения.
- •5) Оператор ptr можно применять и когда требуется принудительно поменять размерность операндов. К примеру, требуется переслать значение 0ffh во второй байт поля flp:
- •Xchg eax,ebx ; обменять содержимое регистров eax и ebx.
- •Xchg al,al ; а эта команда не делает ничего
- •Xchg ax, word ptr [si] ; обменять содержимое регистра ах и слова в памяти по адресу в [si].
- •Технология sse. Блок xmm
- •Имеются специальные команды сравнения двух вещественных чисел. После их выполнения формируются и помещаются в eflags признаки, характеризующие результат операции.
- •8 Флагов состояния. Эти флаги могут изменяться после выполнения машинных команд;
- •1) Первой известной кодировкой символов была кодировка ascii, и она используется до сих пор. В ascii-кодировке каждый символ занимает 8 бит, или один байт.
- •2. Адресные операнды – задают физическое расположение операнда в памяти с помощью указания двух составляющих адреса: сегмента и смещения. К примеру:
- •6. Структурные операнды используются для доступа к конкретному элементу структуры.
- •7. Записи (аналогично структурному типу) используются для доступа к битовому полю некоторой записи.
- •Команды безусловной передачи управления:
- •Команды условной передачи управления:
- •Команды управления циклом:
- •Лекция № 4. Условные переходы. Арифметиченские команды. Bcd числа.
- •1111111011101101 Инвертируем 0000000100010010
- •Лекция №5. Арифметические команды над целыми числами (продолжение). Арифметические операции над двоично-десятичными числами (bcd числами)
- •К содержимому младшей тетрады al прибавляется 6;
- •Флаг cf устанавливается в 1, тем самым фиксируется перенос в старший разряд для того, чтобы его можно было учесть в последующих действиях.
- •Из содержимого младшей тетрады регистра al вычитается 6;
- •Обнуляется старшая тетраду регистра al;
- •Устанавливает флаг cf в 1, фиксируя воображаемый заём из старшего разряда.
- •Делит ax на 10;
- •Результат деления записывается так: частное – в ah, остаток в al.
- •1) Преобразует двузначное неупакованное bcd-число в регистре ах в двоичное число;
- •2) Полученное двоичное число используется в качестве делимого в операции деления;
- •3) Полученное двоичное число помещается в регистр aх.
- •Деление с остатком
- •Блок-схема вывода результата при делении с остатком Числа с плавающей запятой. Работа с сопроцессором
- •Число имеет вид 1,1100011 × 2-1 или 0,11100011.
- •Переводом в десятичную систему счисления получаем 0,88671875. Лекция №6. Работа с сопроцессором. Команды для работы с fpu. Работа с fpu.
- •Блок-схема вывода на экран вещественного числа, которое хранится в регистре st(0) сопроцессора.
- •Деление с остатком, используя команды целочисленного деления
- •Блок-схема вывода результата при делении с остатком Трансцендентные операции fpu
- •Массивы
- •Перечислением элементов массива в поле операндов одной из директив описания данных. Например:
- •Используя оператор повторения dup. К примеру:
- •4) Mov esi,6*2 ; 2 байта – размер элементов
- •Индексная адресация со смещением — режим адресации, при котором полный адрес формируется из двух компонентов:
- •Базовая индексная адресация со смещением — режим адресации, при котором полный адрес формируется максимум из трех компонентов:
- •Структуры
- •Организовать обращение к элементам структуры.
- •Цепочечные команды или команды обработки строк символов
- •Организация обращения к элементам записи.
- •Iotest record
- •Работа с записями
- •1) Для выделения элемента записи необходимо:
- •2) Чтобы поместить измененный элемент на его место в запись необходимо:
- •3. В конце работы файл следует закрыть.
- •4. Признаком ошибки при выполнении функции dos является взведенный флаг с (переноса).
- •2. Создание файла с усечением существующего до нулевой длины.
- •LpFileName — указатель на asciiz-строку с именем (путем) открываемого или создаваемого файла;
- •DwDesiredAccess — тип доступа к файлу:
- •4) Mov esi,6*2 ; 2 байта – размер элементов
- •Индексная адресация со смещением — режим адресации, при котором полный адрес формируется из двух компонентов:
- •Базовая индексная адресация со смещением — режим адресации, при котором полный адрес формируется максимум из трех компонентов:
- •Структуры
- •Организовать обращение к элементам структуры.
- •Цепочечные команды или команды обработки строк символов
- •Организация обращения к элементам записи.
- •Iotest record
- •Работа с записями
- •1) Для выделения элемента записи необходимо:
- •2) Чтобы поместить измененный элемент на его место в запись необходимо:
- •3. В конце работы файл следует закрыть.
- •4. Признаком ошибки при выполнении функции dos является взведенный флаг с (переноса).
- •2. Создание файла с усечением существующего до нулевой длины.
- •LpFileName — указатель на asciiz-строку с именем (путем) открываемого или создаваемого файла;
- •DwDesiredAccess — тип доступа к файлу:
- •3) Создать и открыть новый файл
- •4) Чтение из файла или устройства
- •5) Переместить указатель чтения/записи
- •1) HFile – хэндл того файла, в котором перемещается указатель.
- •7) Запись в файл или устройство
- •8) Закрыть файл
- •9) Удаление файла
- •Функция 01h – ожидание ввода символа с эхопечатью
- •Функция 0ah – получение строки символов
- •1) Создать каталог
- •2) Удалить каталог
- •5) Определить текущий каталог
- •Перечень функций прерывания 21h, работающих с файлами, которые имеют длинные имена и соответствующие функции api Win32.
- •Перечислим функции api Win32, имеющие отношение к работе с файловой системой.
- •1) В Win32 получить время создания, время последнего доступа и время последней модификации файла можно с помощью функции GetFileTime.
- •3) В Win32 имеется функция GetFileInformationByHandle, с помощью которой можно получить все атрибуты файла:
- •Int 10h ; вызов прерывания bios
- •Основные графические режимы vga
- •Int 10h ; считываем символ и его атрибут
- •Стандартные цветовые палитры в режимах 4,5,6.
- •02H/03h - Чтение/запись секторов.
- •Прямое программирование видеобуфера в текстовом режиме
- •Лекция №6. Работа с сопроцессором. Команды для работы с fpu. Работа с fpu.
- •Лабораторная работа №12. Тема: Графические видеорежимы. Работа с vga-режимами
- •Лабораторная работа № 8. Тема: Работа с файлами
- •Лабораторная работа №5. Тема: Работа с массивами
- •Лекция №1. Основные понятия. Первая программа для dos и под Windows. Представление данных в компьютере.
- •Сам ассемблер — программа, которая переводит текст с языка, понятного человеку, в язык, понятный процессору, получает объектный модуль;
- •Компоновщик (linker), который создает исполнимые файлы из одного или нескольких объектных модулей, полученных после запуска ассемблера;
- •Дополнительные вспомогательные программы — компиляторы ресурсов, расширители dos и тому подобное (см. Табл.).
- •Написать её в блокноте, сохранять в *.Asm файл;
- •Из командной строки вызвать ассемблер tasm32.Exe (tasm.Exe);
- •Из командной строки вызвать линковщик tlink32.Exe (tlink.Exe);
- •Процессоры и их регистры
- •Процессоры x86
- •Регистры процессора
- •Описание регистров
- •Задача: найти старшую часть расширенного регистра (eax, ebx, ..., esi, edi)?
- •0030:4012 (Всё шестнадцатиричное)
- •4) Нельзя использовать сегментный регистр cs в качестве операнда назначения.
- •5) Оператор ptr можно применять и когда требуется принудительно поменять размерность операндов. К примеру, требуется переслать значение 0ffh во второй байт поля flp:
- •Xchg eax,ebx ; обменять содержимое регистров eax и ebx.
- •Xchg al,al ; а эта команда не делает ничего
- •Xchg ax, word ptr [si] ; обменять содержимое регистра ах и слова в памяти по адресу в [si].
- •Технология sse. Блок xmm
- •Имеются специальные команды сравнения двух вещественных чисел. После их выполнения формируются и помещаются в eflags признаки, характеризующие результат операции.
- •8 Флагов состояния. Эти флаги могут изменяться после выполнения машинных команд;
- •1) Первой известной кодировкой символов была кодировка ascii, и она используется до сих пор. В ascii-кодировке каждый символ занимает 8 бит, или один байт.
- •2. Адресные операнды – задают физическое расположение операнда в памяти с помощью указания двух составляющих адреса: сегмента и смещения. К примеру:
- •5. Структурные операнды используются для доступа к конкретному элементу структуры.
- •6. Записи (аналогично структурному типу) используются для доступа к битовому полю некоторой записи.
- •Команды безусловной передачи управления:
- •Команды условной передачи управления:
- •Команды управления циклом:
- •От типа операнда в команде безусловного перехода (ближний или дальний);
- •Лабораторная работа №1. Тема: знакомство с системой программирования Турбо-ассемблер.
- •1) Пересылка данных
- •2) Xor логическое исключающее или
- •3) Команды передачи управления
- •Задание для самостоятельной работы
- •Лабораторная работа №1. Тема: знакомство с системой программирования Турбо-ассемблер.
- •1) Пересылка данных
- •2) Xor логическое исключающее или
- •3) Команды передачи управления
- •Команды вычитания.
- •Команды умножения.
- •Команда деления
- •Команды преобразования
- •Команды сдвига
- •Задание для самостоятельной работы.
- •Лабораторная работа №4. Тема: перевод чисел из шестнадцатиричной системы исчисления в десятичную и в двоичную.
- •Задание для самостоятельной работы.
- •Лекция №10. Сложные структуры данных. Массивы. Структуры.
- •4) Mov esi,6*2 ; 2 байта – размер элементов
- •Индексная адресация со смещением — режим адресации, при котором полный адрес формируется из двух компонентов:
- •Базовая индексная адресация со смещением — режим адресации, при котором полный адрес формируется максимум из трех компонентов:
- •Структуры
- •Организовать обращение к элементам структуры.
- •Цепочечные команды или команды обработки строк символов
- •Организация обращения к элементам записи.
- •Iotest record
- •Работа с записями
- •1) Для выделения элемента записи необходимо:
- •2) Чтобы поместить измененный элемент на его место в запись необходимо:
- •3. В конце работы файл следует закрыть.
- •4. Признаком ошибки при выполнении функции dos является взведенный флаг с (переноса).
- •2. Создание файла с усечением существующего до нулевой длины.
- •LpFileName — указатель на asciiz-строку с именем (путем) открываемого или создаваемого файла;
- •DwDesiredAccess — тип доступа к файлу:
- •Лекция №6. Работа с сопроцессором. Команды для работы с fpu. Работа с fpu.
- •Лекция №6. Работа с сопроцессором. Команды для работы с fpu. Работа с fpu.
- •Лекция №6. Работа с сопроцессором. Команды для работы с fpu. Работа с fpu.
- •Лабораторная работа №1. Тема: знакомство с системой программирования Турбо-ассемблер.
- •1) Пересылка данных
- •2) Xor логическое исключающее или
- •3) Команды передачи управления
- •Лабораторная работа №2. Тема: Работа с арифметическими командами.
- •Команды сложения
- •Команды вычитания.
- •Команды умножения.
- •Команда деления
- •Команды преобразования
- •Команды сдвига
- •Лабораторная работа №4. Тема: перевод чисел из шестнадцатиричной системы исчисления в десятичную и в двоичную.
- •Директива extrn Режимы Ideal, masm
- •Преимущества и недостатки ]Преимущества
- •Недостатки
- •Блок-схема вывода на экран вещественного числа, которое хранится в регистре st(0) сопроцессора.
- •Трансцендентные операции fpu
- •Функция MessageBox
- •1) Первой известной кодировкой символов была кодировка ascii, и она используется до сих пор. В ascii-кодировке каждый символ занимает 8 бит, или один байт.
- •2.Адресные операнды – задают физическое расположение операнда в памяти с помощью указания двух составляющих адреса: сегмента и смещения. К примеру:
- •5. Структурные операнды используются для доступа к конкретному элементу структуры.
- •6. Записи (аналогично структурному типу) используются для доступа к битовому полю некоторой записи.
- •4) Нельзя использовать сегментный регистр cs в качестве операнда назначения.
- •5) Оператор ptr можно применять и когда требуется принудительно поменять размерность операндов. К примеру, требуется переслать значение 0ffh во второй байт поля flp:
- •Xchg eax,ebx ; обменять содержимое регистров eax и ebx.
- •Xchg al,al ; а эта команда не делает ничего
- •Xchg ax, word ptr [si] ; обменять содержимое регистра ах и слова в памяти по адресу в [si].
Задание для самостоятельной работы.
I) Ввести программы из примера 1 и примера 2. Оттранслировать, собрать, получить загрузочный модуль. Разобрать работу программы в Turbo Debbuger.
II) Используя предложенную выше программу, разработать программы, которые вычисляют заданные выражения. Исполните программы с несколькими (три - четыре) наборами исходных данных, проверьте правильность результатов. Числа в наборе данных должны быть различными, т.е. положительными, отрицательными, нулевыми.
Номер задания выбирается в соответствии с вариантом.
1) А – 5·(В – 2·С) + 2
2). - 4·А + (В + С) / 4 + 2
3). 7·А - 2·В - 100 + С
4). -А / 2 + 4·(В + 1) + 3·С
5). 5·(А - В) - 2·С + 5
6). (А/ 2 + В) / 4 + С - 1
7). -(С + 2·А + 4·В + В)
9). 2 - В (А + В) + С / 4
10). 2·В - 1 + 4 (А - 3·С)
Замечание. Переменные A, B, C являются целочисленными, могут быть положительными либо отрицательными. Деление полагается целочисленным, т.е. например, 5/2=2, 6/2=3.
Варианты:
1, 2 – Вариант 1
3, 4 – Вариант 2.
5, 6 – Вариант 3.
7, 8 – Вариант 4.
9, 10 – Вариант 5.
и т.д.
Лабораторная работа №4. Тема: перевод чисел из шестнадцатиричной системы исчисления в десятичную и в двоичную.
Программа преобразования двухзначного шестнадцатиричного числа в десятичное изображение на экране с использованием команды xlat. Вход: исходное шестнадцатиричное число, вводится с клавиатуры. Выход: десятичное число на экране.___________________________________________________________
masm
model small
.stack 100h
.data
message db 'Vvedite dve shestnadcatirichnie cifri',13,10,'$'
tabl db 48 dup (0),0,1,2,3,4,5,6,7,8,9,7 dup (0),0ah,0bh,0ch,0dh,0eh,0fh,26 dup (0),0ah,0bh,0ch,0dh,0eh,0fh
;0ah,0bh,0ch,0dh,0eh,0fh,26 dup (0),
;0ah,0bh,0ch,0dh,0eh,0fh
mes2 db '=','$'
otvet1 db 5 dup (0)
.stack 100h
.code
start:
mov AX,@data
mov DS,AX
lea bx,tabl
mov ah,9
mov dx,offset message
int 21h
xor ax,ax
mov ah,1h
int 21h
xlat
mov dl,al
shl dl,4
int 21h
xlat
add al,dl
mov ah,0h
mov cx,ax
xor bx,bx
mov bx,offset otvet1
mov byte ptr [otvet1+6],'$'
mov si,10
mov di,5
lp1: xor dx,dx
div si
xchg ax,dx
add al,'0'
mov byte ptr [otvet1+di],al
xchg ax,dx
dec di
or ax,ax
jne lp1
Vuvod1:
mov dx,offset mes2
mov ah,09h ;Функция Dos вывода сообщения на
int 21h
mov dx,bx
mov ah,09h ;Функция Dos вывода сообщения на
int 21h ;экран
mov AX,4C00h
int 21h
end start
Задание для самостоятельной работы.
Составить программу, которая бы запрашивала две цифры шестнадцатиричного числа, а выводила на экран десятичное и двоичное представление этого числа соответственно.
Лекция №10. Сложные структуры данных. Массивы. Структуры.
Ранее нами использовались только данные двух типов:
-
Непосредственные данные, представляющие собой числовые или символьные значения, являющиеся частью команды.
-
Данные, описываемые с помощью ограниченного набора директив резервирования памяти, позволяющих выполнить самые элементарные операции по размещению и инициализации числовой и символьной информации. При обработке этих директив ассемблер сохраняет информацию о местоположении данных (значения сегментной составляющей адреса и смещения) и типе данных, то есть единицах памяти, выделяемых для размещения данных в соответствии с директивой резервирования и инициализации данных.
Эти два типа данных являются элементарными, или базовыми. Используя данные этих типов, можно формализовать и запрограммировать практически любую задачу.
В язык ассемблера была введена возможность использования нескольких сложных типов данных. Они строятся на основе базовых типов данных.
TASM поддерживает следующие сложные типы данных:
-
массивы;
-
структуры;
-
объединения;
-
записи.
Массивы
Массив — структурированный тип данных, состоящий из некоторого числа элементов одного типа.
Описание и инициализация массива в программе
При необходимости использовать массив в программе его нужно моделировать одним из следующих способов:
-
перечислением элементов массива в поле операндов одной из директив описания данных. При перечислении элементы разделяются запятыми. Например:
;массив из 5 элементов. Размер каждого элемента 4 байта:
mas dd 1,2,3,4,5
-
используя оператор повторения dup. К примеру:
;массив из 5 нулевых элементов. Размер каждого элемента 2 байта:
mas dw 5 dup (0)
Такой способ определения используется для резервирования памяти с целью размещения и инициализации элементов массива;
-
используя директивы label и rept. Директива rept относится к макросредствам языка ассемблера и вызывает повторение указанного числа раз строк, заключенных между директивой и строкой endm. К примеру, определим массив байт в области памяти, обозначенной идентификатором mas_b. В данном случае директива label определяет символическое имя mas_b аналогично тому, как это делают директивы резервирования и инициализации памяти. Директива label не резервирует память, а лишь определяет характеристики объекта. В данном случае объект — это ячейка памяти. Используя несколько директив label, записанных одна за другой, можно присвоить одной и той же области памяти разные имена и типы, что и сделано в следующем фрагменте:
...
n=0
...
mas_b label byte
mas_w label word
rept 4
dw OflfOh
endm
В результате в памяти будет создана последовательность из четырех слов f1f0. Эту последовательность можно трактовать как массив байт или слов в зависимости от того, какое имя области мы будем использовать в программе — mas_b или mas_w;
-
использованием цикла для инициализации значениями области памяти, которую можно будет впоследствии трактовать как массив. Например: инициализация массива в цикле:
;prg_12_l.asm
MASM
MODEL small
STACK 256
.data
mes1 db 0ah,0dh,'Massiv: ','$'
mes2 db '; ','$'
mas db 10 dup (?) ;исходный массив
i db 0
.code
start:
mov ax,@data
mov ds,ax
xor ax,ax ;обнуление ax
mov cx,100 ;значение счетчика цикла в cx
mov si, 0 ;индекс начального элемента в cx
go: ;цикл инициализации
mov bh,i ;i в bh
mov mas[si],bh ;запись в массив i
inc i ;инкремент i
inc si ;продвижение к следующему элементу массива
loop go ;повторить цикл
;вывод на экран получившегося массива
mov cx,100
mov si,0
mov ah,09h
lea dx,mes1
int 21h
show:
mov ah,02h ;функция вывода значения из dl на экран
mov dl,mas[si]
add dl,30h ;преобразование числа в символ
int 21h
mov ah,09h
lea dx,mes2
int 21h
inc si
loop show
exit:
mov ax,4c00h ;стандартный выход
int 21h
end start ;конец программы
Доступ к элементам массива
Все элементы массива располагаются в памяти компьютера последовательно и то как трактовать данную последовательность решает программист. Одну и ту же область памяти можно трактовать как одномерный массив, и одновременно те же самые данные могут трактоваться как двумерный массив. Все зависит только от алгоритма обработки этих данных в конкретной программе.
Эти же соображения можно распространить и на индексы элементов массива. К примеру, в программе статически определена последовательность данных:
Mas dw 0,1,2,3,4,5
Пусть эта последовательность чисел трактуется как одномерный массив. Размерность каждого элемента определяется директивой dw, то есть она равна двум байтам. В общем случае для получения адреса элемента в массиве необходимо начальный (базовый) адрес массива сложить с произведением индекса (номер элемента минус единица) этого элемента на размер элемента массива:
база + (индекс*размер элемента)
Способы адресации, используемые при работе с массивами
-
индексная адресация со смещением — режим адресации, при котором эффективный адрес формируется из двух компонентов:
-
постоянного (базового) — указанием прямого адреса массива в виде имени идентификатора, обозначающего начало массива;
-
переменного (индексного) — указанием имени индексного регистра.
-
К примеру:
mas dw 0,1,2,3,4,5
...
mov si,4
;поместить 3-й элемент массива mas в регистр ax:
mov ax,mas[si]
-
базовая индексная адресация со смещением — режим адресации, при котором эффективный адрес формируется максимум из трех компонентов:
-
постоянного (необязательного компонента), в качестве которого может выступать прямой адрес массива в виде имени идентификатора, обозначающего начало массива, или непосредственное значение;
-
переменного (базового) — указанием имени базового регистра;
-
переменного (индексного) — указанием имени индексного регистра.
-
Этот вид адресации используется при обработке двухмерных массивов.
В качестве базового регистра может использоваться любой из восьми регистров общего назначения. В качестве индексного регистра также можно использовать любой регистр общего назначения, за исключением esp/sp.
Микропроцессор позволяет масштабировать индекс. Применение масштабирования облегчает работу с массивами, которые имеют размер элементов, равный 2, 4 или 8 байтам. Например: рассмотрим программу, в которой просматривается массив, состоящий из слов, и производится сравнение этих элементов с нулем. Выводится соответствующее сообщение.
Листинг 10.1. Просмотр массива слов с использованием масштабирования
;prg_12_2.asm
MASM
MODEL small
STACK 256
.data ;начало сегмента данный
;тексты сообщений:
mes1 db "не равен 0!$",10,13
mes2 db "равен 0!$",10,13
mes3 db 10,13,'Элемент $'
mas dw 2,7,0,0,1,9,3,6,0,8 ;исходный массив
.code
;.486 ;это обязательно
start:
mov ax,@data
mov ds,ax ;связка ds с сегментом данных
xor ax,ax ;обнуление ax
prepare:
mov сx,10 ;значение счетчика цикла в cx
mov esi,O ;индекс в esi
compare:
mov dx,mas[esi*2] ;первый элемент массива в dx
cmp dx,0 ;сравнение dx с 0
je equal ;переход, если равно
not_equal: ;не равно
mov ah,09h ;вывод сообщения на экран
lea dx,mes3
int 21h
mov ah,02h ;вывод номера элемента массива на экран
mov dx,si
add dl,30h
int 21h
mov ah,09h
lea dx,mes1
int 21h
inc esi ;на следующий элемент
dec сх ;условие для выхода из цикла
jcxz exit ;cx=0? Если да - на выход
jmp compare ;нет - повторить цикл
equal: ;равно 0
mov ah,09h ;вывод сообщения mes3 на экран
lea dx,mes3
int 21h
mov ah,02h
mov dx,si
add dl,30h
int 21h
mov ah,09h ; вывод сообщения mes2 на экран
lea dx,mes2
int 21h
inc esi ;на следующий элемент
dec ex ;все элементы обработаны?
jcxz exit
jmp compare
exit:
mov ax,4c00h ;стандартный выход
int 21h
end main ;конец программы
Еще несколько слов об адресации: при работе с массивами:
-
если для описания адреса используется только один регистр, то речь идёт о базовой адресации и этот регистр рассматривается как базовый:
; переслать байт из области данных, адрес которой находится в регистре ebx
mov al,[ebx]
если для задания адреса в команде используется прямая адресация (в виде идентификатора) в сочетании с одним регистром, то речь идёт об индексной адресации. Регистр считается индексным и поэтому можно использовать масштабирование для получения адреса нужного элемента массива:
add eax,mas[ebx*4]
;сложить содержимое eax с двойным словом в памяти по адресу mas + (ebx)*4
-
если для описания адреса используются два регистра, то речь идет о базово-индексной адресации. Левый регистр рассматривается как базовый, а правый — как индексный. Применение регистров ebp/bp и esp/sp по умолчанию подразумевает, что сегментная составляющая адреса находится в регистре ss.
Базово-индексную адресацию можно сочетать с прямой адресацией или указанием непосредственного значения. Адрес тогда будет формироваться как сумма всех компонентов.
К примеру:
mov ax,mas[ebx][ecx*2]
;адрес операнда равен [mas+(ebx)+(ecx)*2]
...
sub dx,[ebx+8][ecx*4]
;адрес операнда равен [(ebx)+8+(ecx)*4]
Масштабирование эффективно только, когда размерность элементов массива равна 2, 4 или 8 байтам. Если же размерность элементов другая, то организовывать обращение к элементам массива нужно обычным способом.
Рассмотрим пример работы с массивом из пяти трехбайтовых элементов. Младший байт в каждом из этих элементов представляет собой некий счетчик, а старшие два байта — что-то еще, для нас не имеющее никакого значения. Необходимо последовательно обработать элементы данного массива, увеличив значения счетчиков на единицу.
Листинг 10.2. Обработка массива элементов с нечетной длиной
;prg_12_3.asm
MASM
MODEL small ;модель памяти
STACK 256 ;размер стека
.data ;начало сегмента данных
N=5 ;количество элементов массива
mas db 5 dup (3 dup (0))
mes db '; ','$'
.code ;сегмент кода
start: ;точка входа в программу
mov ax,@data
mov ds,ax
xor ax,ax ;обнуление ax
mov si,0 ;0 в si
mov cx,N ;N в cx
go:
mov dl,mas[si] ;первый байт поля в dl
inc dl ;увеличение dl на 1 (по условию)
mov mas[si],dl ;заслать обратно в массив
add si,3 ;сдвиг на следующий элемент массива
loop go ;повтор цикла
mov si,0 ;подготовка к выводу на экран
mov cx,N
show: ;вывод на экран содержимого первых байт полей
mov dl,mas[si]
add dl,30h
mov ah,02h
int 21h
add si,3
lea dx,mes
mov ah,09h
int 21h
loop show
exit:
mov ax,4c00h ;стандартный выход
int 21h
end start ;конец программы
Двумерные массивы
Двумерный массив нужно моделировать. На описании самих данных это никак не отражается — память под массив выделяется с помощью директив резервирования и инициализации памяти. Обращение к элементам двумерного массива в памяти как к строкам или как к столбцам зависит от алгоритма обработки двумерного массива. Если последовательность однотипных элементов в памяти трактуется как двумерный массив, расположенный по строкам, то адрес элемента (i, j) вычисляется по формуле:
(база + количество_элементов_в_строке * размер_элемента * i+j)
Здесь i = 0...n-l указывает номер строки, a j = 0...m-l указывает номер столбца.
Например, пусть имеется массив чисел (размером в 1 байт) mas(i,j) с размерностью 4 X 4 (i = 0...3, j = 0...3):
23 04 05 67
05 06 07 99
67 08 09 23
87 09 00 08
В памяти элементы этого массива будут расположены в следующей последовательности:
23 04 05 67 05 06 07 99 67 08 09 23 87 09 00 08
Если необходимо трактовать эту последовательность как двумерный массив и извлечь, например, элемент mas(2, 3) = 23, то, убедимся что:
Эффективный адрес mas(2, 3) = mas + 4 * 1 * 2 + 3 = mas + 11
Организовать адресацию двумерного массива логично, используя базово-индексную адресацию. При этом возможны два основных варианта выбора компонентов для формирования эффективного адреса:
-
сочетание прямого адреса, как базового компонента адреса, и двух индексных регистров для хранения индексов:
mov ax,mas[ebx][esi]
-
сочетание двух индексных регистров, один из которых является и базовым, и индексным одновременно, а другой — только индексным:
mov ax,[ebx][esi]
В программе это будет выглядеть примерно так:
;Фрагмент программы выборки элемента массива mas(2,3) и его обнуления
.data
mas db 23,4,5,67,5,6,7,99,67,8,9,23,87,9,0,8
i=2
j=3
.code
...
mov si,4*1*i
mov di,j
mov al,mas[si][di] ;в аl элемент mas(2,3)
Типовые операции с массивами
Для демонстрации основных приемов работы с массивами лучше всего подходят программы поиска или сортировки. Рассмотрим одну такую программу, выполняющую поиск элемента в двумерном массиве.
Листинг 10.3. Поиск элемента в двумерном массиве
;prg_12_4.asm
MASM
MODEL small
STACK 256
.data
;матрица размером 2х5 - если ее не инициализировать,
;то для наглядности она может быть описана так:
;array dw 2 DUP (5 DUP (?))
;но мы ее инициализируем:
array dw 1,2,3,4,5,6,7,3,9,0
;логически это будет выглядеть так:
;аггау= {1 2}
; {3 4}
; {5 6}
; {7 3}
; {9 0}
elem dw 3 ;элемент для поиска
faileddb 0ah,0dh,'Нет такого элемента в массиве!','$'
success db 0ah,0dh,'Такой элемент в массиве присутствует ","$'
foundtime db ? ;количество найденных элементов
fnd db " раз(a)",0ah,0dh,'$'
.code
main:
mov ax,@data
mov ds,ax
xor ax,ax
mov si,0 ;si=столбцы в матрице
mov bx,0 ;bx=строки в матрице
mov ex,5 ;число для внешнего цикла (по строкам)
external: ;внешний цикл по строкам
mov ax,array[bx][s1] ;в ax первый элемент матрицы
push ex ;сохранение в стеке счетчика внешнего цикла
mov сх,2 ;число для внутреннего цикла (по столбцам)
mov si,0
iternal: ;внутренний цикл по строкам
inc si ;передвижение на следующий элемент в строке
;сравниваем содержимое текущего элемента в ax с искомым элементом:
cmp ax,elem
;если текущий совпал с искомым, то переход на here для обработки,
;иначе - цикл продолжения поиска
je here
;иначе - цикл по строке cx=2 раз
loop iternal
here:
jcxz move_next ;просмотрели строку?
inc foundtime ;иначе - увеличиваем счетчик совпавших
move_next: ;продвижение в матрице
pop cx ;восстанавливаем CX из стека (5)
add bx,1 ;передвигаемся на следующую строку
loop external ;цикл (внешний)
cmp foundtime,0h ;сравнение числа совпавших с 0
ja eql ;если больше 0, то переход
not_equal: ;нет элементов, совпавших с искомым
mov ah,09h ;вывод сообщения на экран
mov dx, offset failed
int 21h
jmp exit ;на выход
eql: ;есть элементы, совпавшие с искомым
mov ah,09h ;вывод сообщений на экран
mov dx,offset success
int 21h
mov ah,02h
mov dl,foundtime
add dl,30h
int 21h
mov ah,09h
mov dx,offset fnd
int 21h
exit: ;выход
mov ax,4c00h ;стандартное завершение программы
int 21h
end main ;конец программы
При анализе работы программы принято, что элементы массива нумеруються с 0. При поиске определенного элемента массив просматривается от начала и до конца. Программа сохраняет в поле foundtime количество вхождений искомого элемента в массив. В качестве индексных регистров используются si и bx.
Структуры
Структура — это тип данных, состоящий из фиксированного числа элементов разного типа. Структура не является переменной, она только схема переменной.
Для использования структур в программе необходимо выполнить три действия:
-
Задать шаблон структуры, то есть определить новый тип данных, который впоследствии можно использовать для определения переменных данного типа.
-
Определить экземпляр структуры. Этот этап подразумевает инициализацию конкретной переменной с заранее определенной (с помощью шаблона) структурой. На этом этапе даётся указание транслятору выделить память и присвоить этой области памяти символическое имя. Описать структуру в программе можно только один раз, а определить — любое количество раз
-
Организовать обращение к элементам структуры.
Описание шаблона структуры
Описание шаблона структуры имеет следующий синтаксис:
имя_структуры STRUC
<описание полей>
имя_структуры ENDS
Здесь <описание полей> представляет собой последовательность директив описания данных db, dw, dd, dq и dt. Их операнды определяют размер полей и при необходимости начальные значения. Этими значениями будут, возможно, инициализироваться соответствующие поля при определении структуры. Структуры не резервируют пространство в сегменте данных и не записывают ни одного байта в конечную программу.
Местоположение шаблона в программе может быть произвольным, но, следует всё же располагать шаблон структуры до того места, где определяется переменная с типом данной структуры. То есть при описании в сегменте данных переменной с типом некоторой структуры ее шаблон необходимо поместить в начале сегмента данных либо перед ним.
Пример работы со структурами. Смоделируем базу данных сотрудников некоторого отдела. Условимся, что все поля базы данных символьные. Определим структуру записи этой базы данных следующим шаблоном:
worker struc ;информация о сотруднике
nam db 30 dup (" ") ;фамилия, имя, отчество
sex db " " ;пол
position db 30 dup (" ") ;должность
age db 2 dup (" ") ;возраст
standing db 2 dup (" ") ;стаж
salary db 4 dup (" ") ;оклад в гривнах
birthdate db 8 dup (" ") ;дата рождения
worker ends
Определение данных с типом структуры
Для использования описанной с помощью шаблона структуры в программе необходимо определить переменную с типом данной структуры. Для этого используется следующая синтаксическая конструкция:
[имя переменной] имя_структуры <[список значений]>
Здесь:
имя переменной — идентификатор переменной данного структурного типа. Задание имени переменной необязательно. Если его не указать, будет просто выделена область памяти размером в сумму длин всех элементов структуры.
список значений — заключенный в угловые скобки список начальных значений элементов структуры, разделенных запятыми. Его задание также необязательно. Если список указан не полностью, то все поля структуры для данной переменной инициализируются значениями из шаблона, если таковые заданы. Допускается инициализация отдельных полей, но в этом случае пропущенные поля должны отделяться запятыми. Пропущенные поля будут инициализированы значениями из шаблона структуры. Если при определении новой переменной с типом данной структуры нет необходимости менять значение полей в ее шаблоне (то есть нас устраивают заданные по умолчанию значения), то нужно просто написать угловые скобки. К примеру:
victor worker <>.
Определим несколько переменных с типом описанной выше структуры:
. data segment
sotr1 worker <" Гурко Андрей Вячеславовича’,,’художник’,
'33','15','1800','26.01.64'>
sotr2 worker <"Михайлова Наталья Геннадьевна",'ж','программист',
'30','10','1680','27.10.58'>
sotr3 worker<’Cтenaнов Юрий Лонгинович’,,'художник',
'38','20','1750','01.01.58'>
sotr4 worker<"Юрова Елена Александровна",'ж','связист',
'32','2',,'09.01.66'>
sotr5 worker<> ;здесь все значения по умолчанию
Методы работы со структурой
Для того чтобы сослаться в команде на поле некоторой структуры, используется специальный оператор — символ «..» (точка). Он используется в следующей синтаксической конструкции:
адресное_выражение.имя_поля_структуры
Здесь:
адресное_выражение — идентификатор переменной некоторого структурного типа или выражение в скобках в соответствии с указанными ниже синтаксическими правилами (рис. 10.1);
имя_поля_структуры — имя поля из шаблона структуры. Это смещение поля от начала структуры.
Таким образом оператор. вычисляет выражение:
(адресное_выражение) + (имя_поля_структуры)
Рис. 10.1. Синтаксис адресного выражения в операторе обращения к полю структуры.
Продемонстрируем на примере определенной нами структуры worker некоторые приемы работы со структурами. К примеру, требуется извлечь в ax значения поля с возрастом. Из-за принципа хранения данных «младший байт по младшему адресу» старшая цифра возраста будет помещена в а1, а младшая — в ah. Для корректировки достаточно использовать команду xchg a1,ah:
mov ax,word ptr sotr1.age ;в аl возраст sotr1
xchg ah,al
;a можно и так:
lea bx,sotrl
mov ax,word ptr [bx].age
xchg ah,al
Язык ассемблера разрешает определять не только отдельную переменную с типом структуры, но и массив структур. К примеру, определим массив из 10 структур типа worker:
mas_sotr worker 10 dup (<>)
Транслятор назначает имени типа структуры и имени переменной с типом структуры атрибут типа. Значением этого атрибута является размер в байтах, занимаемый полями этой структуры. Извлечь это значение можно с помощью оператора type. После того, как стал известен размер экземпляра структуры, организовать индексацию в массиве структур можно следующим образом. К примеру:
worker struc
...
worker ends
...
mas_sotr worker 10 dup (<>)
...
mov bx,type worker ;bx=77
lea di,mas_sotr
;извлечь и вывести на экран пол всех сотрудников:
mov сх,10
cycl:
mov dl,[di].sex
... ;вывод на экран содержимого поля sex структуры worker
add di,bx ;к следующей структуре в массиве mas_sort
loop cycl
Пример: Выполним копирование поля nam третьего сотрудника в поле nam пятого сотрудника:
worker struc
...
worker ends
mas_sotr worker 10 dup (<>)
...
mov bx,offset mas_sotr
mov si,(type worker)*2 ;si=77*2
add si,bx
mov di,(type worker)*4 ;di=77*4
add di,bx
mov cx,30
repmovsb ;??????? Не знаю что это такое
Объединения
Ассемблер даёт возможность переопределить область памяти для объекта с другими типом и именем с помощью специального типа данных, называемого объединением. Объединение — тип данных, позволяющий трактовать одну и ту же область памяти как имеющую разные типы и имена.
Описание объединений в программе напоминает описание структур, то есть сначала описывается шаблон, в котором с помощью директив описания данных перечисляются имена и типы полей:
имя_объединения UNION
<описание полей>
имя_обьединения ENDS
Отличие объединений от структур состоит в том, что при определении переменной типа объединения память выделяется в соответствии с размером максимального элемента. Обращение к элементам объединения происходит по их именам, но при этом все поля в объединении накладываются друг на друга. Одновременная работа с элементами объединения исключена. В качестве элементов объединения можно использовать и структуры.
В листинге 10.4 кроме демонстрации использования собственно типа данных «объединение», в нем показывается возможность взаимного вложения структур и объединений. Основная идея программы в том, что указатель на память, формируемый программой, может быть представлен в виде:
-
16-битного смещения;
-
32-битного смещения;
-
пары из 16-битного смещения и 16-битной сегментной составляющей адреса;
-
пары из 32-битного смещения и 16-битного селектора.
Какие из этих указателей можно применять в конкретной ситуации, зависит от режима адресации (use16 или use32) и режима работы микропроцессора. Описанный в листинге 10.4 шаблон объединения позволяет облегчить формирование и использование указателей различных типов.
Листинг 10.4. Пример использования объединения
masm
model small
stack 256
.586P
pnt struc ;структура pnt, содержащая вложенное объединение
union ;описание вложенного в структуру объединения
offs_16 dw ?
offs_32 dd ?
ends ;конец описания объединения
segm dw ?
ends ;конец описания структуры
.data
point union ;определение объединения,
;содержащего вложенную структуру
off_16 dw ?
off_32 dd ?
point_16 pnt <>
point_32 pnt <>
point ends
tst db «Строка для тестирования»
adr_data point <> ;определение экземпляра объединения
.code
main:
mov ax,@data
mov ds,ax
mov ax,seg tst
;записать адрес сегмента строки tst в поле
;структуры adr_data
mov adr_data.point_16.segm,ax
;когда понадобится, можно извлечь значение из
;этого поля обратно,
;к примеру, в регистр bx:
mov bx,adr_data.point_16.segm
;формируем смещение в поле структуры adr_data
mov ax,offset tst ;смещение строки в ax
mov adr_data.point_16.offs_l6,ax
;аналогично, когда понадобится, можно извлечь
;значение из этого поля:
mov bx,adr_data.point_16.offs_16
exit:
mov ax,4c00h
int 21h
end main
Когда будет необходимость работать в защищенном режиме микропроцессора и использовать 32-разрядные адреса, то аналогичным способом можно заполнить и использовать описанное выше объединение.
Записи
Запись — структурный тип данных, состоящий из фиксированного числа элементов длиной от одного до нескольких бит.
При описании записи для каждого элемента указывается его длина в битах и необязательно некоторое значение. Суммарный размер записи определяется суммой размеров ее полей и не может быть более 8, 16 или 32 бит. Если суммарный размер записи меньше указанных значений, то все поля записи «прижимаются» к младшим разрядам.
Использование записей в программе, так же, как и структур, организуется в три этапа:
-
Задание шаблона записи, то есть определение набора битовых полей, их длин и, при необходимости, инициализация полей.
-
Определение экземпляра записи. Так же как и для структур, этот этап подразумевает инициализацию конкретной переменной типом заранее определенной с помощью шаблона записи.
-
Организация обращения к элементам записи.
Описание записи
Описание шаблона записи имеет следующий синтаксис:
имя_записи RECORD <описание элементов>
Здесь:
<описание эленентов> представляет собой последовательность описаний отдельных элементов записи согласно синтаксической диаграмме (рис. 10.2).
При описании шаблона память не выделяется, так как это информация для транслятора ассемблера о структуре записи. Так же как и для структур, местоположение шаблона в программе может быть любым, но при этом необходимо учитывать логику работы однопроходного транслятора.
Рис. 10.2. Синтаксис описания шаблона записи
Определение экземпляра записи
Для использования шаблона записи в программе необходимо определить переменную с типом данной записи, для чего применяется следующая синтаксическая конструкция (рис. 10.3).
Рис. 10.3. Синтаксис описания экземпляра записи
Рассмотрим несколько вариантов инициализации. Если инициализировать поля не требуется, то достаточно указать ? при определении экземпляра записи:
...
iotest record i1:1,i2:2=11,i3:1,i4:2=11,i5:2=00
...
flag iotest? ;все поля записи после выполнения данной инициализации
;обнуляются
Если требуется частичная инициализация элементов, то они заключаются в угловые (< и >) или фигурные ({ и }) скобки. Различие здесь в том, что в угловых скобках элементы должны быть заданы в том же порядке, что и в определении записи. Если значение некоторого элемента совпадает с начальным, то его можно не указывать, но обязательно обозначить его запятой. Для последних элементов идущие подряд запятые можно опустить.
К примеру, согласиться со значениями по умолчанию можно так:
iotest record i1:1,i2:2=11,i3:1,i4:2=11,i5:2=00
...
flag iotest<> ;согласились со значением по умолчанию
Изменить значение поля i2 можно так:
iotest record i1:1,i2:2=11,i3:1,i4:2=11,i5:2=00
...
flag iotest<,l0,> ; переопределили i2
Применяя фигурные скобки, также можно указать выборочную инициализацию полей, но при этом необязательно обозначать запятыми поля, со значениями по умолчанию которых мы согласны:
iotest record i1:1,i2:2=11,i3:1,i4:2=11,i5:2=00
...
flag iotest{i2=10} ;переопределили 12, не обращая внимания на порядок
;следования других компонентов записи
Работа с записями
Для организации работы с отдельными элементами записи нужно усвоить несколько моментов:
-
Каждому имени элемента записи ассемблер присваивает числовое значение, равное количеству сдвигов вправо, которые нужно произвести для того, чтобы этот элемент оказался «прижатым» к началу ячейки. Это дает возможность локализовать его и работать с ним. Но для этого нужно знать длину элемента в битах.
-
Сдвиг вправо производится с помощью команды сдвига shr.
-
Ассемблер содержит оператор width, который позволяет узнать размер элемента записи в битах или полностью размер записи. Варианты применения оператора width:
-
width имя_элемента_записи — значением оператора будет размер элемента в битах;
-
width имя_экземпляра_записи или width имя_типа_записи — значением оператора будет размер всей записи в битах.
-
mov al,width i2
...
mov ax,width iotest
-
Ассемблер содержит оператор mask, который позволяет локализовать биты нужного элемента записи. Эта локализация производится путем создания маски, размер которой совпадает с размером записи. В этой маске обнулены биты во всех позициях, за исключением тех, которые занимает элемент в записи.
-
Действия по преобразованию элементов записи производятся с помощью логических команд.
Таким образом, непосредственно обратиться к элементу записи невозможно. Чтобы произвести обработку интересующего нас элемента, нужно сначала выделить его, сдвинуть при необходимости к младшим разрядам, выполнить требуемые действия и поместить обратно на свое место в записи. Рассмотрим типовые алгоритмы для работы с элементами записи.
1) Для выделения элемента записи необходимо:
-
Поместить запись во временную память — регистр (8-, 16- или 32-битный, в зависимости от размера записи).
-
Получить битовую маску, соответствующую элементу записи, с помощью оператора mask.
-
Локализовать биты в регистре с помощью маски и команды and.
-
Сдвинуть биты элемента к младшим разрядам регистра командой shr. Число разрядов для сдвига получить с использованием имени элемента записи.
В результате этих действий элемент записи будет локализован в начале рабочего регистра и далее с ним можно производить любые действия.
2) Чтобы поместить измененный элемент на его место в запись необходимо:
-
Используя имя элемента записи в качестве счетчика сдвигов, сдвинуть влево биты элемента записи.
-
Если вы не уверены в том, что разрядность результата преобразований не превысила исходную, можно выполнить «обрезание» лишних битов, используя команду and и маску элемента.
-
Подготовить исходную запись к вставке измененного элемента путем обнуления битов в записи на месте этого элемента. Это можно сделать путем наложения командой and инвертированной маски элемента записи на исходную запись.
-
С помощью команды or наложить значение в регистре на исходную запись.
В качестве примера рассмотрим листинг, который обнуляет поле i2 в записи iotest.
Листинг 10.5. Работа с полем записи
;ргд_12_7.asm
masm
model small
stack 256
iotest record i1:1,i2:2=11,i3:1,i4:2=11,i5:2=00
.data
flag iotest<>
.code
main:
mov ax,@data
mov ds,ax
mov al,mask i2
shr al,i2 ;биты i2 в начале ax
and al,0fch ;обнулили i2
;помещаем i2 на место
shl al,i2
mov bl,[flag]
xor bl,mask i2 ;сбросили i2
or bl,al ;наложили
exit:
mov ax,4c00h ;стандартный выход
int 21h
end main ;конец программы
Если необходимо изменить размер элемента или его начальное значение, достаточно внести изменения в экземпляр записи или в описание ее типа; функциональную часть программы, работающую с этой записью, трогать не нужно.
Дополнительные возможности обработки
Для работы с записями разработчики транслятора TASM, начиная с версии 3.0, включили в систему его команд две дополнительные команды на правах директив. Последнее означает, что эти команды внешне имеют формат обычных команд ассемблера, но после трансляции они приводятся к одной или нескольким машинным командам. Эти команды позволяют скрыть от программиста действия по выделению и установке отдельных полей записи (мы их уже рассмотрели).
Для установки значения некоторого поля записи используется команда setfield с синтаксисом:
setfield имя_ элемента_записи назначение, регистр_источник
Для выборки значения некоторого поля записи используется команда getfield с синтаксисом:
getfield имя_элемента_записи регистр_назначения, источник
Работа команды setfield заключается в следующем. Местоположение записи определяется операндом назначение, который может представлять собой имя регистра или адрес памяти. Операнд имя_элемента_записи определяет элемент записи, с которым ведется работа (по сути, он определяет смещение элемента в записи относительно младшего разряда). Новое значение, в которое необходимо установить указанный элемент записи, должно содержаться в операнде регистр_источник. Обрабатывая данную команду, транслятор генерирует последовательность команд, которые выполняют следующие действия:
-
сдвиг содержимого регистр_источник влево на количество разрядов, соответствующее расположению элемента в записи;
-
логическую операцию or над операндами назначение и регистр_исгочник. Результат операции помещается в операнд назначение.
Команда setfield не производит предварительной очистки элемента, в результате после логического сложения командой or возможно наложение старого содержимого элемента и нового устанавливаемого значения. Поэтому требуется предварительно подготовить поле в записи путем его обнуления.
Действие команды getfield обратно setfield. В качестве операнда источник может быть указан либо регистр, либо адрес памяти. В регистр, указанный операндом регистр_назначение, помещается результат работы команды — значение элемента записи. Команда getfield всегда использует 16-битный регистр_назначение, даже если указано в этой команде имя 8-битного регистра.
В качестве примера применения команд setfield и getfield рассмотрим листинг 10.6.
Листинг 10.6. Работа с полями записи
;prg_12_8.asm
masm
model small
stack 256
iotest record i1:1,i2:2=11,i3:1,i4:2=11,i5:2=00
.data
flag totest<>
.code
main:
mov ax,@data
mov ds,ax
mov al,flag
mov bl,3
setfield i5 al,bl
xor bl,bl
getfield i5 bl,аl
mov bl,1
setfield i4 аl,bl
setfield i5 al,bl
exit:
mov ax,4c00h ;стандартный выход
int 21h
end main ;конец программы
В листинге 10.6 демонстрируется порядок извлечения и установки некоторых полей записи. Результат работы команд setfield и getfield удобнее всего изучать в отладчике. При установке значений полей не производится их предварительная очистка. Это сделано специально. Для такого рода операций лучше использовать некоторые универсальные механизмы? иначе велик риск внесения ошибок, которые трудно обнаружить и исправить. В качестве такого механизма можно предложить макрокоманды, к рассмотрению которых мы и приступим на следующем уроке.
Лекция №8. Массивы, структуры, записи, объединения
Пересылка данных с помощью команды xlat
Команда: |
XLAT адрес XLATB |
Назначение: |
Трансляция в соответствии с таблицей |
Помещает в AL байт из таблицы в памяти по адресу ES:BX (или ES:EBX) со смещением относительно начала таблицы, равным AL.
В качестве аргумента для XLAT в ассемблере можно указать имя таблицы, но эта информация никак не используется процессором и служит только как комментарий. Если этот комментарий не нужен, можно применить форму записи XLATB.
Пример: Преобразовать шестнадцатеричное число в ASCII-код соответствующего ему символа:
.data
htable db "0123456789ABCDEF"
.code
….
mov al,0Ch
mov bx, offset htable
xlat
После выполнения команды xlat, регистр AL будет содержать не число 0Сh, а ASCII-код буквы «С».
Пример 3: Заполнить массив из 30-ти элементов размером в байт значениями индексов. Вывести массив на экран в одну строку, учитывая, что элементы массива максимум двухзначные числа.
.386
.model flat, stdcall
includelib import32.lib
extrn ExitProcess:PROC
extrn MessageBoxA:PROC
.data
Ttl db 'Massiv',0h
mas db 30 dup (?) ; 32 элементов массива + 10 символов +переходы на
; следующую строчку 2 байта 0ah,0dh
db 0ah
mes1 db 'Massiv: ',0ah,0dh
masVivod db 85 dup (?) ; 10 элементов массива + 10 символов +переходы на
; следующую строчку 2 байта 0ah,0dh
i db 0
N db 30
tabl db 30h,31h,32h,33h,34h,35h,36h,37h,38h,39h
.code
start:
xor ecx,ecx ;обнуление есx
xor edx,edx
mov cl,N ;значение счетчика цикла в cl
mov esi, 0 ;индекс начального элемента в si
;-------------------------------------------------------------------------------------------------
; Заполнение элементов массива значениями их индексов
go:
mov bh,i
mov mas[esi],bh
inc esi
inc i
loopnz go
;-------------------------------------------------------------------------------------------------
mov cl,N
mov edi,0
mov esi,0
;-------------------------------------------------------------------------------------------------
; Проверяем число больше или равно 10, если «да», то уходим на метку met1 и
; выводим его как двузначное число. Если число меньше 10 (однозначное),
; то уходим на метку met2.
go1: ; цикл инициализации
xor eax,eax
mov al,mas[edi]
cmp al,0ah
jae met1
jmp met2
;-------------------------------------------------------------------------------------------------
; Формируем двузначное число в массив masVivod. Делим его на 10, чтобы
; отделить первую цифру и получить одну цифру в остатке. Переводим первую
; цифру и остаток в ASCII код
met1:
mov bl,10
div bl
mov ebx, offset tabl
xlat
mov masVivod[esi],al
inc esi
mov al,ah
xlat
mov masVivod[esi],al
jmp met3
;-------------------------------------------------------------------------------------------------
; Если число однозначное, то заменяем число на символ
; числа и записываем символ числа (ASCII код числа) в регистр al
met2:
mov ebx, offset tabl
xlat
mov masVivod[esi],al
;-------------------------------------------------------------------------------------------------
; Ставим после числа символ «;» и наращиваем индексы
met3:
inc esi
mov masVivod[esi],';'
inc esi
inc edi
loopnz go1
mov masVivod[esi],0h
;вывод на экран получившегося массива
push 0h
push offset Ttl
push offset mes1
push 0h
call MessageBoxA
push 0h
call ExitProcess
end start
Результат:
Пример 5: Вывести в одну строку все значения элементов массива, учитывая, что значения элементов массива могут быть трёхзначными и имеют размер в один байт. Значение элементов массива сформировать как i*k, i=k.
.386
.model flat, stdcall
includelib import32.lib
extrn ExitProcess:PROC
extrn MessageBoxA:PROC
.data
Ttl db 'Massiv',0h
mas db 30 dup (?) ; 32 элементов массива + 10 символов +переходы на
; следующую строчку 2 байта 0ah,0dh
mes1 db 'Massiv: ',0ah,0dh
masVivod db 85 dup (?) ; 10 элементов массива + 10 символов +переходы на
; следующую строчку 2 байта 0ah,0dh
i db 0
k db 0
N db 16 ; Максимальное значение элемента массива 15*15=225
tabl db 30h,31h,32h,33h,34h,35h,36h,37h,38h,39h
.code
start:
xor ecx,ecx
xor edx,edx
mov cl,N
mov esi, 0
go:
mov bl,i
mov al,k
mul bl
mov mas[esi],al
inc esi
inc i
inc k
loopnz go
mov cl,N
mov edi,0
mov esi,0
; ----------------------------------------------------------------------------------------------
; Проверяем число. Если оно однозначное, то уходим на метку met2. Если число
; двухзначное, то уходим на метку met1. Если число трёхзначное, то продолжаем
; выполнять код программы.
go1:
xor eax,eax
mov al,mas[edi]
cmp al,0ah
jb met2
cmp al,64h
jb met1
; Если число трёхзначное, то делим его на 10, чтобы
; получить две первых цифры числа и остаток. Заносим в регистр целую часть от деления
; а остаток переводим в ASCII код. Оставшиеся две цифры числа (целую часть от
; деления) сохраняем в регистре ah и переходим на метку met4.
mov bl,10
div bl
mov ebx, offset tabl
xchg al,ah
xlat
mov masVivod[esi+2],al
mov al,ah
xor ah,ah
jmp met4
; ---------------------------------------------------------------------------------------------
; Если число двухзначное, то делим его на 10, чтобы
; получить первую цифру числа и остаток. Заносим в память первую цифру и
; остаток.
met1:
mov bl,10
div bl
mov ebx, offset tabl
xchg al,ah
xlat
mov masVivod[esi+1],al
mov al,ah
xlat
mov masVivod[esi],al
inc esi
jmp met3
met4:
mov bl,10
div bl
mov ebx, offset tabl
xchg al,ah
xlat
mov masVivod[esi+1],al
mov al,ah
xlat
mov masVivod[esi],al
inc esi
inc esi
jmp met3
; ----------------------------------------------------------------------------------------------
; Имеем однозначное число. Записываем ASCII код этого числа в память.
met2:
mov ebx, offset tabl
xlat
mov masVivod[esi],al
; ----------------------------------------------------------------------------------------------
; Ставим после числа в памяти символ «;» в качестве разделителя.
met3:
inc esi
mov masVivod[esi],';'
inc esi
inc edi
loop go1
mov masVivod[esi],0h
;------------------------------------------------------------
;вывод на экран получившегося массива
push 0h
push offset Ttl
push offset mes1
push 0h
call MessageBoxA
push 0h
call ExitProcess
end start
Результат:
_______________________________________________________________
Самостоятельно на практике: Вывести все значения элементов массива в несколько строк. В каждой строке должно быть по 5 элементов массива. Значения элементов массива могут быть трёхзначными размером в байт.
Доступ к элементам массива
Все элементы массива располагаются в памяти компьютера последовательно и то, как трактовать данную последовательность решает программист.
Одну и ту же область памяти можно трактовать как одномерный массив, и одновременно те же самые данные могут трактоваться как двумерный массив. Все зависит только от алгоритма обработки этих данных в конкретной программе.
Эти же соображения можно распространить и на индексы элементов массива.
Наример, в программе статически определена последовательность данных:
Mas dw 0,1,2,3,4,5,456,876,7,8752 ; 00 00 00 01 00 02 00 03 00 04
; 00 05 01 С8 03 6С 00 07 22 30
Организовать доступ к предпоследнему байту можно следующим образом:
1) Mas[19] ; =30
2) Mas[13h] ; =C8
3) Mov esi,19
Mas[esi] ; =30