- •Аппаратно-ориентированное программирование
- •Ббк 32.973.73
- •Удк 681.3 ббк 32.973.73ф 73
- •1. Основы программирования на ассемблере
- •1.1. Принципы построения ассемблерных программ
- •1.2. Понятие архитектуры компьютера
- •1.3. Регистры программиста в ia32
- •1.4. Описание сегментной структуры программы
- •2. Простейшие средства ассемблера
- •2.1. Средства описания данных
- •2.2. Обращения к функциям ос посредством прерываний
- •2.3. Средства преобразования в исполняемый файл
- •2.4. Управление строками при выводе и ввод данных
- •2.5. Простейшие способы адресации
- •3. Архитектурные элементы для построения программ
- •3.1. Организация условных переходов
- •3.2. Средства организации циклов
- •3.3. Особенности команд умножения и деления
- •3.4. Организация процедур
- •3.5. Неарифметические операции над кодами
- •4. Использование неэлементарных способов адресации
- •4.1. Косвенно-регистровая адресация
- •4.2. Использование индексной адресации данных
- •4.3. Базовая и индексно базовая адресации
- •4.4. Адресация с масштабированием
- •5. Взаимосвязи программных единиц
- •5.1. Многомодульная разработка программ
- •5.2. Использование библиотек объектных модулей
- •5.3. Организация стекового кадра подпрограммы
- •5.4. Программный доступ к системным функциям Win32
- •5.5. Особенности использования объектных файлов формата coff
- •5.6. Стандартный доступ к системным функциям Unix
- •6. Вспомогательные средства базовой архитектуры
- •6.1. Использование строковых команд пересылки
- •6.2. Применение строковых команд сравнения
- •7. Использование ассемблерных отладчиков
- •7.1. Особенности отладчика gdb для программ в Linux
- •7.2. Отладчики текстового режима для Windows
- •Библиографический список
- •Оглавление
3.5. Неарифметические операции над кодами
К настоящему моменту мы познакомились с командами преобразования информации, которые выполняют сложение, вычитание, умножение и деление. Кроме этих, очевидным образом необходимых команд, в архитектуру процессора входят еще некоторые команды, выполняющие неарифметические операции. В составе таких команд можно выделить две основные группы, которые предназначены для поразрядных логических операций и операций сдвига двоичных кодов.
Для выполнения поразрядной логической операции И служит команда с мнемокодом AND, имеющая общий вид
AND операнд1, операнд2
Основное практическое применение эта команда имеет с константным вторым операндом, который в этом случае называют маской логической операции. Команда AND с маской применяется для выборочного сброса в нуль отдельных битов двоичного кода первого операнда. (Такое действие основано на обычных, хорошо известных свойствах логической операции И: x&0=0, x&1=x.) Например, команда
AND al, 3Fh
сбрасывает в нуль два старших бита двоичного кода в регистре AL
Для выполнения поразрядной логической операции ИЛИ служит команда с мнемокодом OR, имеющая общий вид
OR операнд1, операнд2
Основное практическое применение эта команда имеет также с константным вторым операндом - маской логической операции ИЛИ. Команда OR с маской применяется для выборочной установки (в единицу) отдельных битов двоичного кода первого операнда. (Такое действие основано на обычных, хорошо известных свойствах логической операции И: x0=x, x1=1.) Например, команда
OR dx, 8001h
устанавливает в единицу старший и младший биты двоичного кода в регистре DX.
Менее употребительной является команда исключающего ИЛИ, задаваемая мнемокодом XOR и употребляемая обычно также с константой - маской в качестве второго операнда. В общем случае она имеет вид
XOR операнд1, операнд2
и служит для обращения (инверсии) тех двоичных разрядов первого операнда, которым соответствуют единичные биты во втором операнде.
Последняя из логических команд выполняет операции поразрядного инвертирования двоичного кода единственного ее операнда и имеет вид
NOT операнд
Команды сдвига представлены множеством модификаций. Основными в большинстве применений являются команды с мнемокодами SHL и SHR, наименования которых восходят к английским словам Shift, Left и Right. Эти команды задают сдвиг двоичного кода в первом операнде на число разрядов, заданное во втором операнде. В настоящее время основными формами этих команд являются задаваемые следующими схемами:
SHL операнд1, число
SHR операнд1, число
Биты, вдвигаемые в этих командах, всегда нулевые. Кроме перечисленных команд имеются еще команды арифметического сдвига SAL, SAR и циклического сдвига ROL, ROR, RCL, RCR. Из-за редкого их использования они не будут рассматриваться в данном пособии.
Далее в листинге 3.5.1 приведена программа, демонстрирующая материал разделов 3.4 и 3.5. В этой программе содержатся подпрограммы вывода на экран содержимого регистров AL, AX и EAX в виде шестнадцатеричных представлений. Причем процедура call wrhex_hal отображает на экране значение только младшей половины регистра AL, выдавая его всегда в виде одной шестнадцатеричной цифры, процедура wrhex_al выдает содержимое регистра AL в виде двух шестнадцатеричных цифр, процедура wrhex_ax выдает содержимое регистра AX в виде четырех шестнадцатеричных цифр, а процедура wrhex_eax выдает содержимое регистра EAX в виде восьми шестнадцатеричных цифр.
; Использование процедур.
GLOBAL _start
SEGMENT .text
_start: mov al, 0d5h
call wrhex_hal
call wrcrlf
mov al, 0fah
call wrhex_al
call wrcrlf
mov ax, 05e7bh
call wrhex_ax
call wrcrlf
mov eax, 03fec5e7bh
call wrhex_eax
call wrcrlf
mov eax,1
int 80h ; function=exit for Linux
;procedure wrcrlf
wrcrlf:
pusha
mov eax,4 ; N function=write
mov ebx,1 ; N handle=1 (stdout)
mov edx,1 ; number of byte
mov ecx, crlf ; address of crlf
int 80h
popa
ret
; end procedure wrcrlf
;procedure wrhex_hal
wrhex_hal: push eax
push ebx
push ecx
push edx
and al, 0Fh
cmp al, 10
jge hexa
add al, '0'
jmp wrchar
hexa: add al,'A'-10 ; из AL вычесть 10 и прибавить значение 'A'
wrchar:
mov [cha],al
mov eax,4 ; N function=write
mov ebx,1 ; N handle=1 (stdout)
mov edx,1 ; number of byte
mov ecx, cha ; address of cha
int 80h
pop edx
pop ecx
pop ebx
pop eax
ret
; end procedure wrhex_hal
; procedure wrhex_al
wrhex_al: push eax
shr al,4
call wrhex_hal
pop eax
call wrhex_hal
ret
; end procedure wrhex_al
; procedure wrhex_ax
wrhex_ax: push eax
shr ax,8
call wrhex_al
pop eax
call wrhex_al
ret
; end procedure wrhex_ax
; procedure wrhex_eax
wrhex_eax: push eax
shr eax,16
call wrhex_ax
pop eax
call wrhex_ax
ret
; end procedure wrhex_eax
SEGMENT .data
crlf db 10
cnt dd 0
cha db 0
Листинг 3.5.1. Вывод шестнадцатеричного значения из регистров AL, AX, EAX
Кроме того, в качестве вспомогательной использована процедура wrcrlf, вызов которой выводит управляющий символ перевода на следующую строку, поэтому когда в основной программе нужно задать такой переход вывода не следующую строку экрана, требуется лишь запись команды CALL wrcrlf.
Во всех приведенных процедурах используется сохранение регистров и восстановление их из стека, причем в процедуре wrcrlf для этого применены универсальные команды PUSHA и POPA, а в остальных процедурах сохраняются и восстанавливаются только действительно изменяемые в них регистры.
Основную работу для всех процедур выполняет подпрограмма wrhex_hal, предназначенная для вывода значения младшей половины регистра AL в виде одной шестнадцатеричной цифры. В ее начале содержимое регистра AL изменяется обнулением битов в старшей половине регистра. Для этих целей использована команда логического поразрядного умножения на константу 0fh. После этого выполняется сравнение промежуточного результата (значения младшей половины) с константой 10.
Если рассматриваемое значение меньше 10, то шестнадцатеричная цифра представляется обычной десятичной цифрой, и в этом случае используется уже изученная выше технология получения выводимой цифры путем прибавления к ее значению кода цифры '0'. В противном случае следует вычесть из промежуточного результата значение 10, получив тем самым числовое смещение требуемого кода от самой меньшей по значению шестнадцатеричной цифры, отличной от арабстких, и изображаемой буквой 'A'. Затем добавить к очередному результату значение кода буквы 'A' (что и порождает код шестнадцатеричной цифры, изображаемой соответствующей буквой). Две последние операции оказалось возможным объединить в одну с помощью команды
add al,'A'-10
которая прибавляет к промежуточному значению в регистре AL значение константы, сформированной из разности кода буквы 'A' и числа 10.
Далее подготовленное в регистре AL значение любой возможной шестнадцатеричной цифры выводится с помощью традиционной последовательности команд, обеспечивающих вызов системной функции write. Причем, в соответствии с требованиями организации обращения выводимое значение переносится из регистра AL в однобайтовое служебное поле данных с именем cha.
Процедура wrhex_al строится уже проще на основе использования процедуры wrhex_hal. В начале из аргумента - регистра AL - сдвигом на 4 бита в младшую половину регистра помещается его бывшая старшая половина. Затем обращением call wrhex_hal на экран выводится шестнадцатеричная цифра, отвечающая значению этой старшей половины. Потом в регистре AL восстанавливается первоначальное значение, бывшее в нем при обращении к данной подпрограмме call wrhex_hal. С этой целью ранее запомненное для сохранения значение всего регистра EAX восстанавливается командой pop eax. Далее опять вызывается подпрограмма wrhex_hal, которая выводит в виде шестнадцатеричной цифры значение младшей половины регистра AL. Тем самым отображаются обе шестнадцатеричные цифры значения всего регистра AL, причем в правильном порядке. Заметим, что какое-либо восстановление регистров после последнего вызова уже не требуется, так как после восстановления значения регистра EAX никаких изменений регистров в текущей процедуре wrhex_al не происходит. Это демонстрирует, что общее правило восстановления регистров может в конкретных ситуациях видоизменяться.
Процедуры wrhex_ax и wrhex_eax построены очень похоже на процедуру wrhex_al, они отличаются только тем, что на первом этапе содержимое регистров, соответственно, AX и EAX сдвигается на 8 и 16 битов. Тем самым вначале задается для промежуточного вывода старшая половина соответствующего регистра, а затем, после восстановления значения регистра eax, используется отображение содержимого младшей половины этого регистра. Отображения же указанных половин выполняются вызовом, соответственно, процедур wrhex_al и wrhex_ax.
В основной части программы после занесения в регистры AL, AX и EAX констант 0fah, 05e7bh, 03fec5e7bh осуществляются вызовы, соответственно, процедур wrhex_al, wrhex_ax и wrhex_eax, что обеспечивает демонстрацию использования этих процедур.
Упражнения
1. Разработать подпрограмму, которая считывает последовательно десятичные цифры со стандартного ввода до тех пор, пока не встретится символ, отличный от цифры. Читаемая таким образом последовательность десятичных цифр должна преобразовываться в соответствующее двоичное значение, возвращаемое подпрограммой в регистре EAX в качестве результата.
2. Использовать подпрограмму предыдущего упражнения для ввода двух десятичных чисел, задаваемых со стандартного ввода; выполнить сложение этих чисел в основной части программы и затем, переписав основную преобразующую последовательность команд из листинга 3.3.1 в виде подпрограммы, обращением к последней вывести полученную сумму.