Пример: Написать программу на ассемблер. Задан массив А из N = 40 элементов. Навести алгоритм и программу определения количества элементов массива А, которые удовлетворяют условию L >= Ai >= M, где L = 6 и M = 22.
Текст программы
файл 1.asm
.386
.model flat,stdcall
option casemap:none ; отличие строчных и прописных букв
ExitProcess proto :DWORD
Mas_sum proto :DWORD, :DWORD, :DWORD ; прототип процедуры
.code
Mas_sum proc arg1:DWORD,arg2:DWORD,masiv:DWORD
mov ebx,0
mov eax,masiv
cmp eax,arg1
jnc m1
jmp _end
m1:
cmp eax,arg2
jc m2
jmp _end
m2:
inc ebx
_end:
ret ; возвращение управления ОС
Mas_sum endp ; окончание процедуры с именем Mas_sum
end ; окончание программы с именем start
end start ; директива окончания программы с именем start
.386
.model flat,stdcall
option casemap:none ; отличие строчных и прописных букв
MASM 32-bit Windows Assembler
include masm32includewindows.inc
include masm32includekernel32.inc
include masm32includefpu.inc
include masm32includeuser32.inc
include masm32includemsvcrt.inc
includelib masm32libuser32.lib
includelib masm32libkernel32.lib
includelib masm32libmsvcrt.lib
includelib masm32libfpu.lib
includelib 1.lib
ExitProcess proto :DWORD
Mas_sum proto :DWORD, :DWORD, :DWORD ; прототип процедуры
.data ; директива определения данные
_c dd 40
sum dd 0
op1 dd 6 ; запись в 32-разрядную память op1
op2 dd 22 ; минимальных предел
frmt db «%d»,0
buf db 30 dup(?)
stdout DWORD ?
stdin DWORD ?
cRead dd ?
temp dd ?
mas1 dd 40 dup(0)
st1 db «Vvesty masiv: »
st2 db «Вывод количества элементов в пределах (6,22) массива! А, 0
st3 db 10 dup(0)
ifmt db «количество = %d»,0
.code ; директива начала кода программы
_start:
lea esi, mas1 ; загрузка адреса начала массива
mov ecx,_c
m1:
mov ebx,ecx
invoke GetStdHandle,STD_OUTPUT_HANDLE
mov stdout,eax
invoke GetStdHandle,STD_INPUT_HANDLE
mov stdin,eax
invoke WriteConsoleA,stdout,ADDR st1,14,NULL,NULL ; VIVOD ST1
invoke ReadConsole,stdin,ADDR buf,20
ADDR cRead,NULL ; чтения числа как символ
invoke crt_atoi,ADDR buf ; преобразовать символ в число
mov [esi],eax
add esi,4
mov ecx,ebx
loop m1
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
mov ecx,_c
lea esi, mas1 ; загрузка адреса начала массива
mov eax [esi] ; загрузка числа
m3:
invoke Mas_sum, op1,op2,eax
add sum,ebx
add esi,4 ; расчет адреса нового числа
mov eax[esi]
loop m3
invoke wsprintf
ADDR st3
ADDR ifmt
ebx
invoke MessageBox
NULL
addr st3
addr st2
MB_OK
#6 Арифметические команды сложения ADD и ADC
invoke ExitProcess,0
ret
end _start ; конец программы
Результат работы программы:
Источник: movaxbx.ru
Пример выполнения программы в среде разработки RadASM ассемблер Masm32
Цель лабораторной работы – изучение специальных подпрограмм библиотеки MASM32.lib организации ввода-вывода для консольного режима, изучение команд обработки строк для организации циклической обработки.
Предмет исследования
§ Структура программы на языке ассемблера.
§ Организация ввода-вывода данных.
§ Программа решения задачи в виде консольного приложения.
Контрольные вопросы
1. Структура программы на ассемблере.
2. Назначение арифметических команд.
3. Назначение команд INC и DEC .
4. Типы форматов хранения чисел.
5. Назначение команд ADD и SUB.
6. Вспомогательные команды для арифметических вычислений.
Задание к работе
Создать консольную программу, которая определяет число вызовов таймера. В программе должны быть:
§ строка с сообщением «число вызовов таймера»
§ возможность задания координат и цвета сообщения.
Цвет задаем, используя строки FOREGROUND_BLUE equ 1h ; синий цвет букв и далее. Чтобы сменить координаты выводимого сообщения, надо поменять числа в строках MOV CRD.X,10 и MOV CRD.Y,15.
Таблица 1 — Варианты к заданию:
№ | Координаты сообщения | Цвет сообщения и фона |
X=5, Y=10 | 2h+8h | |
X=15, Y=15 | 1h+8h | |
X=10, Y=20 | 4h+8h | |
X=5, Y=18 | 2h+80h | |
X=19, Y=30 | 2h+40h | |
X=12, Y=16 | 4h+10h | |
X=35, Y=40 | 1h+40h | |
X=0, Y=30 | 1h+80h | |
X=28, Y=15 | 4h+20h | |
X=17, Y=50 | 1h+20h |
Пример выполнения программы в среде разработки RadASM ассемблер Masm32
Среда разработки RadASM представляет собой среду разработки программного обеспечения для ОС Windows, изначально предназначенную для написания программ на языке ассемблера. Имеет гибкую систему файлов настроек, благодаря чему может быть использована как среда разработки программного обеспечения на высокоуровневых языках, а также документов, основанных на языках разметки.
Проект представляет собой набор файлов. В файле «Имя_проекта.rap» (расширение – rap) содержится информация о настройках проекта; файлах, входящих в проект. Помимо этого файла, проекта так же содержит файл «Имя_проекта.asm» (расширение – asm) – файл, содержащий основную часть программы. Так же могут входить дополнительные файлы.
Главное окно программ при запуске выглядит следующим образом (рисунок 1).
Окно программы содержит несколько областей: панель меню; панель инструментов; рабочую область; обозреватель проектов; свойство проекта и так далее, которые можно настроить при необходимости.
Для создания нового проекта необходимо зайти в меню «Файл» и выбрать пункт «Новый проект» (рисунок 2).
Рисунок 2 – Окно пункта меню «Файл»
В выпадающем списке «Ассемблер» необходимо выбрать строку «masm» (рисунок 3). Тип проекта – «Console App». Ввести имя проекта. Имя проекта должно содержать ОДНО СЛОВО БЕЗ ПРОБЕЛОВ. При необходимости ввести описание.
По умолчанию проекты сохраняются в папке «radasmMasmProjects», где radasm – имя папки, содержащий среду. Необходимости указать местоположение проекта на диске D:\. Нажать кнопку «Next >».
Рисунок 3 – Выбор типа проекта
Далее необходимо выбрать базовый шаблон проекта (рисунок 4). Шаблоны находятся в папке «radasmMasmTemplates». В шаблоне уже прописан базовый код, общий для всех проектов и необходимые настройки компилятора и отладчика. После создания проекта, в редакторе будет загружена программа (рисунок 5). В данной лабораторной работе не используется шаблон (Нажимаем «Next >».)
Рисунок 4 – Выбор шаблона проекта
Предпоследнее окно Мастера предлагает выбрать типы создаваемых файлов – выбираем Asm (исходные файлы ассемблера), и папки – выбираем папку Bak, используемую для размещения предыдущих копий файлов (рисунок 5).
Рисунок 5 – Выбор типов файлов и папок
Последнее окно Мастера определяет доступные для работы с проектом пункты меню запуска приложения Создать и выполняемые команды. В данной сборке выполняемые команды уже настроены, поэтому в нем можно ничего не менять, хотя использоваться будут не все пункты созданного меню, а только следующие: Assemble (транслировать или, точнее, ассемблировать), Link (компоновать), Run (выполнить) и Run w/Debug (выполнить в подключенном отладчике).
Рисунок 6 – Пункты меню запуска приложения
В результате будет получена пустая заготовка консольного приложения Windows. Просмотреть эту заготовку можно, дважды щелкнув левой клавишей мыши по файлу *.asm в окне навигатора Project, расположенном справа вверху.
Создать консольную программу, которая выводит на экран «Число вызовов таймера». Листинг программы находится в разделе приложения (Приложение 3)
Опишем кратко данную программу. В ней задаем тип процессора, модель памяти, константы.
Плоская модель памяти.
.MODEL FLAT, STDCALL
STD_OUTPUT_HANDLE equ -11
STD_INPUT_HANDLE equ -10
TIME_PERIODIC equ 1
MOUSE_EV equ 2h
Задаются атрибуты выводимого сообщения — цвет текста
FOREGROUND_BLUE equ 1h ;Синий цвет букв.
FOREGROUND_INTENSITY equ 8h ;Повышенная интенсивность
BACKGROUND_BLUE equ 10h ;Синий цвет фона.
BACKGROUND_INTENSITY equ 80h ;Повышенная интенсивность
COL1 = 4h+8h ; Изменение цвета текста.
Описываем прототипы внешних процедур в программе.
Директивы подключения библиотек
Задаем координаты структуры
Сегмент данных, в нем описываем выводимые на экран сообщения, тип данных, количество символов, координаты сообщения.
_DATA SEGMENT DWORD PUBLIC USE32 ‘DATA’
STR2 DB «Пример таймера в консольном приложении»
STR3 DB 100 dup(0)
FORM DB «Numbers of Time Call: %lu»,0
;В сегменте кода создаем новую консоль, заголовок окна
_TEXT SEGMENT DWORD PUBLIC USE32 ‘CODE’
;Создание новой консоли
;Установка имени консоли
PUSH OFFSET STR2
PUSH OFFSET STR2
PUSH OFFSET STR2
;Задаем цветовые атрибуты и устанавливаем таймер
;Задаем процедуру для таймера, точность вызова таймера, его вызов через секунду
PUSH offset TIME
CALL TIME ;Вызов таймера на экран, закрытие таймера и консоли после нажатия Enter
Источник: megapredmet.ru
Ассемблер для начинающих
С этой статьи я начинаю цикл статей «Изучаем Ассемблер». Основная идея состоит в том, что я знаю ассемблер не лучше вас и я как бы изучаю ассемблер вместе с вами. Предполагается, что вы уже хотя бы немного знакомы с любым языком программирования. Итак, начнем!
Основы
Какой компилятор выбрать? Компиляторов ассемблера существует очень много. Самые популярные из них: MASM, TASM, FASM, NASM. Большинство примеров будет на MASM’е, но большинство компиляторов мало чем отличаются по синтаксису. Скачать MASM можно на http://www.masm32.cjb.net/. Вместе с MASM поставляется редактор для него.
Перед тем, как компилировать программу нужно ее сохранить(File->Save). Теперь можно откомпилировать(Project->Build All). Чтобы запустить вашу программу нажмите Project->Run Program.
Теперь о структуре программы. Вот «скелет» стандартной программы:
.386 .model flat, stdcall option casemap :none ;подключение библиотек,необходимых нам include MASM32INCLUDEwindows.inc include MASM32INCLUDEmasm32.inc include MASM32INCLUDEgdi32.inc include MASM32INCLUDEuser32.inc include MASM32INCLUDEkernel32.inc includelib MASM32LIBmasm32.lib includelib MASM32LIBgdi32.lib includelib MASM32LIBuser32.lib includelib MASM32LIBkernel32.lib ;раздел, где объявляются все константы .const ;раздел, где объявляются переменные, уже имеющие какое-то значение .data ;раздел, где объявляются переменные, еще не имеющие значения .data? .code start: ;с этого слова начинается код программы invoke ExitProcess,0 end start ;с этого слова заканчивается код программы
Советую сохранить этот «скелет» в отдельном файле для удобства. В большинстве наших программ мы будем использовать функции WinAPI. Давайте напишем программу, которая будет выводить всплывающее окно с каким-либо сообщением. Для вывода этого окна мы будем пользоваться функцией MessageBox:
.386 .model flat, stdcall option casemap :none include MASM32INCLUDEwindows.inc include MASM32INCLUDEmasm32.inc include MASM32INCLUDEgdi32.inc include MASM32INCLUDEuser32.inc include MASM32INCLUDEkernel32.inc includelib MASM32LIBmasm32.lib includelib MASM32LIBgdi32.lib includelib MASM32LIBuser32.lib includelib MASM32LIBkernel32.lib .data message db «Sources.RU Magazine»,0 ;переменная с сообщением mestitle db » Sources.RU Magazine «,0 ;переменная с заголовком .code start: invoke MessageBox,0,ADDR message,ADDR mestitle,MB_OK ;выводим сообщение invoke ExitProcess,0 ;завершаем программу end start
Как можно увидеть в примере, все API-функции вызываются с помощью команды invoke. Сначала нужно написать имя функции, а потом все ее параметры, то есть:
invoke имя_функции, [параметр1, параметр2, параметр3, … …]
У многих сразу возникнет вопрос: где взять справочник по API? Я отвечу: а искать не пробовали? Но если все-таки не нашли, то я пользуюсь API-Guide (http://www.allapi.net/). Это справочник по API для VB, но он подходит и для ассемблера.
А вообще сети их полно, просто нужно хорошо поискать.
Математика
На любом языке программирования высокого уровня, чтобы сложить два числа нужно сделать что-то вроде этого:
В ассемблере все по-другому. Чтобы сложить, вычесть, умножить или разделить два числа он использует не переменные, а регистры процессора. Регистр процессора – это как бы переменные, которые находятся в процессоре. Вот таблица с основными регистрами:
EAX используется для сохранения числа и для счета
ECX используется для счета
EDX сохраняет остаток от деления
EBX может быть использован для всего
ESI используется для управления строками
EDI также используется для управления строками
И таблица с основными операторами, которые используются для счета:
MOV EAX,число1 копирует число1 в регистр EAX
ADD EAX,число1 прибавляет к содержимому EAX число1
SUB EAX,число1 отнимает от содержимого EAX число1
MUL регистр1 используется для умножения, умножает содержимое регистр1 на число, которое всегда хранится в EAX и сохраняет результат в EAX
DIV регистр1 Делит содержимое EAX на содержимое регистр1 и сохраняет результат в EAX
Вообще все арифметические операции в ассемблере производятся между регистром и аккумулятором, то есть регистром EAX. В операторах MUL и DIV только один параметр, потому что ассемблер сразу знает, что второе число хранится в EAX. Но почему же в операторах SUB и ADD два параметра, а не один? Об этом мы поговорим позже после того, как рассмотрим пример в конце статьи.
Теперь немного подробнее об арифметике. В регистре EDX всегда хранится остаток от деления и вы должны запомнить, что перед делением нужно обязательно очистить этот регистр. Это можно сделать так:
MOV EDX,0
XOR EDX,EDX
Между этими двумя способами нет никакой разницы, но второй способ быстрее. Привожу пример программы, вычисляющей число 7*30-40*2:
; в программе есть то, о чего я еще не объяснял, но я объясню вам это позже .386 .model flat, stdcall option casemap :none include MASM32INCLUDEwindows.inc include MASM32INCLUDEmasm32.inc include MASM32INCLUDEgdi32.inc include MASM32INCLUDEuser32.inc include MASM32INCLUDEkernel32.inc includelib MASM32LIBmasm32.lib includelib MASM32LIBgdi32.lib includelib MASM32LIBuser32.lib includelib MASM32LIBkernel32.lib wsprintfA PROTO C :DWORD,:VARARG wsprintf equ .data bufferforstring db 10 dup(0) titlestring db «Результат»,0 szformat db «%u»,0 .code start: mov eax,7 ;заносим в EAX 5 mov ecx,30 ;заносим в ECX 25 mul ecx ;умножаем EAX на ECX mov ebx,eax ;заносим содержимое EAX в EBX mov eax,40 ;заносим в EAX 40 mov ecx, 2 ;заносим в ECX 2 mul ecx ;заносим в ECX 2 sub ebx,eax ;отнимаем от EBX EAX invoke wsprintf,addr bufferforstring,addr szformat,eax invoke MessageBox,0,ADDR bufferforstring,ADDR titlestring,MB_OK ;выводим резульат invoke ExitProcess,0 end start
Помните, как я обещал объяснить, почему в mul и div один параметр, а в sub и add их два?
На самом деле ответ прост. Вспомните правила арифметики. Ведь умножение и деление должно производится раньше, чем сложение и вычитание! То есть, чтобы решить наше выражение(7*30-40*2), нужно где-то хранить результат 7*30 и 40*2, EAX мы использовать не можем, так как он используется в арифметических операциях. Поэтому приходится хранить результат 7*30 в EBX.
Конечно, в нашем случае в операторе sub все равно используется EAX. Но подумайте, что было бы, если бы у нас было в выражении не одна операция вычитания или сложения, а несколько?
Источник: sources.ru