Как избавится от команд условного перехода в Ассемблере.

Низкоуровневое программирование портов, микроконтроллеров и т.д.

Модератор: Andy

Ответить
АНБ
Сообщения: 1
Зарегистрирован: 29 янв 2016, 23:18

29 янв 2016, 23:28

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

По статистике условных переходов в программе на Ассемблере 15-16 %, то есть каждая шестая команда, да и еще и 4% безусловных. Вот типичный оператор выбора, проверяющий код нажатой кнопки и осуществляющий семь условных переходов, пример из старой программы «Тетрис».

t_KeyHit:

cmp al,4Bh
je t_Left
cmp al,4Dh
je t_Right
cmp al,50h
je t_Down
cmp al,39h
je t_Rotate
cmp al,44h
je t_Pause
cmp al,01h
je t_j0
cmp al,10h
jne t_KDjmp

t_Left:
mov bx,t_XY
mov cx,PieceW
dec bx
call Fits
adc t_XY,-1
jmp t_KeyDone

t_Right:
mov bx,t_XY
mov cx,PieceW
inc bx
call Fits
sbb t_XY,-1
jmp t_KeyDone

t_Down:


t_Rotate:


t_Pause:


t_j0:


t_KD jmp:


Семь условных переходов – это слишком… В данном случае избавиться от них очень легко при помощи команды xlat, которая упорядочит регистр AL, а упорядоченные адреса обработчиков могут находится в массиве.

В общем нужно выполнить два шага:
• разместить адреса функций, на которые осуществляется выбор в массиве;
• создать таблицу перекодировки согласно последнему байту смещений адресов в этом массиве.
Код может быть примерно таким:

.data
mas dd offset t_Left, offset t_Right, offset t_Down, …
;это начало массива адресов функций
tbl db 0,последний байт смещения je t_Pause, 14 dup (0),
db последний байт смещения jne t_KDjmp, 41 dup (0),
db последний байт смещения je t_Down, …
;это начало таблицы перекодировки для xlat
.code

t_KeyHit:

lea ebx, tbl ;заносим адрес таблицы
lea eax, mas ;заносим адрес массива
xlat ;упорядочиваем кнопки по смещению
mov ebx,[eax];адрес нужного обработчика из массива
jmp ebx ;или call ebx

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

Следующий пример возьмем из той же программы. Если на метку t_KeyHit можно перейти только при нажатии двух кнопок Left или Right, то вся вышеприведенная конструкция превращается в условный оператор: «Если t_KeyHit, то t_Left, иначе t_Right, выход».

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

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

t_KeyHit:

lea ebx, tbl ;заносим адрес таблицы
lea eax, mas ;заносим адрес массива
xlat ;упорядочиваем кнопки по смещению
push … ;заносим все нужные данные в стек
push … ;по определенному порядку из массива
push …

mov esp,eax ;переводим указатель стека на адрес
;нужных данных
pop eax ;извлекаем нужные данные
pop edx ;извлекаем нужные данные
mov ebx,t_XY
mov ecx,PieceW
adc ebx, eax ;вместо inc bx и dec bx
call Fits
adc t_XY, edx ;вместо adc t_XY,-1 и sbb t_XY,-1
jmp t_KeyDone

Это не действующая программа, а пример рассуждений. Можно указатель стека установить на упорядоченные данные в массиве сегмента данных, если ОС позволяет такие манипуляции.

Когда в операторе выбора «Если …, то …, иначе …, выход» в обеих ветвях разный код, возможно, рациональнее выполнить обе ветви кода занести оба результата в стек или временный массив данных, затем уже нужный результат вытянуть от туда, а второй просто игнорировать.

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

Проблема остается только с циклами, но их можно обрабатывать безусловным переходом:

LEA EAX, _МеткаНачалаЦикла
_МеткаНачалаЦикла:
тело цикла
CMOVcc EAX, _АдресВыходаИзЦикла
CMP EAX

Говорят, что команда CMOVcc не выгружает конвейер, но работает с тремя регистрами или двумя регистрами и памятью. Можно обойтись и без команды CMOVcc, но тогда понадобятся 3-5 других команд и 2 регистра, правда, можно воспользоваться сопроцессором.

Успехов Вам!
Ответить