Хотелось бы рассмотреть что-то интересное и полезное вплане использования, поэтому выбор пал на ассемблер, а именно на создание примитивной графики.
Язык ассемблер — это низкоуровневый язык программирования или же программа, которая исходный текст программы, написанный на языке ассемблера, переводит в программу на машинный язык. Язык, по некоторым меркам сложный, но ведь создание примитивов графики берет начало именно тут. Я же хочу рассмотреть ассемблер под Windows, а именно MASM, который, на ряду с Visual Studio, не так давно использовал для создания графических примитивов. Об этом с иллюстрациями и подробностями далее.
Приступая к работе
Рассмотрим маломальски простенькую структуру, которая необходима для создания приложений под Windows:
1) помещаем все константы, стpуктуpы и функции, относящиеся к Windows в начале нашего .asm файла — экономим силы и время;
2) используем диpективу includelib, чтобы указать библиотеки импоpта — это укажет компилятоpу на то, что пpогpамма будет использовать функции из этих библиотек импоpта;
Как написать программу на ассемблере 64 бит
3) объявляйте пpототипы API-функций, стpуктуp и/или констант в подключаемом файле с использованием тех же имен, что и в Windows include файлах, по крайней мере старайтесь, поскольку это избавит всех от головной боли в будующем;
4) используйте makefile, чтобы автоматизиpовать пpоцесс компиляции.
Я же отступлю кое-где и кое-как, но в целом у нас должна получиться отличная программа, которая нарисует нам довольно интересный таки примитив. Рассмотрим пример структуры программы на Ассемблере (см. Листинг 1)
Листинг 1. Пример структуры программы
.type_process ; описание типа процессора
.model ; описание модели памяти
include lib ; подключение inc
includelib lib ; подключение lib
.DATA ; иницилизиpуемые данные
; имя класса и окна
.DATA? ; неиницилизиpуемые данные
; дескриптор пpогpаммы
.CODE ; здесь начинается код программы
Определение графических примитивов
Контекст Устройства и WM_PAINT
В Windows окно само отвечает за перерисовку себя. Для того чтобы окно осуществило перерисовку, оно должно получить сообщение WM_PAINT.
Обычно используют один из трех методов:
а) рабочая область может быть восстановлена, если ее содержимое формируется с помощью каких-либо вычислений;
б) последовательность событий, формирующих рабочую область, может быть сохранена, а затем «проиграна» сколь угодно раз;
в) можно создавать виртуальное окно и направлять весь вывод в виртуальное окно, а при получении основным окном сообщения WM_PAINT копировать содержимое виртуального окна в основное (будет использовано для демонстрации написанного позже приложения).
Установка текущей позиции
Для установки текущей позиции используется функция MoveToEx(), где функция описывается следующим образом:
WINGDIAPI BOOL WINAPI MoveToEx(HDC, int, int, LPPOINT);
Первый аргумент — это контекст устройства, второй и третий — координаты точки, в которую устанавливается текущая графическая позиция. Последний аргумент — указатель на структуру типа POINT, в которую функция запишет координаты старой текущей позиции.
ВИРУС на Ассемблере | Как программно отключить монитор?
Рисование линии
Для прорисовки линии используется функцию LineTo(), где функция описывается следующим образом:
WINGDIAPI BOOL WINAPI LineTo(HDC, int, int);
Первый аргумент — контекст устройства, второй и третий аргументы — координаты точек.
Рисование прямоугольника
Для прорисовки прямоугольника используется функция Rectangle(), где функция описывается следующим способом:
WINGDIAPI BOOL WINAPI Rectangle(HDC, int, int, int, int);
Первый аргумент — это контекст устройства, все же остальные аргументы — координаты верхнего левого и нижнего правого углов прямоугольника.
Рисование эллипса
Для прорисовки эллипса необходимо вызвать функцию Ellipse(), где функция описывается следующим образом:
WINGDIAPI BOOL WINAPI Ellipse(HDC, int, int, int, int);
Первый аргумент — это контекст устройства.
Примечание: эллипс ограничен прямоугольником и именно через координаты этого прямоугольника и определяется прорисовываемый эллипс. Второй и третий аргументы — координаты левого верхнего угла прямоугольника, четвертый и пятый аргументы — координаты нижнего правого угла.
Рисование прямоугольника с закругленными краями
Для прорисовки прямоугольника с закругленными краями используется функция RoundRect(), где функция описывается следующим образом:
WINGDIAPI BOOL WINAPI RoundRect(HDC, int, int, int, int, int, int);
Первые пять аргументов полностью идентичны аргументам функции Rectangle(). Последние два аргумента содержат ширину и высоту эллипса, определяющего дуги.
Написание и разбор .asm кода
Для написания примитива рассмотрим шаги, которые необходимы для создания и отрисовки графики:
1) получение дескриптора для программы;
2) регистрация класса окна;
3) создание окна;
4) отображение окна на экpане;
5) обновление содержимого экpана в окне;
6) выход из пpогpаммы.
Приступим к созданию, но для начала создадим новый проект в Visual Studio: File -> New Project
Выбираем пустой прокт: Empty project
Создаем новый файл: правой кнопкой по Source -> Add -> New Item
Создаем новый файл (.asm):
1-ый способ — дописать при создании нового файла file.asm (я таким способом создавал)
2-ой способ — изменить расширение файлу после его создания (file.txt -> rename -> file.asm)
Используем masm в Visual Studio: нажимаем правой кнопкой по преокту -> Build Customization
Задаем этот самый masm: ставим галочку напротив masm
Приступаем к написанию этого самого примитива, а сам листинг смотрите ниже.
Листинг 2. Написание кода на ассемблере
.386
.model stdcall, flat
option casemap:none
includelib kernel32.lib
include kernel32.inc
includelib user32.lib
include user32.inc
include windows.inc
include gdi32.inc
hwnd dd 0
hInst dd 0
szTitleName db ‘АиПОС. Лабороторная работа №6’, 0
szClassName db ‘Приложение Win32’, 0
msg MONMSGSTRUCT
wc WNDCLASS
ps PAINTSTRUCT
Main PROC
invoke GetModuleHandle, 0 ;получение значения баз. адреса,
mov hInst, eax ;по которому загружен модуль.
mov wc.style, CS_HREDRAW + CS_VREDRAW + CS_GLOBALCLASS
mov wc.lpfnWndProc, offset WndProc ;адрес оконной процедуры
mov wc.cbClsExtra, 0
mov wc.cbWndExtra, 0
mov eax, hInst ;дескриптор приложения
mov wc.hInstance, eax ;в поле hInstance
invoke LoadIcon, 0, IDI_APPLICATION
mov wc.hIcon, eax ;дескриптор значка в поле hIcon
invoke LoadCursorA, 0, IDC_ARROW
mov wc.hCursor, eax ;дескриптор курсора в поле hCursor
mov wc.hbrBackground, WHITE_BRUSH ;цвет бекграунда окна белый
mov dword ptr wc.lpszMenuName, 0 ;главного меню нет
mov dword ptr wc.lpszClassName, offset szClassName ;имя класса окна
invoke RegisterClassA, offset wc ;регистрация класас окна
invoke CreateWindowEx, 0, offset szClassName, offset szTitleName,
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT, 0, 0, hInst, 0
mov hwnd, eax ;создание окна
invoke ShowWindow, hwnd, SW_SHOWNORMAL ;показ окна
invoke UpdateWindow, hwnd ;перерисовывка содержимого окна
cycle1: ;цикл сообщений
invoke GetMessage, offset msg, 0, 0, 0
cmp ax, 0
je end_c
invoke TranslateMessage, offset msg ;трансляция ввода с клавиатуры
invoke DispatchMessage, offset msg ;отправляем сообщение
;оконной процедуре
jmp cycle1
end_c:
invoke ExitProcess, 0 ;выход из приложения
Main ENDP
WndProc PROC USES ebx edi esi, _hwnd:DWORD, _wmsg:DWORD, _wparam:DWORD, _lparam:DWORD
local _hdc:DWORD
cmp _wmsg, WM_DESTROY
je wmdestroy
cmp _wmsg, WM_PAINT
je wmpaint
invoke DefWindowProcA, _hwnd, _wmsg, _wparam, _lparam ;обраб. по умолчанию
jmp exit_proc
wmpaint:
invoke BeginPaint, _hwnd, offset ps ;получаем контекст устройства
mov _hdc, eax
invoke Rectangle, _hdc, 170, 120, 310, 260 ;тело
invoke Rectangle, _hdc, 120, 120, 170, 140 ;левая лапа
invoke Rectangle, _hdc, 310, 120, 360, 140 ;правая лапа
invoke Rectangle, _hdc, 170, 260, 190, 310 ;левая ноголапа
invoke Rectangle, _hdc, 290, 260, 310, 310 ;правая ноголапа
invoke Rectangle, _hdc, 210, 80, 270, 120 ;башка
invoke Rectangle, _hdc, 220, 85, 225, 90 ;левый глаз
invoke Rectangle, _hdc, 250, 85, 255, 90 ;правый глаз
invoke Rectangle, _hdc, 225, 105, 255, 120 ;рот
invoke EndPaint, _hdc, offset ps ;освобождаем контекст
mov eax, 0 ;возвращаемое значение — 0
jmp exit_proc
wmdestroy:
invoke PostQuitMessage, 0 ;послать сообщение WM_QUIT
mov eax, 0 ;возвращаемое значение — 0
exit_proc:
ret
WndProc ENDP
END Main
Результат
По ходу действий и написания кода проставлял комментарии, но чтобы понять полную суть, рассмотрю подробнее все, что сделал и написал.
Разбор полётов
Строка с .386 передает MASM, что используется набор инструкций пpоцессоpа 80386. Строка .model stdcall, flat передает MASM, что будет использоваться плоская модель памяти. А саму передачу паpаметpов использовали типом STDCALL как по умолчанию.
Подключил windows.inc в начале кода, поскольку он содеpжит системный стpуктуpы и константы, котоpые потpебовались для реализации примитивов в пpогpамме. Поскольку пpогpамма вызывает API функции Windows, которые находятся в user32.dll (CreateWindowEx и другие) и kernel32.dll (ExitPocess и другие) их необходимо тоже прописать.
Описываем прототип главной функции PROC.
Следом идёт .data, где: szClassName — имя нашего класса окна и szTitleName — имя нашего окна.
В .code содеpжит все инстpукции, где код должен pасполагаться между и end .
Пеpвая же инстpукция — вызов GetModuleHandle, чтобы получить дескриптор нашей пpогpаммы. Она используется как паpаметp, пеpедаваемый функциям API, которые вызываются нашей пpогpаммой.
Далее идет инициализация класса окна — оно опpеделяет некотоpые важные хаpактеpистики окна, такие как иконка, куpсоp, функцию, ответственную за окно и так далее. Тут же и описываем дескриптор самого приложения, дескриптор значка и дескриптор курсора. Дескриптора меню в реализованном приложении нет, поскольку это увеличило бы код программы, а функциональности ему не добавило, тем более, что это примитив и он тут вовсе не нужен. Параметры, которые могут или были использованы для создания окна:
1) cbSize: задает размеp общей стpуктуpы WDNCLASSEX в байтах;
2) style: задает стиль окона;
3) cbClsExtra: задается количество дополнительных байтов, котоpые нужно будет зарезервировать для самой программы;
4) hInstance: задает дескриптор модуля;
5) hIcon: задает дескриптор иконки, а его получение просходит посредством обращения функции LoadIcon;
6) hCursor: задает дескриптор куpсоpа, а его получение просходит посредством обращения функции LoadCursor;
7) hbrBackground: задает цвет фона;
8) lpszMenuName: задается дескриптор меню для окон;
9) lpszClassName: задается имя класса окна.
После pегистpации класса окна функцией RegisterClassEx, происходит вызов CreateWindowEx, чтобы создать наше окно, основанное на этом класе.
Основной и немаловажной является процедура WndProc PROC USES ebx edi esi, _hwnd:DWORD, _wmsg:DWORD, _wparam:DWORD, _lparam:DWORD.Не обязательно ее было называть ее WndProc, где пеpвый паpаметp, _hwnd — это хэндл окна, котоpому пpедназначается сообщение,_wmsg — передаваемое сообщение. Стоит сказать, что _wmsg — это не msg стpуктуpа, но это всего лишь число. _wparam и _lparam — это дополнительные паpаметpы, которые используются некоторыми сообщениями.
В конце концов подошли к заключительной части, где и описываются задаваемые фигуры, их координаты и возвращаемые значения. Это ключевая часть, поскольку именно здесь pасполагается логика действий пpогpаммы. Тут же описываем освобождение контекста и возравщаем значения, где далее посылаем сообщение о завершении.
Единственное сообщение, которое осталось обработать — wmdestroy — это сообщение будет посылаться окну, когда оно закpывается. В то вpемя, когда пpоцедуpа окна его получает, окно уже исчезло с экpана. После выполнения wmdestroy вызывается PostQuitMessage, котоpый посылает сообщение о выходе и это вынуждает GetMessage веpнуть нулевое значение в eax, а это уже выход из программы.
Источник: habr.com
Создание объектного модуля (трансляция программы)
На этой лекции мы познакомимся со специальными программными средствами, предназначенными для преобразования исходных текстов на ассемблере к виду, приемлемому для выполнения на компьютере, и научимся использовать их.
Но прежде чем обсуждать сами инструментальные средства разработки программ, представляется необходимым уделить внимание общим методологическим принципам разработки программного обеспечения. Если вы — начинающий программист, то у вас наверняка очень большой интерес к практической работе и, возможно, разработку программы вы производите на чисто интуитивном уровне.
До определенного момента здесь нет ничего страшного; это даже естественно. Но совсем не задумываться над тем, как правильно организовать разработку программы (не обязательно на ассемблере), нельзя, так как хаотичность и ставка только на интуицию в конечном итоге станут стилем программирования. А это может привести к тому, что рано или поздно за вами закрепится слава программиста, у которого программы работают «почти всегда» со всеми вытекающими отсюда последствиями для вашей карьеры. Поэтому нужно помнить одно золотое правило: надежность программы достигается, в первую очередь, благодаря ее правильному проектированию, а не бесконечному тестированию.
Это правило означает, что если программа правильно разработана в отношении как структур данных, так и структур управления, то это в определенной степени гарантирует правильность ее функционирования. При применении такого стиля программирования ошибки являются легко локализуемыми и устранимыми.
О том, как правильно организовать разработку программ (независимо от языка), написана не одна сотня книг. Большинство авторов предлагают следующий процесс разработки программы (мы адаптируем его, где это необходимо, к особенностям ассемблера):
1. Этап постановки и формулировки задачи:
§ изучение предметной области и сбор материала в проблемно-ориентированном контексте;
§ определение назначения программы, выработка требований к ней и представление требований, если возможно, в формализованном виде;
§ формулирование требований к представлению исходных данных и выходных результатов;
§ определение структур входных и выходных данных;
§ формирование ограничений и допущений на исходные и выходные данные.
2. Этап проектирования:
§ формирование «ассемблерной» модели задачи;
§ выбор метода реализации задачи;
§ разработка алгоритма реализации задачи;
§ разработка структуры программы в соответствии с выбранной моделью памяти.
3. Этап кодирования:
§ уточнение структуры входных и выходных данных и определение ассемблерного формата их представления;
§ комментирование текста программы и составление предварительного описания программы.
4. Этап отладки и тестирования:
§ составление тестов для проверки правильности работы программы;
§ обнаружение, локализация и устранение ошибок в программе, выявленных в тестах;
§ корректировка кода программы и ее описания.
5. Этап эксплуатации и сопровождения:
§ настройка программы на конкретные условия использования;
§ обучение пользователей работе с программой;
§ организация сбора сведений о сбоях в работе программы, ошибках в выходных данных, пожеланиях по улучшению интерфейса и удобства работы с программой;
§ модификация программы с целью устранения выявленных ошибок и, при необходимости, изменения ее функциональных возможностей.
К порядку применения и полноте выполнения перечисленных этапов нужно подходить разумно. Многое определяется особенностями конкретной задачи, ее назначением, объемом кода и обрабатываемых данных, другими характеристиками задачи. Некоторые из этих этапов могут либо выполняться одновременно с другими этапами, либо вовсе отсутствовать. Главное, чтобы вы, приступая к созданию нового программного продукта, помнили о необходимости его концептуальной целостности и недопустимости анархии в процессе разработки.
Ранее мы обсуждали пример программы на ассемблере. Если посмотреть на описанный выше процесс разработки программы, то можно увидеть, что обсуждение велось нами в полном согласии с этим процессом. Мы подробно обсудили проблему, структуры данных, структуру программного модуля и т. д. Наше обсуждение закончилось на этапе кодирования программы.
Далее, по логике, нужно было ввести программу в компьютер, перевести в машинное представление и выполнить. Как это сделать? Дальнейшее обсуждение будет посвящено именно этому вопросу.
Традиционно у существующих реализаций ассемблера нет интегрированной среды, подобной интегрированным средам Turbo Pascal, Turbo С или Visual C++. Поэтому для выполнения всех функций по вводу кода программы, ее трансляции, редактированию и отладке необходимо использовать отдельные служебные программы. Большая часть их входит в состав специализированных пакетов ассемблера.
На рис. 4.1 приведена общая схема процесса разработки программы на ассемблере на примере рассмотренной ранее программы (см. листинг 3.1). На схеме выделено четыре шага этого процесса. На первом шаге, когда вводится код программы, можно использовать любой текстовый редактор.
Основным требованием к нему является то, чтобы он не вставлял посторонних символов (спецсимволов редактирования). Файл должен иметь расширение. asm.
Рис.4.1. Процесс разработки программы на ассемблере.
Программы, реализующие остальные шаги схемы, входят в состав программного пакета ассемблера. Традиционно на рынке ассемблеров для микропроцессоров фирмы Intel имеется два пакета:
§ «Макроассемблер» MASM фирмы Microsoft.
§ Turbo Assembler TASM фирмы Borland.
У этих пакетов много общего. Пакет макроассемблера фирмы Microsoft (MASM) получил свое название потому, что он позволял программисту задавать макроопределения (или макросы), представляющие собой именованные группы команд. Они обладали тем свойством, что их можно было вставлять в программу в любом месте, указав только имя группы в месте вставки.
Пакет Turbo Assembler (TASM) интересен тем, что имеет два режима работы. Один из этих режимов, называемый MASM, поддерживает все основные возможности макроассемблера MASM. Другой режим, называемый IDEAL, предоставляет более удобный синтаксис написания программ, более эффективное использование памяти при трансляции программы и другие новшества, приближающие компилятор ассемблера к компиляторам языков высокого уровня.
В эти пакеты входят трансляторы, компоновщики, отладчики и другие утилиты для повышения эффективности процесса разработки программ на ассемблере. Воспользуемся тем, что транслятор TASM, работая в режиме MASM, поддерживает почти все возможности транслятора MASM. Для работы вполне достаточно иметь пакет ассемблера фирмы Borland — TASM 3.0 или выше. Обратившись к этому пакету, мы «убьем сразу двух зайцев» — изучим основы и TASM, и MASM. В будущем это позволит вам при необходимости использовать любой из этих пакетов.
Создание объектного модуля (трансляция программы)
Итак, исходный текст программы на ассемблере подготовлен и записан на диск. Следующий шаг — трансляция программы. На этом шаге формируется объектный модуль, который включает в себя представление исходной программы в машинных кодах и некоторую другую информацию, необходимую для отладки и компоновки его с другими модулями. Для получения объектного модуля исходный файл необходимо подвергнуть трансляции при помощи программы tasm.exe из пакета TASM.
Формат командной строки для запуска tasm.exe следующий:
TASM [опции] имя_исходного_файла [,имя_объектного_файла]
На первый взгляд, все очень сложно. Не пугайтесь — если вы вдруг забыли формат командной строки и возможные значения параметров, то получить быструю справку на экране монитора можно, просто запустив tasm.exe без задания каких-либо аргументов. Обратите внимание, что большинство параметров заключено в квадратные скобки.
Это общепринятое соглашение по обозначению параметров, которые могут отсутствовать. Таким образом, обязательным аргументом командной строки является лишь имя_исходного_файла. Этот файл должен находиться на диске и обязательно иметь расширение.asm. За именем исходного файла через запятую могут следовать необязательные аргументы, обозначающие имена объектного файла, файла листинга и файла перекрестных ссылок. Если не задать их, то соответствующие файлы попросту не будут созданы. Если же их нужно создать, то необходимо учитывать следующее:
§ Если имена объектного файла, файла листинга и файла перекрестных ссылок должны совпадать с именем исходного файла (наиболее типичный случай), то нужно просто поставить запятые вместо имен этих файлов:
В результате будут созданы файлы, как показано на рис. 4.1 для шага 2.
§ Если имена объектного файла, файла листинга и файла перекрестных ссылок не должны совпадать с именем исходного файла, то нужно в соответствующем порядке в командной строке указать имена соответствующих файлов, к примеру:
В результате на диске будут созданы файлы
§ Если требуется выборочное создание файлов, то вместо ненужных файлов необходимо подставить параметр nul. Например:
В результате на диске будут созданы файлы
Необязательный аргумент опции позволяет задавать режим работы транслятора TASM. Этих опций достаточно много. Для того чтобы получить представление о них, просмотрите приложение. Некоторые из опций понадобятся нам в ближайшее время, а большинство из них, скорее всего, никогда не будут вами востребованы.
Давайте немного поэкспериментируем с программой tasm.exe. Попутно выясним еще несколько важных моментов. Прежде всего проведем некоторые организационные мероприятия. После инсталляции пакета TASM в каталоге TASMBIN, где находится файл tasm.exe, можно увидеть большое количество файлов.
Можно запустить программу tasm.exe прямо отсюда, но тогда созданные ею файлы объектного кода, листинга и перекрестных ссылок тоже окажутся в этом каталоге. Если вы пишете одну программу, то неудобство не столь заметно, но при работе с несколькими программами очень скоро этот каталог станет похож на свалку.
Чтобы избежать подобной ситуации, рекомендуется выполнить следующие действия:
§ Создать в каталоге TASM подкаталоги TASMWORK и TASM PROGRAM. Каталог PROGRAM будем использовать для хранения отлаженных кодов программ и их исполняемых модулей (файлы с расширением.ехе).
Каталог WORK будем использовать как рабочий; в нем будут находиться необходимые для получения исполняемого модуля файлы из пакета транслятора TASM и файл исходного модуля, с которым мы в данный момент работаем. После того как ошибки в исходном модуле устранены, он вместе со своим исполняемым модулем переписывается в каталог PROGRAM. Из каталога WORK удаляются все ненужные файлы — и он готов для работы со следующим исходным модулем на ассемблере. Таким образом, в каталоге WORK всегда находится рабочая версия программы, а в каталоге PROGRAM — отлаженная версия.
§ Поместить в каталог WORK файлы tasm.exe, tlink.exe и rtm.exe. Если вы что-то забудете туда поместить, программы tasm.exe и tlink.exe выдадут вам сообщение об этом.
§ Поместить файл prg_3_1.asm в каталог WORK.
После всех этих действий можно начинать работу. Перейдем в каталог WORK и запустим на трансляцию программу prgJM.asm командной строкой вида
tasm.exe /zi prg_3_1.
В результате на экране вы получите последовательность строк. Самая первая из них будет информировать вас о номере версии пакета TASM, который использовался для трансляции данной программы. Далее идет строка, содержащая имя транслируемого файла. Если ваша программа содержит ошибки, то транслятор выдаст на экран строки сообщений, начинающиеся словами «Error» и «Warning».
Программа предыдущей лекции (листинг 3.1) синтаксически правильная, но в учебных целях вы можете внести туда какую-нибудь бессмыслицу и посмотреть, что получится. Наличие строки с «Error» говорит о том, что у вас в программе есть недопустимые с точки зрения синтаксиса комбинации символов. Логика работы программы для транслятора не имеет никакого значения. Вы можете написать абсолютную чушь, но если она будет синтаксически правильна, транслятор поспешит вас обрадовать, сообщив, что все хорошо.
Наличие строки «Warning» означает, что конструкция синтаксически правильна, но не соответствует некоторым соглашениям языка и это может послужить источником последующих ошибок. Для устранения ошибок нужно определить место их возникновения и проанализировать ситуацию. Место ошибки легко определяется по значению в скобках в сообщении об ошибке. Это значение является номером ошибочной строки. Запомнив его, вы переходите в файл с исходной программой и по номеру строки находите место ошибки.
Этот способ локализации ошибок имеет недостатки. Во-первых, он не нагляден. Во-вторых, не всегда номера строк в сообщении соответствуют действительным номерам ошибочных строк в исходном файле. Такая ситуация будет наблюдаться, например, в том случае, если вы используете макрокоманды.
При их использовании транслятор вставляет в файл дополнительные строки в соответствии с описанием применяемой макрокоманды, в результате чего получается отличие в нумерации. Исходя из этих соображений, для локализации ошибок лучше использовать информацию из специального, создаваемого транслятором файла листинга. Этот файл имеет расширение.
1st; его имя определяется в соответствии с теми соглашениями, которые мы разобрали выше. Ниже приведен полный формат листинга для программы, содержащей некоторые ошибки. Листинг — очень важный документ, и ему нужно уделить должное внимание.
Листинг 4.1. Пример листинга асемблера
Файл листинга содержит, в частности, код ассемблера исходной программы. Но в листинге приводится расширенная информация об этом коде. Для каждой команды ассемблера указываются ее машинный (объектный) код и смещение в кодовом сегменте. Кроме того, в конце листинга TASM формирует таблицы, которые содержат информацию о метках и сегментах, используемых в программе.
Если есть ошибки или сомнительные участки кода, то TASM включает в конец листинга сообщения о них. Если сравнить их с сообщениями, выводимыми на экран, то видно, что они совпадают. Кроме того, что очень удобно, эти же сообщения включаются в текст листинга непосредственно после ошибочной строки.
Строки в файле листинга имеют следующий формат:
§ — уровень вложенности включаемых файлов или макрокоманд в файле;
§ — номер строки в файле листинга. Эти номера используются для локализации ошибок и формирования таблицы перекрестных ссылок.
Помните, что эти номера могут не соответствовать номерам строк в исходном файле. В добавление к вышесказанному нужно отметить, что ассемблер имеет директиву INCLUDE, которая позволяет включить в данный файл строки другого файла. Нумерация при этом, как и в случае макрокоманд, будет последовательная для строк обоих файлов. Факт вложенности кода одного файла в другой фиксируется увеличением значения на единицу. Это замечание касается и использования макрокоманд;
§ — смещение в байтах текущей команды относительно начала сегмента кода. Это смещение называют также счетчиком адреса. Смещение вычисляет транслятор для адресации в сегменте кода;
§ — машинное представление команды ассемблера, представленной далее в этой строке полем ;
§ — строка кода из исходного файла.
Дальнейшие действия зависят от характера ошибки. По мере накопления опыта ошибки будут происходить, скорее всего, в результате простой описки. Пока же наши действия будут заключаться в выяснении того, насколько правильно написана та или иная синтаксическая конструкция. Исправив несколько первых ошибок, перетранслируйте программу и приступайте к устранению следующих ошибок. Возможно, что этого делать не придется, так как после исправления одной ошибки могут исчезнуть и последующие (так называемые наведенные ошибки).
§ нормальном окончании процесса трансляции можно судить по сообщению TASM, в котором отсутствуют строки с сообщениями об ошибках и предупреждениях.
Изучая внимательно файл листинга, вы, наверное, заметили, что не все строки исходной программы имеют соответствующий . Это обстоятельство обусловлено тем, что исходный файл на ассемблере в общем случае может содержать конструкции следующих типов:
§ команды ассемблера — конструкции, которым соответствуют машинные команды;
§ директивы ассемблера — конструкции, которые не генерируют машинных команд, а являются указаниями транслятору на выполнение некоторых действий или служат для задания режима его работы;
§ макрокоманды — конструкции, которые, будучи представлены одной строкой в исходном файле программы, после обработки транслятором генерируют в объектном модуле последовательность команд, директив или макрокоманд ассемблера.
Формат листинга и его полнота не являются жестко регламентированными. Их можно изменить, задавая в исходном файле программы директивы управления листингом. Для знакомства с ними обратитесь к приложению.
Источник: poisk-ru.ru
Про Ардуино и не только
Порой возникает необходимость в изучении кода, генерируемого компилятором. Это помогает понять, что получается «на выходе» при использовании тех или иных функций, приемов, алгоритмов. Существуют специализированные инструменты — дизассемблеры, преобразующие машинный код в текст на языке ассемблера, но это в основном платные программы. Мне же в большинстве случаев вполне хватает результатов программы avr-objdump, которой и посвящена эта публикация.
avr-objdump — это программа, отображающая различную информацию из объектных файлов. Параметры, с которыми она вызывается, определяют, какая именно информация должна быть отображена. Например, для дизассемблирования объектного файла может быть использована следующая команда:
avr-objdump -d file_name.elf
Кому-то может больше понравиться дизассемблированный код с фрагментами исходного кода, где это возможно. Пример вызова для получения такого результата:
avr-objdump -S file_name.elf
Хорошо, где взять avr-objdump и объектный файл? Первый входит в состав IDE Arduino и расположен в каталоге Arduino_dirhardwaretoolsavrbin, где Arduino_dir — это путь установки IDE Arduino.
Что касается объектных (elf) файлов, то они создаются средой разработки при компиляции проекта и находятся во временной папке скетча. Найти их можно следующим образом:
1. Если у вас еще не включен вывод сообщений при компиляции скетчей, то перейдите в окно настроек IDE Arduino и установите соответствующую опцию:
2. После компиляции скетча в последних строках, выводимых средой разработки, вы увидите примерно следующее:
Выделенный фрагмент — это временный каталог для текущего скетча, в него помещаются все файлы, генерируемые в ходе компиляции, в том числе объектный файл.
Остается перейти в этот каталог и скопировать elf файл в Arduino_dirhardwaretoolsavrbin. Теперь, когда у нас avr-objdump и объектный файл находятся в одном каталоге (конечно, это необязательно, если в командах указывать полный путь к файлам), запускаем командную строку и вводим одну из приведенных ранее команд для дизассемблирования с поправкой на имя файла. В моем случае команда выглядит так:
avr-objdump.exe -S sketch_jun18a.ino.elf
В интерпретаторе командной строки необязательно вводить имя файла целиком: достаточно ввести несколько первых символов, затем нажимать TAB, пока интерпретатор не подставит нужное имя.
Итак, выполняем введенную команду и видим весьма объемный результат ее работы:
Для вывода результата работы avr-objdump в файл воспользуемся перенаправлением вывода:
avr-objdump.exe -S sketch_jun18a.ino.elf > my_file.asm
При выполнении данной команды в каталоге с avr-objdump.exe будет создан файл my_file.asm, в который будет сохранен результат работы команды: