Форум программистов, компьютерный форум, киберфорум
Assembler, MASM, TASM
Войти
Регистрация
Восстановить пароль
Карта форума Темы раздела Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.63/40: Рейтинг темы: голосов - 40, средняя оценка - 4.63
21 / 21 / 19
Регистрация: 18.03.2014
Сообщений: 148
1

Аналог Switch в masm

22.10.2015, 11:05. Показов 7258. Ответов 3
Метки нет (Все метки)

Author24 — интернет-сервис помощи студентам
Доброго времени суток, уважаемые форумчане!

Изучаю работу с сообщениями винды из под masm. Есть такой код:
Assembler
1
2
3
4
5
6
7
8
9
10
WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM 
    .if uMsg==WM_DESTROY
        invoke PostQuitMessage, NULL
    .else            
        invoke DefWindowProc, hWnd, uMsg, wParam, lParam        
        ret
    .endif
    xor eax,eax
    ret
WndProc endp
Как видно из кода, на радость си_с_плюсами_филла есть макросы .if, .else (также есть .while). Хотелось бы узнать, есть ли что-то подобное для switch. Если делать обработку для большого кол-ва сообщений, то куча вложенных друг в друга if/else, то код явно простотой не отличится. Можно сделать, в принципе, через метки, но хотелось бы привычным swich-ом.

Есть ли аналог свича, как с ифом?
0
Лучшие ответы (1)
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
22.10.2015, 11:05
Ответы с готовыми решениями:

Аналог switch в QT
здраствуйте подскажите в QT не работает switch? вот часть кода switch (str1) { ...

Аналог Switch
Итак, задача: умножать ячейку B на ячейку C, D, E, или F, в зависимости от того, что находится в...

Аналог switch
ребят, есть в actionscript что-то типа swith в c++ ?

Аналог Case Switch
Добрый день ... что-то не могу подходящее нагуглить. Надеюсь на вашу помощь. Имею табличку: A ...

3
Ушел с форума
Автор FAQ
16279 / 7604 / 1065
Регистрация: 11.11.2010
Сообщений: 13,617
22.10.2015, 11:54 2
Лучший ответ Сообщение было отмечено bogdan_017 как решение

Решение

bogdan_017,
Сообщения, которые Windows посылает приложению, на самом деле получает процедура окна, определенная для данного класса. Чаще всего она представляет собой конструкцию «выбора», которую в языке C/C++ называют SWITCH, в Pascal/Delphi — CASE, в Basic — SELECT CASE, а в абстрактном языке программирования — мультивлетвением. Задача конструкции CASE заключается в проверке на равенство значения переменной var элементу из списка.
Код
CASE <var> OF
<case A> (выполнить, если var = A)
<case B> (выполнить, если var = B)
.  .  .
<case N> (выполнить, если var = N)
<default> (выполнить, если нет соответствия)
END_CASE
Данная конструкция направляет сообщения функциям обработчикам. На ассемблере конструкция CASE может быть реализована, по крайней мере, семью способами:
  1. тупо, через повторяющиеся команды cmp и je.
  2. через макрос
  3. через высокоуровневые директивы .IF .ELSEIF .ELSE
  4. через косвенные переходы или косвенный вызов процедур
  5. через команду SCASD
  6. через последовательное приближение
  7. через имитацию команды XLAT
Рассмотрим подпрограмму, обрабатывающую только четыре сообщения: WM_CREATE=1, WM_DESTROY=2, WM_PAINT=0Fh и WM_TIMER=113h. Пусть сообщение WM_XX обрабатывается по адресу @@WM_XX. Учтем так же, что частота появлений различных сообщений неодинакова (спасибо Y_Mur), поэтому проверяем сперва наиболее часто посылаемые WM_PAINT и WM_TIMER, затем однократно используемую WM_CREATE, и наконец последнее сообщение в жизни нашего приложения WM_DESTROY.
Итак, первый способ очевиден.
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
cmp dword ptr Msg,WM_TIMER 
    je @@WM_TIMER
cmp dword ptr Msg,WM_PAINT
    je @@WM_PAINT
cmp dword ptr Msg,WM_CREATE
    je @@WM_CREATE
cmp dword ptr Msg,WM_DESTROY
    je @@WM_DESTROY
leave        
    jmp DefWindowProcA; обработка по умолчанию      
@@WM_DESTROY: 
. . .
@@WM_CREATE:
. . .
@@WM_PAINT:
. . .
@@WM_TIMER:
. . .
один из вариантов первого способа — заменить команду cmp на команду sub
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
mov eax,Msg
dec eax 
jz @@WM_CREATE  ;WM_CREATE=1
dec eax
jz @@WM_DESTROY ;WM_CREATE+1=WM_DESTROY=2
sub eax,0Dh 
jz @@WM_PAINT   ;WM_DESTROY+0Dh=WM_PAINT=0Fh
sub eax,104h
jz @@WM_TIMER   ;WM_PAINT+104h=WM_TIMER=113h
mov esp,ebp     ;все сообщения, не обрабатываемые в функции 
      pop ebp       ;WndProc, направляются на обработку по умолчанию      
    jmp DefWindowProcA 
@@WM_DESTROY: 
. . .
Способ второй. Используем макрос с IRP-блоком (Вы, конечно, можете, если захотите, написать другой макрос). Блоки повторения IRP имеют следующий вид:
Код
IRP p,<v1,…,vn>
<тело блока>
ENDM
Запись в уголках <v1,…,vn> это явно указываемые фактические параметры, p — некоторое имя, оно играет роль формального (фиктивного) параметра и используется в предложениях тела блока. Фактические параметры vi перечисляются через запятую, а вся их совокупность обязательно заключается в угловые скобки. Встретив IRP-блок, макрогенератор заменит блок на n копий тела блока (по одной копии на фактический параметр), причем в i-той копии все вхождения имени p заменятся на vi. Знак & указывает границу формального параметра, выделяет его из окружающего текста, но в окончательный текст программы не попадает. Если в теле блока повторения или макроса имеются комментарии (в конце каких-то предложений или как самостоятельные предложения), то они переносятся во все копии блока. Если комментарий полезен при описании самого блока повторения, но совершенно не нужен в его копиях — тогда комментарий начинают с двух точек с запятой — такие комментарии не копируются.
Assembler
1
2
3
4
5
6
7
8
IRP CASE,<WM_TIMER,WM_PAINT,WM_CREATE,WM_DESTROY>;;последовательность вариантов
cmp dword ptr Msg, CASE;;вариант найден?
je @@&CASE
ENDM;;закончить макроописание
leave       ; обработка по умолчанию
    jmp DefWindowProcA      
@@WM_DESTROY: 
. . .
Способ третий. Пример использования директив ассемблера .IF .ELSEIF .ELSE .ENDIF:
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
.IF (Msg==WM_TIMER)
. . .           ;здесь обрабатывается сообщение WM_TIMER
.ELSEIF (Msg==WM_PAINT)
. . .           ;здесь обрабатывается сообщение WM_PAINT
.ELSEIF (Msg==WM_CREATE)
. . .           ;здесь обрабатывается сообщение WM_CREATE
.ELSEIF (Msg==WM_DESTROY)
. . .           ; здесь обрабатывается сообщение WM_DESTROY
.ELSE
 leave      
jmp DefWindowProcA; обработка по умолчанию
.ENDIF
И вариант с макросом, и вариант с .IF .ELSEIF .ELSE .ENDIF все равно будут оттранслированы в повторяющиеся команды CMP и JE
Способ четвертый. Налицо в программе очень много повторяющихся почти одинаковых фрагментов. Нельзя ли написать программу как-нибудь иначе? Чем различаются эти фрагменты программы? Разными операндами в команде CMP, разными адресами переходов или разными вызываемыми процедурами. Уберем операнды команды CMP в какой-нибудь регистр и используем для разных адресов переходов косвенный переход, а для разных вызываемых процедур — косвенный вызов процедуры.
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
;Таблица сообщений, которые обрабатывает процедура окна
MsgTable dd WM_PAINT, @@WM_PAINT, WM_TIMER, @@WM_TIMER, WM_CREATE, @@WM_CREATE, WM_DESTROY, @@WM_DESTROY, 0; Конец списка обязательно 0
. . .
;Вызов соответствующего обработчика
mov esi,offset MsgTable ;устанавливаем в ESI адрес таблицы-диспетчера
a1:     mov eax,[esi]       ;устанавливаем в eax тип сообщения
    test eax,eax    ;это конец таблицы? Если да, то данное сообщение не
jz @@default; обрабатывается приложением - передаем управление Windows
    cmp eax,Msg
    jne a2  ;для данного сообщения не найден обработчик
add esi,4   ;устанавливаем в esi адрес обработки сообщения
jmp [esi]           ;передаем управление обработчику сообщения
a2: add esi,8           ;перейдем к следующему сообщению
    jmp short a1    
@@default: leave        ;Обработчик не найден, возвращается значение,
jmp DefWindowProcA  ; которое дает стандартный обработчик
@@WM_DESTROY:
Способ пятый. Используем команду SCASD с префиксом REPNE. Внесем в конец таблицы сообщений, которые обрабатывает процедура окна, адрес обработки по умолчанию, а в регистр ECX количество элементов в таблице сообщений минус один (компилятор подсчитает это число автоматически). Напомню, что команда SCASD сравнивает значение в регистре EAX с ячейкой памяти, локализуемой регистром EDI. Префикс REPNE не только заставляет циклически выполняться команду сканирования, пока ECX не станет равным 0, но и отслеживает состояние флага ZF. Как только содержимое в ячейке памяти совпадет со значением в регистре EAX, сканирование остановится. Регистр EDI будет указывать не на ячейку памяти, значение которой совпадает с регистром EAX, а на следующую ячейку. Сканирование также остановится, если содержимое регистра ECX станет равным 0. При этом в регистре EDI будет адрес метки @@default (адрес обработки по умолчанию)
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
;Таблица сообщений, которые обрабатывает процедура окна
MsgTable dd WM_PAINT, @@WM_PAINT, WM_TIMER, @@WM_TIMER, WM_CREATE, \ 
@@WM_CREATE, WM_DESTROY, @@WM_DESTROY, @@default
MsgCount = ($ - MSGTable - 4)/4
. . .
mov edi,offset MsgTable;устанавливаем в EDI адрес таблицы-диспетчера
mov eax,Msg
mov ecx,MsgCount;удвоенное количество обрабатываемых сообщений (4*2=8)
repne scasd     ;ищем сообщение и если найдем — передадим
jmp [edi]       ; управление обработчику сообщения
@@default:  leave       ; если обработчик не найден, вызывается
      jmp DefWindowProcA ; обработка по умолчанию
@@WM_DESTROY:
По величине это, по-моему, самый короткий код функции обработки сообщений. Кстати, читатель может спросить: «а что произойдет, если адрес обработчика сообщения совпадет с номером сообщения?» Этого не может произойти, так как максимальный номер стандартных сообщений равен 400h, а 32-разрядная операционная система Windows отводит каждой программе свое адресное пространство размером около двух гигабайт, начиная с адреса 100000h. За границы своего адресного пространства задача выйти не может, так же, как никакая другая задача не может работать с данным пространством - этим и определяется автономность программы.
Способ шестой — последовательное приближение. Пусть у нас уже не 4 обрабатываемых сообщения, а 100. Тогда при использовании с первого по пятый способ мы должны будем последовательно сравнить до 100 значений, до тех пор пока не найдем искомое. Увеличим скорость обработки сообщений, которые Windows посылает Вашему приложению. Рассортируем номера обрабатываемых нашей программой сообщений по возрастанию, теперь разделим номера сообщений на две равные группы — 50 сообщений с меньшими номерами в одной группе, 50 сообщений с большими номерами в другой. После определения, к какой из групп сообщений принадлежит вновь поступившее сообщение, нам потребуется уже не 100, а только 50+1 сравнение. Разобьем каждую из образовавшихся групп с номерами сообщений еще на две подгруппы, и потребуется уже 25+2 сравнений, вместо 100. Продолжаем делить подгруппы с номерами сообщений, и получаем 13+3 вместо 100, затем 7+4, далее 4+5, и, наконец, 2+6, больше разбивать подгруппы не имеет смысла (дальше было бы 1+7, а это одно и то же, что и 2+6). То есть, в общем случае, если 2(n-1) < N <= 2n, то определить N можно не более чем за n+1 приближение. Заметьте, увеличение скорости обработки будет сопровождаться разрастанием кода программы.
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
mas     dd WM_CREATE, WM_DESTROY... WM_PAINT ;массив сообщений
        n = ($ - mas)/4
mas1        dd @@WM_CREATE, @@WM_DESTROY,...  @@WM_PAINT;массив адресов
...
        xor ecx,ecx         ;в ecx индекс первого элемента
        mov edx,n-1         ;в edx индекс последнего элемента
        mov eax,Msg
        cmp eax,WM_CREATE;сравниваем с минимальным элементом таблицы
        jb short @@default;если меньше, то обработка по умолчанию
        mov ebx,edx     ;создаем индекс центрального элемента
a1:     cmp ecx,edx     ;проверка ecx>edx на окончание поиска
    ja short @@default;проверены все элементы, обработка по умолчанию
        shr ebx,1   ;индекс центрального элемента равен (edx+ecx)/2
        cmp mas[ebx*4],eax  ;сравниваем с искомым значением
        jb short a2         ;mas[ebx*4]<Msg
        je short a3         ;mas[ebx*4]=Msg     
inc ebx     ;учтем только что проверенное значение
        mov ecx,ebx         ;изменяем нижнюю границу поиска
        add ebx,edx     ;создаем индекс центрального элемента
        jmp short a1        ;переходим к следующему элементу
a2:     dec ebx     ;учтем только что проверенное значение
mov edx,ebx         ;изменяем верхнюю границу поиска
        add ebx,ecx     ;создаем индекс центрального элемента
        jmp short a1        ;переходим к следующему элементу
@@default:  leave           ;искомое значение не найдено,
jmp DefWindowProc   ;обработка по умолчанию
a3:     jmp mas1[ebx*4]     ;искомое значение найдено
@@WM_CREATE: ...
А это еще один, более короткий вариант последовательного приближения. Но для него требуется, чтобы количество сообщений должно быть равным 2n. Допустим, мы обрабатываем 16 сообщений,
Количество элементов 2(n-1) < N <= 2n
ebx (индекс элемента) 2(n-1)
ecx (счетчик приближений) n+1
так как N = 16, то в соответствии с таблицей: n = 4, ebx = 8, ecx = 5
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
mas     dd WM_CREATE, WM_DESTROY, ... WM_PAINT;массив сообщений
mas1        dd @@WM_CREATE, @@WM_DESTROY,... @@WM_PAINT ;массив адресов
n equ 4
...
        mov ecx,n+1         ;ecx=5
        mov ebx,1
        shl ebx,n-1         ;ebx=8 индекс центрального элемента
        xor edx,edx         ;нижняя граница поиска
        mov eax,Msg
        cmp eax,WM_CREATE;сравниваем с минимальным элементом таблицы
        jb short @@default;если меньше, то обработка по умолчанию
a1:     cmp mas[edx+ebx*4],eax  ;сравниваем с искомым значением
        ja short a2         ;mas[ebx*4]<Msg
        je short a3         ;mas[ebx*4]=Msg     
        lea edx,[edx+ebx*4] ;изменяем нижнюю границу поиска
a2:     shr ebx,1       ;изменяем индекс центрального элемента
        loop a1         ;переходим к следующему элементу
@@default:  leave           ;искомое значение не найдено
jmp DefWindowProc       ;обработка по умолчанию
a3:     jmp mas1[edx+ebx*4]     ;искомое значение найдено
@@WM_CREATE: ...
Если Вы и дальше хотите экспериментировать с обработкой сообщений — попробуйте заменить команду cmp на команды sub, dec, test, or или and.
Способ седьмой — имитация команды XLAT. При программировании на ассемблере стремятся либо получить максимально компактный код, либо достичь максимального быстродействия, хотя иногда добиваются и того, и другого. Способы четыре и пять, реализуемые через косвенный переход или косвенный вызов, хотя и имеют наименьший размер кода, но по быстродействию остаются таким же, как и способы с прямым переходом. То есть, всегда возможен вариант, когда будут перебраны все варианты сообщений, перед тем как запустится обработка сообщения по умолчанию. Реализуем мультиветвление при помощи косвенного перехода по таблице. Для увеличения скорости просмотра очереди сообщений строим процедуру подобную команде XLAT (сама команда XLAT, к сожалению, дает возможность работать только с 256 вызовами), строим таблицу MsgTable, где номер элемента таблицы соответствует номеру сообщения. Сами табличные элементы представляют собой адреса (процедур), где эти сообщения обрабатываются. Чтобы не перечислять все 400h (1024) сообщений, можно определить номер максимального сообщения Max_Msg и если номер сообщения больше максимального, сразу отправлять на обработку сообщения по умолчанию (метка @@default). Сообщения, не обрабатываемые в процедуре WinProc, также имеют адрес обработки соответствующий адресу метки @@default. При этом время выполнения перехода всегда одинаково для всех вариантов мультиветвления.
Assembler
1
2
3
4
mov eax,Msg
cmp eax,Max_Msg
ja @@default;MsgTable - таблица с адресами обработки
jmp MsgTable[eax*4];каждый адрес обработки расположен через 4 байта
Но у этого способа есть и оборотная сторона. К сожалению, большая скорость обработки сообщений будет компенсирована раздувшейся таблицей MsgTable.
3
21 / 21 / 19
Регистрация: 18.03.2014
Сообщений: 148
22.10.2015, 14:32  [ТС] 3
если я правильно понял, записи вида:
Кликните здесь для просмотра всего текста
Цитата Сообщение от Mikl___ Посмотреть сообщение
Итак, первый способ очевиден.
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
cmp dword ptr Msg,WM_TIMER 
    je @@WM_TIMER
cmp dword ptr Msg,WM_PAINT
    je @@WM_PAINT
cmp dword ptr Msg,WM_CREATE
    je @@WM_CREATE
cmp dword ptr Msg,WM_DESTROY
    je @@WM_DESTROY
leave
    jmp DefWindowProcA; обработка по умолчанию 
@@WM_DESTROY: 
. . .
@@WM_CREATE:
. . .
Кликните здесь для просмотра всего текста
Цитата Сообщение от Mikl___ Посмотреть сообщение
Assembler
1
2
3
4
5
6
7
8
IRP CASE,<WM_TIMER,WM_PAINT,WM_CREATE,WM_DESTROY>;;последовательность вариантов
cmp dword ptr Msg, CASE;;вариант найден?
je @@&CASE
ENDM;;закончить макроописание
leave       ; обработка по умолчанию
    jmp DefWindowProcA  
@@WM_DESTROY: 
. . .

по факту равносильны (во втором случае вместо CASE в проверку будут последовательно подставляться сообщения v[1]-v[n]), верно?

И еще, никогда не знал, Что можно делать так. Обычно я заранее изменял значение переменной, а потом возвращал ее значение назад
Цитата Сообщение от Mikl___ Посмотреть сообщение
Assembler
1
mov ecx,n+1  ;ecx=5
P.S. благодарю за исчерпывающий ответ
0
3406 / 1825 / 489
Регистрация: 28.02.2015
Сообщений: 3,696
22.10.2015, 18:33 4
Цитата Сообщение от bogdan_017 Посмотреть сообщение
по факту равносильны
Да.
Цитата Сообщение от bogdan_017 Посмотреть сообщение
во втором случае вместо CASE в проверку будут последовательно подставляться сообщения
Это макрос. Будет сгенерирован код первого варианта.
1
22.10.2015, 18:33
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
22.10.2015, 18:33
Помогаю со студенческими работами здесь

Каков аналог Switch case из C#
В c# я использовал элемент tree view, где в after_select использовал конструкцию switch-case....

Нужен аналог функции Switch из VB6
Существует ли ресурс, где описаны изменения написания функций в VB.NET по сравнению с VB6? Для...

Условный оператор в Excel (аналог switch в php)
Добрый день. Потребовалось создать чуть более сложный документ на Excel 2010. Подскажите,...

Как сделать чтобы Switch работал в другом Switch'e?
Как сделать что бы Switch работал в другом Switch'e? Вот допустим выбираем один пункт и в...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
4
Ответ Создать тему
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2024, CyberForum.ru