Blog. Just Blog

Быстрый поиск

Введите фрагмент названия статьи для поиска

Проверка баланса скобок на Ассемблере

28.01.2011 | Категория: Образ мышления: Assembler | Автор: ManHunter
Есть такая классическая задача по программированию: проверка правильности расстановки скобок в строке, или иначе проверка баланса скобок. Суть задачи заключается в том, что есть произвольное математическое выражение, записанное в строку, в этом выражении используется три вида скобок "( )", "[ ]" и "{ }", последовательность и вложенность может быть любая. Требуется проверить, все ли скобки закрыты, соответствует ли каждая закрывающая скобка открывающей и нет ли лишних закрывающих скобок. В разных интернетах есть решения на различных языках программирования, я решил добавить свое решение на Ассемблере, тем более, что он идеально подходит для реализации этого алгоритма.

Решение заключается в том, что мы двигаемся по строке с самого начала, перебирая все символы по одному. Если символ является открывающей скобкой любого типа, то мы помещаем его в стек. Если символ является закрывающей скобкой, то берем из стека последнюю сохраненную открывающую скобку и проверяем на соответствие. Если не совпадают - ошибка несоответствия скобок, если стек пустой, то ошибка лишней закрывающей скобки. Если конец строки достигнут, но стек не пустой, то ошибка незакрытых скобок. Как видите, решение целиком построено на работе со стеком, а Ассемблер предоставляет для этого все возможности. Переходим к программированию.
  1. ;------------------------------------------------------------------------
  2. ; Функция проверки правильности расстановки скобок в выражении
  3. ; На входе: указатель на строку ASCIIZ
  4. ; На выходе: EAX - результат проверки выражения
  5. ;    0 - скобки расставлены правильно
  6. ;    1 - ОШИБКА: скобки не совпадают
  7. ;    2 - ОШИБКА: закрывающая скобка без открывающей
  8. ;    3 - ОШИБКА: открывающая скобка без закрывающей
  9. ;------------------------------------------------------------------------
  10. proc    CheckBrackets str:DWORD
  11.         local res:DWORD ; Результат
  12.  
  13.         pusha                   ; Сохранить все регистры
  14.  
  15.         mov     esi,[str]       ; Указатель на строку
  16.         cld
  17.         xor     ecx,ecx         ; Глубина стека
  18.         mov     [res],0         ; Результат проверки
  19.  
  20. .scan_expr:
  21.         lodsb                   ; Прочитать символ из строки
  22.         or      al,al           ; Конец строки достигнут?
  23.         jz      .finish
  24.  
  25.         ; Проверить открывающие скобки
  26.         cmp     al,'['
  27.         je      .push_bracket
  28.         cmp     al,'('
  29.         je      .push_bracket
  30.         cmp     al,'{'
  31.         je      .push_bracket
  32.  
  33.         ; Проверить закрывающие скобки
  34.         cmp     al,']'
  35.         je      .pop_bracket
  36.         cmp     al,')'
  37.         je      .pop_bracket
  38.         cmp     al,'}'
  39.         je      .pop_bracket
  40.  
  41.         jmp     .scan_expr      ; Следующий символ
  42.  
  43.         ; Занести скобку в стек
  44. .push_bracket:
  45.         inc     ecx             ; Увеличить счетчик
  46.         movzx   eax,al          ; Записать скобку в стек
  47.         push    eax
  48.         jmp     .scan_expr      ; Следующий символ
  49.  
  50.         ; Извлечь скобку из стека
  51. .pop_bracket:
  52.         or      ecx,ecx         ; В стеке ничего нет?
  53.         jne     @f
  54.  
  55.         ; ОШИБКА: закрывающая скобка без открывающей
  56.         mov     [res],2
  57.         jmp     .finish
  58. @@:
  59.         dec     ecx             ; Уменьшить счетчик
  60.         pop     ebx             ; Извлечь из стека скобку
  61.  
  62.         ; Проверить закрывающие скобки
  63.         cmp     bl,'['
  64.         jne     @f
  65.  
  66.         cmp     al,']'          ; Скобки совпадают?
  67.         je      .scan_expr      ; Да, следующий символ
  68. @@:
  69.         cmp     bl,'('
  70.         jne     @f
  71.  
  72.         cmp     al,')'          ; Скобки совпадают?
  73.         je      .scan_expr      ; Да, следующий символ
  74. @@:
  75.         cmp     bl,'{'
  76.         jne     @f
  77.  
  78.         cmp     al,'}'          ; Скобки совпадают?
  79.         je      .scan_expr      ; Да, следующий символ
  80. @@:
  81.         ; ОШИБКА: скобки не совпадают
  82.         mov     [res],1
  83.         or      ecx,ecx         ; В стеке пусто?
  84.         jz      .exit           ; Да, просто на выход
  85.         jmp     .clean_stack    ; Надо почистить стек
  86. .finish:
  87.         or      ecx,ecx         ; Все проверили и в стеке пусто?
  88.         jz      .exit           ; Да, скобки расставлены правильно
  89.  
  90.         ; ОШИБКА: открывающая скобка без закрывающей
  91.         mov     [res],3
  92.  
  93.         ; Почистить стек от оставшихся скобок
  94. .clean_stack:
  95.         pop     eax
  96.         loop    .clean_stack
  97. .exit:
  98.         popa                    ; Восстановить все регистры
  99.         mov     eax,[res]
  100.  
  101.         ret
  102. endp
Текст функции проверки подробно прокомментирован, так что в дополнительных пояснениях не нуждается. Тем более, что алгоритм был расписан словами выше. Где это можно использовать? Кроме различных калькуляторов подобные решения можно применять для валидации xml-подобных документов, при проверке синтаксиса каких-либо скриптов и т.п., так что главное уяснить принцип.

Читать статью целиком »
Просмотров: 7616 | Комментариев: 4

Эффект плавного открытия окна

08.12.2010 | Категория: Образ мышления: Assembler | Автор: ManHunter
Разберем еще один красивый эффект для ваших приложений - плавное сворачивание и разворачивание окна. Для этого используются те же функции, что и для создания окон нестандартной формы, так как по сути это такая же работа с прямоугольными регионами, но только в цикле с заданными параметрами. Алгоритм простой: прямоугольный регион окна увеличивается от центра до полного размера или уменьшается со всех сторон до центра. Для этого надо сперва надо вычислить шаг, на который будет увеличиваться или уменьшаться горизонтальная и вертикальная координата.
  1.         ; Обработчик инициализации окна
  2.         ...
  3.         ; Получить размер окна
  4.         invoke  GetClientRect,[hwnddlg],coord
  5.  
  6.         ; Вычислить размеры окна для создания основного региона
  7.         mov     eax,[coord.bottom]
  8.         sub     eax,[coord.top]
  9.         mov     [height],eax
  10.  
  11.         mov     eax,[coord.right]
  12.         sub     eax,[coord.left]
  13.         mov     [width],eax
  14.  
  15.         SPEED   = 4     ; Скорость анимации
  16.  
  17.         ; Вычислить коэффициент соотношения сторон в зависимости от
  18.         ; размеров ширины и высоты окна
  19.         cmp     eax,[height]
  20.         ja      @f
  21.  
  22.         ; Высота/ширина
  23.         xor     edx,edx
  24.         mov     eax,[height]
  25.         mov     ecx,[width]
  26.         div     ecx
  27.         shl     eax,SPEED
  28.         mov     [delta_x],(1 shl SPEED) ; Шаг изменения ширины
  29.         mov     [delta_y],eax           ; Шаг изменения высоты
  30.         jmp     @1
  31. @@:
  32.         ; Ширина/высота
  33.         xor     edx,edx
  34.         mov     eax,[width]
  35.         mov     ecx,[height]
  36.         div     ecx
  37.         shl     eax,SPEED
  38.         mov     [delta_x],eax           ; Шаг изменения ширины
  39.         mov     [delta_y],(1 shl SPEED) ; Шаг изменения высоты
  40. @1:
Поскольку коэффициенты получаются целочисленные, то наиболее красивый эффект будет на окнах квадратной формы или на окнах с кратным соотношением размеров сторон.

Читать статью целиком »
Просмотров: 6863 | Комментариев: 6

Расчет CRC16 на Ассемблере

30.11.2010 | Категория: Образ мышления: Assembler | Автор: ManHunter
CRC (Cyclic Redundancy Code - циклический избыточный код) - алгоритм расчета контрольной суммы для передаваемого сообщения, основанный на полиномиальной арифметике. Основная идея алгоритма CRC состоит в представлении сообщения в виде огромного двоичного числа, делении его на другое фиксированное двоичное число и использовании остатка от этого деления в качестве контрольной суммы. Получив сообщение, приемник должен выполнить аналогичное действие и сравнить полученный результат с принятой контрольной суммой. Сообщение считается достоверным, выполняется это равенство. Классический алгоритм CRC16 часто используется в архиваторах для контроля целостности данных служебных заголовков архивов, также его удобно использовать для сравнения строки с каким-либо значением, когда по соображениям безопасности сравниваемое значение не хранится в открытом виде. Для контроля целостности файлов функцию CRC16 лучше не использовать, так как из-за небольшой длины ее научились подделывать. Чтобы выполнить расчет CRC16 требуется сперва подготовить так называемую таблицу инициализации. В сегменте данных таблица резервируется как 256 слов, по одному word на каждый возможный байт:
  1. ; Сегмент данных
  2. section '.data' data readable writeable
  3.  
  4. ; Таблица инициализации для расчета CRC16
  5. crc16table rw 256
В классическом варианте для расчета CRC16 используется полином 0a001h, на его основе таблица слов заполняется значениями. Для этого используется следующая вспомогательная функция:
  1. ;-----------------------------------------------------------------------
  2. ; Функция создания таблицы инициализации для расчета CRC16
  3. ;-----------------------------------------------------------------------
  4. proc init_CRC16
  5.         push    eax ebx ecx edi
  6.  
  7.         ; Указатель на выделенную под таблицу память
  8.         mov     edi,crc16table
  9.         ; Расчитать значения для всех 256 слов
  10.         xor     edx,edx
  11. CRC16_Polynom:
  12.         mov     eax,edx
  13.         mov     ecx,8
  14. CRC16_NL:
  15.         shr     ax,1
  16.         jae     CRC16_NoXOR
  17.         ; Magic Number!
  18.         xor     ax,0a001h
  19. CRC16_NoXOR:
  20.         loop    CRC16_NL
  21.         ; Записать значение в таблицу полиномов
  22.         stosw
  23.         inc     edx            ; Счетчик +1
  24.         cmp     edx,256        ; Всю таблицу сгенерировали?
  25.         jne     CRC16_Polynom  ; Нет, работаем дальше
  26.  
  27.         ; Восстановить измененные регистры
  28.         pop     edi ecx ebx eax
  29.         ret
  30. endp
Таблица инициализации получается всегда одинаковой (при условии неизменности полинома), так что ее можно даже не раcчитывать, а хранить в виде массива констант. Если требуется таблица инициализации CRC16 отдельно для использования в других проектах или языках программирования, то она приведена ниже. Для некоторых других разновидностей алгоритма CRC16, например, CRC-CCITT или CRC16-IBM, полином будет другим, и, соответственно, таблица также будет другой.

Читать статью целиком »
Просмотров: 11790 | Комментариев: 2

Как запретить Windows переходить в спящий режим

26.10.2010 | Категория: Образ мышления: Assembler | Автор: ManHunter

Как запретить Windows переходить в спящий режим

В некоторых случаях требуется, чтобы на время работы вашего приложения компьютер постоянно оставался в активном состоянии, то есть не включался скринсейвер, не отключался монитор, система не переходила в спящий режим. Для этого надо "убедить" Windows, что за клавиатурой сидит реальный пользователь и проявляет какую-то активность, в этом случае все счетчики времени бездействия будут сбрасываться. Для программной имитации действий пользователя используются две функции: mouse_event для симуляции работы с мышкой мышки и, соответственно, keybd_event для клавиатуры. Также можно использовать более универсальную функцию SendInput, она позволяет симулировать не только мышку и клавиатуру, но и хардварные события. Есть еще более суровые варианты, связанные с ковырянием в реестре, изменением профилей электропитания, но их я рассматривать не буду.

Читать статью целиком »
Просмотров: 13403 | Комментариев: 25

Экранная лупа на Ассемблере

15.10.2010 | Категория: Образ мышления: Assembler | Автор: ManHunter
Алгоритм реализации экранной лупы достаточно простой. Надо получить часть изображения рабочего стола и скопировать его с масштабированием в нужную область вашего приложения. Сделать это можно при помощи функции StretchBlt. Если посмотрите описание, то увидите, что для работы этой функции требуются следующие параметры: размеры результирующей области, размеры исходной области и контексты устройств (окон), в которых находятся области. А поскольку мы сейчас разрабатываем лупу, значит она должна увеличивать, то есть размеры исходного окна должны быть пропорционально меньше результирующего. Коэффициент пропорциональности и есть коэффициент увеличения лупы. При инициализации окна выполним предварительные расчеты:
  1.         ...
  2.         ; Получить контекст окна лупы
  3.         invoke  GetDlgItem,[hwnddlg],ID_ZOOM
  4.         mov     ebx,eax
  5.         invoke  GetDC,eax
  6.         mov     [wDC],eax
  7.  
  8.         ; Получить размеры окна лупы
  9.         invoke  GetClientRect,ebx,coord
  10.         mov     eax,[coord.right]
  11.         sub     eax,[coord.left]
  12.         mov     [dWidth],eax
  13.         mov     eax,[coord.bottom]
  14.         sub     eax,[coord.top]
  15.         mov     [dHeight],eax
  16.  
  17.         ; Получить контекст десктопа
  18.         invoke  GetDesktopWindow
  19.         mov     [hDesktop],eax
  20.         invoke  GetDC,eax
  21.         mov     [dDC],eax
  22.         ...
Как теперь запустить экранную лупу? Здесь есть два варианта: при получении сообщения от мыши и по таймеру. У каждого способа свои достоинства и недостатки. Если обрабатывать сообщения мыши, то требуется инжект вспомогательной dll во все процессы, а экран лупы будет обновляться только при движении мыши. Это существенно снизит нагрузку на систему, но если окно под курсором будет обновляться, а курсор останется неподвижным, то лупа получит только тот кадр, который был при последнем движении курсора. Обновление по таймеру создаст дополнительную нагрузку на систему, но при этом изображение в лупе будет в точности соответствовать текущему состоянию окна под курсором. Оба способа имеют место быть, каждый под свои задачи.

Читать статью целиком »
Просмотров: 6541 | Комментариев: 6

01 ... 64 65 66 67 68 69 70 ... 78
Наверх
Powered by PCL's Speckled Band Engine 0.2 RC3
© ManHunter / PCL, 2008-2026
При использовании материалов ссылка на сайт обязательна
Время генерации: 0.1 сек. / MySQL: 3 (0.0181 сек.) / Память: 4.5 Mb
Наверх