Как вывести флаг ассемблер
Перейти к содержимому

Как вывести флаг ассемблер

  • автор:

FasmWorld Программирование на ассемблере FASM для начинающих и не только

Учебный курс. Часть 24. Команды управления флагами

Автор: xrnd | Рубрика: Учебный курс | 10-09-2010 | Распечатать запись

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

Как вы, наверно, помните флаги изменяются в результате выполнения арифметических и логических команд, а также команд сдвига. Регистр флагов можно сохранить в стек с помощью команды PUSHF и восстановить из стека с помощью команды POPF. Кроме того, в процессоре существуют специальные команды, которые позволяют явно установить или сбросить флаги CF, DF и IF. Это очень простые команды: у них нет операндов и результатом является только изменение значения соответствующего флага.

Флаг переноса CF

Команда CLC сбрасывает флаг CF.

Команда STC устанавливает флаг CF в единицу.

Команда CMC инвертирует значение флага CF.

Флаг направления DF

Этот флаг определяет направление обработки данных цепочечными командами (о них подробно расскажу в отдельной статье). Он должен устанавливаться или сбрасываться перед использованием этих команд.

Команда CLD сбрасывает флаг DF.

Команда STD устанавливает флаг DF в единицу.

Флаг прерывания IF

Этот флаг определяет, разрешены в данный момент прерывания или нет (о прерываниях тоже будет отдельная статья).

Команда CLI сбрасывает флаг IF (запрещает прерывания).

Команда STI устанавливает флаг IF в единицу (разрешает прерывания).

Команды LAHF и SAHF

Команда LAHF загружает младший байт регистра флагов в AH. Её удобно использовать, когда нужно получить значения сразу нескольких флагов. Порядок расположения флагов представлен на рисунке:

Команда SAHF выполняет обратную операцию — загружает содержимое AH в младший байт регистра флагов. Это позволяет одновременно изменить значения нескольких флагов. При этом биты 1, 3, 5 регистра AH игнорируются.

Пример программы

В качестве примера использования команды LAHF я написал процедуру, которая получает и выводит на консоль значения флагов SF, ZF, AF, PF и CF. Подобную процедуру можно использовать в отладочных целях. Достаточно добавить её вызов в то место программы, где хочется проверить состояние флагов.

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

;Процедура вывода значения флага CF в виде символа print_cf: push ax push dx mov ah,2 ;Функция DOS 02h — вывод символа mov dl,’0′ ;DL = ‘0’ adc dl,0 ;Если CF = 1, то в DL будет символ ‘1’ int 21h ;Обращение к функции DOS pop dx pop ax ret

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

;Процедура вывода состояния флагов на консоль print_flags: push ax push cx push dx pushf ;Сохранение регистра флагов lahf ;Загрузка младшего байта FLAGS в AH mov cl,ah ;CL = AH mov ah,9 ;Функция DOS 09h — вывод строки mov dx,s_sf ;DX = адрес строки ‘FLAGS: SF=’ int 21h ;Обращение к функции DOS shl cl,1 ;Сдвиг CL влево на 1 бит call print_cf ;Печать выдвинутого бита mov dx,s_zf int 21h ;Вывод строки ‘ ZF=’ shl cl,1 ;Сдвиг CL влево на 1 бит call print_cf ;Печать выдвинутого бита mov dx,s_af int 21h ;Вывод строки ‘ AF=’ shl cl,2 ;Сдвиг CL влево на 2 бита call print_cf ;Печать выдвинутого бита mov dx,s_pf int 21h ;Вывод строки ‘ PF=’ shl cl,2 ;Сдвиг CL влево на 2 бита call print_cf ;Печать выдвинутого бита mov dx,s_cf int 21h ;Вывод строки ‘ CF=’ shl cl,2 ;Сдвиг CL влево на 2 бита call print_cf ;Печать выдвинутого бита mov dx,s_endl int 21h ;Вывод конца строки popf ;Восстановление регистра флагов pop dx pop cx pop ax ret

Полный исходный код примера — printflags.asm. Вывод программы выглядит так:

Упражнение

Чтобы потренироваться в работе с флагами напишите следующую программу. Вычислите сумму значений флагов CF, SF, ZF и выведите на консоль результат сложения. Если результат больше 1, инвертируйте значение флага CF ��

Комментарии:

Здравствуте!
Сделал, но только вот какя фигня, если запускаю программу из винды, то сумма=0, а если запускаю в отладчике, то сумма =1. С чего такая разница не понимаю.
use16
org 100h
jmp start
;————————-
summ_flags db ‘Summa Flagov = $’

;————————-
start:
mov dx,summ_flags
mov ah,09h
int 21h
xor ax,ax
lahf
shr ah,1
call summa
shl ah,2
call summa
shl ah,1
call summa
cmp cl,1
jg inv_cf
vivod:
xor dx,dx
mov ax,cx
mov bx,10
div bx

add dl,’0′
mov ah,02h
int 21h
mov ah,08h
int 21h
mov ax,4c00h
int 21h

summa:
jc summ
ret
summ:
add cl,1
ret
inv_cf:
cmc
jmp vivod

Так как у тебя перед командой LAHF идёт XOR AX,AX, то сумма флагов должна быть равна 1 (ZF = 1, CF = 0, SF = 0).

Разница из-за того, что ты явно не обнуляешь регистр CL. Отладчик при запуске меняет значения регистров, а когда запускаешь без отладчика, в регистрах может быть другое значение.

Кстати, на подобном принципе делают защиту от отладки. Если при запуске программы какие-то регистры нулевые, то это запуск в отладчике )))

Регистр флагов

Ещё один важный регистр, использующийся при выполнении большинства ко­манд, – регистр флагов. Как и раньше, его младшие 16 бит, представлявшие собой весь этот регистр до процессора 80386, называются FLAGS. В EFLAGS каждый бит является флагом, то есть устанавливается в 1 при определённых условиях или установка его в 1 изменяет поведение процессора. Все флаги, расположенные в старшем слове регистра, имеют отношение к управлению защищённым режи­мом, поэтому здесь рассмотрен только регистр FLAGS (см. рис. 3):

Рис. 3. Регистр флагов FLAGS

CFфлаг переноса. Устанавливается в 1, если результат предыдущей опера­ции не уместился в приёмнике и произошёл перенос из старшего бита или если требуется заём (при вычитании), в противном случае – в 0. Например, после сложения слова 0FFFFh и 1, если регистр, в который надо поместить результат, – слово, в него будет записано 0000h и флаг CF = 1.

PFфлаг чётности. Устанавливается в 1, если младший байт результата предыдущей команды содержит чётное число битов, равных 1, и в 0, если нечётное. Это не то же самое, что делимость на два. Число делится на два без остатка, если его самый младший бит равен нулю, и не делится, когда он равен 1.

AFфлаг полупереноса или вспомогательного переноса. Устанавливается в 1, если в результате предыдущей операции произошёл перенос (или заём) из третьего бита в четвёртый. Этот флаг используется автоматически коман­дами двоично-десятичной коррекции.

ZFфлаг нуля. Устанавливается в 1, если результат предыдущей команды – ноль.

SFфлаг знака. Он всегда равен старшему биту результата.

TFфлаг ловушки. Он был предусмотрен для работы отладчиков, не ис­пользующих защищённый режим. Установка его в 1 приводит к тому, что после выполнения каждой программной команды управление временно пере­даётся отладчику (вызывается прерывание 1).

IFфлаг прерываний. Сброс этого флага в 0 приводит к тому, что процессор перестаёт обрабатывать прерывания от внешних устройств. Обычно его сбрасывают на короткое время для выполнения критических участков кода.

DFфлаг направления. Он контролирует поведение команд обработки строк: когда он установлен в 1, строки обрабатываются в сторону уменьше­ния адресов, когда DF = 0 – наоборот.

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

Флаги IOPL (уровень привилегий ввода-вывода) и NT (вложенная задача) применяются в защищённом режиме.

Последний регистр FLAGS – это регистр состояния процессора (PSW). Из его 16 разрядов используются только девять (рис. 2): CF (Carry Flag) – флаг переноса при арифметических операциях, PF (Parity Flag) – флаг четности результата, AF (Auxiliary Flag) – флаг дополнительного переноса, ZF (Zero Flag) – флаг нулевого результата, SF (Sign Flag) – флаг знака (со­впадает со старшим битом результата), TF (Trap Flag) – флаг пошагового режима (используется при отладке), IF (Interrupt-enable Flag) – флаг разрешения аппаратных прерываний, DF (Direction Flag) – флаг направле­ния при строковых операциях, OF (Overflow Flag) – флаг переполнения.

Рис. 2. Регистр состояния процессора 8086.

Биты регистра состояния устанавливаются или очищаются в зависи­мости от результата исполнения предыдущей команды и используются не­которыми командами процессора. Биты регистра состояния могут также устанавливаться и очищаться специальными командами процессора (о си­стеме команд процессора будет рассказано в следующем разделе).

Как узнать состояние флага

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

Вообще это требуется редко. Обычно флаги проверяются после выполнения таких команд, как CMP, инструкциями перехода, такими, например, как JB.

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

Но в наборе команд процессора Intel таких инструкций нет, поэтому, если вам очень захотелось узнать состояние отдельного флага, то придётся подойти к решению этой задачи творчески…

Итак, в языках Ассемблера INTEL есть по крайней мере три способа получить состояние отдельного флага. Далее мы их рассмотрим.

Использование команд перехода

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

Таблица 1. Команды, которые можно использовать для проверки флагов.

Команда Условие перехода
JZ, JE ZF = 1
JC, JB, JNAE CF = 1
JS SF = 1
JO OF = 1
JPE, JP PF = 1
JNZ, JNE ZF = 0
JNC, JNB, JAE CF = 0
JNS SF = 0
JNO OF = 0
JPO, JNP PF = 0

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

А теперь о том, как это использовать. Поясню на примере:

Если флаг ZF = 1, то мы переходим к метке ZF_1 и записываем в регистр АН число 1. Если флаг ZF = 0, то мы переходим к метке ZF_0 и записываем в регистр АН число 0. Таким образом мы узнаём состояние флага ZF и записываем его в регистр AH.

Чтение флагов в регистр

Имеется также особая команда, которая позволяет загрузить младшие 8 битов регистра флагов в регистр AH. Это команда LAHF. Пример:

Команда LAHF загружает в регистр АН следующие флаги:

Разряды регистра АН и флаги, которые в них загружает команда LAHF
7 6 5 4 3 2 1 0
SF ZF 0 AF 0 PF 1 CF

То есть в разряд 1 всегда загружается единица (поэтому в примере выше первый вызов LAHF загрузил в АН число 2), в разряды 3 и 5 всегда загружается 0. В остальные разряды загружаются состояния соответствующих флагов на момент вызова команды LAHF.

Чтение флагов через стек

Есть также команда PUSHF, которая загружает регистр флагов в стек. Преимущество этой команды в том, что она загружает все флаги (даже если регистр флагов 32-разрядный). Однако её не советуют использовать без крайней необходимости, потому что она выполняется довольно медленно (иногда это важно).

Ещё одно удобство этой команды заключается в том, что она загружает именно регистр флагов. И если потом выгрузить стек, например, в регистр АХ, то все биты регистра АХ будут установлены в том же порядке, что и биты регистра флагов. Пример:

22. Команды управления флагами i8086

Статья основана на материале xrnd с сайта asmworld (из учебного курса по программированию на ассемблер 16-битного процессора 8086 под DOS).

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

Как вы, наверно, помните флаги изменяются в результате выполнения арифметических и логических команд, а также команд сдвига. Регистр флагов можно сохранить в стек с помощью команды PUSHF и восстановить из стека с помощью команды POPF. Кроме того, в процессоре существуют специальные команды, которые позволяют явно установить или сбросить флаги CF, DF и IF. Это очень простые команды: у них нет операндов и результатом является только изменение значения соответствующего флага.

Флаг переноса CF

Команда CLC сбрасывает флаг CF.

Команда STC устанавливает флаг CF в единицу.

Команда CMC инвертирует значение флага CF.

Флаг направления DF

Этот флаг определяет направление обработки данных цепочечными командами (о них подробно расскажу в отдельной статье). Он должен устанавливаться или сбрасываться перед использованием этих команд.

Команда CLD сбрасывает флаг DF.

Команда STD устанавливает флаг DF в единицу.

Флаг прерывания IF

Этот флаг определяет, разрешены в данный момент прерывания или нет (о прерываниях тоже будет отдельная статья).

Команда CLI сбрасывает флаг IF (запрещает прерывания).

Команда STI устанавливает флаг IF в единицу (разрешает прерывания).

Команды LAHF и SAHF

Команда LAHF загружает младший байт регистра флагов в AH. Её удобно использовать, когда нужно получить значения сразу нескольких флагов. Порядок расположения флагов представлен на рисунке:

Команда SAHF выполняет обратную операцию — загружает содержимое AH в младший байт регистра флагов. Это позволяет одновременно изменить значения нескольких флагов. При этом биты 1, 3, 5 регистра AH игнорируются.

Пример программы

В качестве примера использования команды LAHF я написал процедуру, которая получает и выводит на консоль значения флагов SF, ZF, AF, PF и CF. Подобную процедуру можно использовать в отладочных целях. Достаточно добавить её вызов в то место программы, где хочется проверить состояние флагов.

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

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

Полный исходный код примера:

Вывод программы выглядит так:

Упражнение

Чтобы потренироваться в работе с флагами напишите следующую программу. Вычислите сумму значений флагов CF, SF, ZF и выведите на консоль результат сложения. Если результат больше 1, инвертируйте значение флага CF.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *