Каталог расширений

Популярные теги

3gp       avi       fb2       jpg       mp3       pdf      

Как создать файл в ассемблере


Первая программа на ассемблере - Hellow World в стиле TASM.

Наша первая программа на ассемблере.

Наша первая программа на ассемблере будет в формате *.COM — как мы уже знаем, исполняемые файлы указанного формата очень крохотные (tiny) по размеру и  состоят из одного сегмента, в котором размещаются код, данные и стек.

Ещё мы знаем, что в указанном формате пишутся резидентные программы, драйверы и вирусы.

Резидентная (TSR-программа, от англ. Terminate and Stay Resident) — это программа, которая после запуска передает управление операционной системе, но сама не завершается, а остаётся в оперативной памяти, реагируя на определённые действия пользователя. Например, при нажатии сочетания горячих клавиш делает снимок экрана.

Код в статьях отображается в удобочитаемой форме: каждая строка имеет свой номер, строки и код подсвечиваются. Чтобы скопировать «чистый исходник», наведите курсор мыши на текст, дождитесь всплывающего меню и нажмите в меню кнопочку «копировать» (изображение двух листочков бумаги с текстом). Чистый код не содержит нумерации строк!

Наша первая программа выведет на экран монитора (консоль) надпись «Hello, World!». Итак, как говорил Юрий Алексеевич, поехали!

Создаём исполняемый файл PRG.COM.

Для достижения нашей цели делаем следующее.

  • Скачиваем с нашего сайта архив (DOS-1.rar) с предустановленными DOSBox и программами. Запускаем DOSBox. Стартует эмулятор MS-DOS и Norton Commander пятой версии.
  • В папке D:\TASM.2_0\TASM\ находим текстовый файл PRG.ASM. Это обычный текстовый файл, который можно создать
    с помощью любого текстового редактора, с расширением ASM вместо TXT.
  • В файл вносим код:

;Строка, после точки с запятой является комментарием ;и не обрабатывается ассемблером ; prg.asm - название файла. .model tiny ; создаём программу типа СОМ .code ; начало сегмента кода org 100h ; начальное значение смещения программы в памяти - 100h start: mov ah,9 ; номер функции DOS - в АН mov dx,offset message ; адрес строки - в DX int 21h ; вызов т.н. "прерывания" - системной функции DOS ret ; завершение СОМ-программы message db "Hello, World!",0Dh,0Ah,'$' ; строка для вывода end start ; конец программы.

;Строка, после точки с запятой является комментарием

;и не обрабатывается ассемблером

; prg.asm - название файла.

.model tiny ; создаём программу типа СОМ

.code ; начало сегмента кода

org 100h ; начальное значение смещения программы в памяти - 100h

start:

mov ah,9 ; номер функции DOS - в АН

mov dx,offset message ; адрес строки - в DX

int 21h ; вызов т.н. "прерывания" - системной функции DOS

ret ; завершение СОМ-программы

message db "Hello, World!",0Dh,0Ah,'$' ; строка для вывода

end start ; конец программы.

  • В папке D:\TASM.2_0\TASM\ находим «батник» ASM-COM.BAT со следующим текстом:

tasm.exe prg.asm tlink.exe /t /x prg.obj

tasm.exe prg.asm

tlink.exe /t /x prg.obj

Первая строка — запуск транслятора с названием нашего файла с кодом, расположенного в одной директории с транслятором.

Вторая строка — запуск компилятора с параметрами /t /x и название объектного файла — prg.obj, получившегося в результате выполнения первой команды.

Чтобы посмотреть список всех возможных параметров с пояснениями для файлов tasm.exe и tlink.exe необходимо запустить эти программы без параметров. Если вы сделаете это, не выходя из оболочки NC, то, чтобы просмотреть чистое окно DOS нажмите Ctrl+O, чтобы вернуться в NC, нажмите сочетание клавиш повторно.

  • После запуска ASM-COM.BAT в этой же директории появится файл prg.com. Запустив его мы увидим сообщение «Hello World!» в окне MS-DOS (при необходимости просмотра, снова применяем Ctrl+O).

Батник ASM-EXE.BAT предназначен для создания исполняемого файла формате *.EXE (предусматривает раздельную сегментацию для кода, данных и стека — наиболее распространённый формат исполняемых файлов DOS).

Батник COMPLEX.BAT предназначен для создания исполняемых файлов из двух файлов кода (названия обязательно должны быть prg.asm, prg1.asm).

Наша первая программа на ассемблере прекрасно работает!

TASMED (Tasm Editor) — среда разработки приложений DOS на ассемблере.

Выше мы рассмотрели стандартный подход к программированию на TASM в системе MS-DOS. Указанным алгоритмом создания программ можно пользоваться и далее.

Для более удобной работы с кодом целесообразно применять какую-либо среду разработки. Среда разработки — это громко сказано для времён MS-DOS, правильнее сказать — специфический редактор.

Можете попробывать TASMED в папке D:\UTILS\TASMED\. Программа уже настроена и готова к использованию.

Первая программа на ассемблере в среде разработки TASMED.

Основные плюсы:

  • подсветка ассемблерного синтаксиса;
  • возможность сохранения проектов под любым именем и в любой директории;
  • работа как с TASM, так и MASM.

Минусы:

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

Практические советы: группирование проектов, русский язык в MS-DOS.

Для удобства группирования создаваемых программ можно создать отдельную папку (мы создали папку PROJECTS) в которой создавать папки названий проектов, куда копировать соответствующие файлы. Пока у нас — это PRG.ASM, PRG.OBJ, PRG.EXE. Однако, в зависимости от параметров и наших программ их может быть больше (PRG.MAP, PRG.SYM и др.).

В нашем случае, все программы, рассматриваемые в курсе обучения будут группироваться в директории D:\WORK в соответствующих папках. Например, наша первая программа в папке D:\WORK\PRGCOM\ (файлы prg.asm и prg.com). Папку D:\TASM.2_0\PROJECTS\ оставляем пустой для ваших проектов и экспериментов.

Для того, чтобы в исходниках, которые вы будете просматривать в системе MS-DOS с использованием текстовых редакторов DOS и различных сред разработок нормально отображалась кириллица (например в комментариях к строкам кода) необходимо проделать следующие действия.

  1. Запустить драйвер русской раскладки клавиатуры. В нашей запущенной MS-DOS системе на базе DOSBox это файл C:\KEYRUS\keyrus.com. Впрочем, при запуске MS-DOS согласно нашим настройкам DOSBox, он запустится автоматически. При этом будет обеспечено не только отображение русского текста в текстовых редакторах, но и русскоязычная раскладка клавиатуры. Переключение раскладки Eng->Rus и наоборот — горячая клавиша «правый CTRL».
  2. Текст исходников необходимо писать в текстовых редакторах или средах разработки DOS.
  3. Если исходники пишутся в Windows редакторах, должна быть обеспечена русскоязычная кодировка текста — ASCII для DOS (CP866 или OEM866).
Русскоязычная кодировка текста — программа просмотра файлов Total Commander.Русскоязычная кодировка текста — используем Notepad++.

Конечно вопрос снимается сам собой, если комментарии писать на английском.

В следующей статье мы разберём код нашей первой программы на ассемблере.

MASM, TASM, FASM, NASM под Windows и Linux / Хабр

Часть I
Часть II
Часть III

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

MASM


Используется для создания драйверов под Windows.

По ссылке переходим на сайт и скачиваем пакет (masm32v11r.zip). После инсталляции программы на диске создается папка с нашим пакетом C:\masm32. Создадим программу prog11.asm, которая ничего не делает.
.586P .model flat, stdcall _data segment _data ends _text segment start: ret _text ends end start 

Произведём ассемблирование (трансляцию) файла prog11.asm, используя ассемблер с сайта masm32.


Ключ /coff используется здесь для трансляции 32-битных программ.
Линковка производится командой link /subsystem:windows prog11.obj (link /subsystem:console prog11.obj)

Как сказано в Википедии

MASM — один из немногих инструментов разработки Microsoft, для которых не было отдельных 16- и 32-битных версий.

Также ассемблер версии 6. можно взять на сайте Кипа Ирвина kipirvine.com/asm, автора книги «Язык ассемблера для процессоров Intel».

Кстати, вот ссылка на личный сайт Владислава Пирогова, автора книги “Ассемблер для Windows”.

MASM с сайта Microsoft

Далее скачаем MASM (версия 8.0) с сайта Microsoft по ссылке. Загруженный файл носит название «MASMsetup.exe». При запуске этого файла получаем сообщение -«Microsoft Visual C++ Express Edition 2005 required».

Открываем этот файл архиватором (например 7zip). Внутри видим файл setup.exe, извлекаем его, открываем архиватором. Внутри видим два файла vc_masm.msi,vc_masm1.cab. Извлекаем файл vc_masm1.cab, открываем архиватором. Внутри видим файл FL_ml_exe_____X86.3643236F_FC70_11D3_A536_0090278A1BB8. Переименовываем его в файл fl_ml.exe, далее, произведём ассемблирование файла prog11.asm, используя ассемблер fl_ml.exe.

MASM в Visual Studio

Также MASM можно найти в папке с Visual Studio (у меня VS 10) вот здесь: C:\Program Files\Microsoft Visual Studio 10.0\VC\bin\ml.exe.

Для того, чтобы запустить на 32- или 64-разрядной системе и создавать программы, работающие как под 32-, так и под 64-разрядной Windows, подходит MASM32 (ml.exe, fl_ml.exe). Для того, чтобы работать на 32- и 64-разрядных системах и создавать программы, работающие под 64-разрядной Windows, но неработающие под 32-разрядной нужен ассемблер ml64.exe. Лежит в папке C:\Program Files\Microsoft Visual Studio 10.0\VC\bin\amd64 и вот здесь — C:\Program Files\Microsoft Visual Studio 10.0\VC\bin\x86_amd64.

TASM


Программный пакет компании Borland, предназначенный для разработки программ на языке ассемблера для архитектуры x86. В настоящее время Borland прекратила распространение своего ассемблера.

Скачать можно, например, здесь. Инсталлятора нет, просто извлекаем программу. Вот исходник из книги Питера Абеля (рис. 3.2) «Язык Ассемблера для IBM PC и программирования».

stacksg segment para stack 'stack' db 12 dup ('stackseg') stacksg ends codesg segment para 'code' begin proc far assume ss:stacksg,cs:codesg,ds:nothing push ds sub ax,ax push ax mov ax, 0123h add ax, 0025h mov bx,ax add bx,ax mov cx,bx sub cx,ax sub ax,ax nop ret begin endp codesg ends end begin 

Выполним ассемблирование (трансляцию) файла abel32.asm.

Корректность работы программы можно проверить, произведя линковку (tlink.exe) объектного файла и запустив полученный файл в отладчике.

Как было сказано выше, MASM можно использовать для работы с 16-битными программами. Выполним ассемблирование (трансляцию) программы abel32.asm с помощью ассемблера MASM:

Ключ /coff здесь не используется.
Линковка производится файлом link16.exe

FASM


В статье Криса Касперски «Сравнение ассемблерных трансляторов» написано, что «FASM — неординарный и весьма самобытный, но увы, игрушечный ассемблер. Пригоден для мелких задач типа „hello, world“, вирусов, демок и прочих произведений хакерского творчества.»

Скачаем FASM с официального сайта. Инсталлятора нет, просто извлекаем программу. Откроем fasm editor — C:\fasm\fasmw.exe. В папке C:\fasm\EXAMPLES\HELLO есть файл HELLO.asm.

include 'win32ax.inc' .code start: invoke MessageBox,HWND_DESKTOP,"Hi! I'm the example program!",invoke GetCommandLine,MB_OK invoke ExitProcess,0 .end start 

Откроем файл HELLO.asm из fasmw.exe. Изменим строку include 'win32ax.inc' на строку include 'c:\fasm\INCLUDE\WIN32AX.INC'. Запускаем из меню Run → Run.

Вот ссылки на ресурсы, посвященные FASM:

→ FASM на Cyberforum'е
→ FASM на asmworld .com программы под Dos
→ Цикл статей «Ассемблер под Windows для чайников»
→ Сайт на narod'е

FASM в Linux

Для того, использовать FASM в Linux (у меня Ubuntu), скачаем соответствующий дистрибутив (fasm-1.71.60.tgz), распакуем его, в папке у нас будет бинарный файл fasm, копируем этот файл в /usr/local/bin для того, чтобы можно было запускать его из консоли, как любую другую команду.Выполним ассемблирование программы hello.asm из папки fasm/examples/elfexe/hello.asm.

Корректность работы программы можно проверить в отладчике.

Nasm


Nasm успешно конкурирует со стандартным в Linux- и многих других UNIX-системах ассемблером Gas.

Nasm в Linux можно установить его с помощью менеджера пакетов или из командной строки: в дистрибутиве Debian (Ubuntu) командой apt-get install nasm, в дистрибутивах Fedora, CentOS, RedHat командой yum install nasm.

Создадим программу, которая 5 раз выводит сообщение “Hello”. Пример взят из книги Андрея Викторовича Столярова “Программирование на языке ассемблера NASM для ОС UNIX”. Учебник, а также библиотека “stud_io.inc” есть на личном сайте автора.

%include "stud_io.inc" global _start section .text _start: mov eax, 0 again: PRINT "Hello" PUTCHAR 10 inc eax cmp eax, 5 jl again FINISH 

Выполним ассемблирование и линковку и запустим файл hello.asm.
$ nasm -f elf hello.asm $ ld hello.o -o hello $ ./hello 

Для 64bit необходимо использовать команду nasm -f elf64 hello.asm

NASM для Windows

NASM для Windows можно установить, скачав соответствующий дистрибутив с соответствующего сайта.

Ассемблирование:
nasm -f bin имя_файла.asm -o имя_файла.com

Ссылки на ресурсы, посвященные Nasm:

→ Сайт А.В. Столярова
→ Сайт, на котором лежит электронный учебник (в архиве)
→ То же самое

AS


Стандартный ассемблер практически во всех разновидностях UNIX, в том числе Linux и BSD. Свободная версия этого ассемблера называется GAS (GNU assembler). Позволяет транслировать программы с помощью компилятора GCC.

Из учебников удалось найти только книгу на английском «Programming from the ground up». На русском удалось найти только одну главу из книги С. Зубкова «Assembler для DOS, Windows и UNIX».

Возьмем пример программы, которая ничего не делает, с сайта. Создадим программу gas.s

.section .text .globl _start _start: movl $1, %eax movl $2, %ebx int $0x80 

Выполним ассемблирование (трансляцию), линковку и запуск программы:
$ as -o gas.o gas.s $ ld -o gas gas.o $ ./gas 

Если в данной программе изменить _start на main, то можно выполнить ассемблирование (трансляцию) и линковку компилятором gcc.
.section .text .globl main main: movl $1, %eax movl $2, %ebx int $0x80 

Выполним ассемблирование (трансляцию), линковку и запуск программы:
$ gcc gas.s -o gas $ ./gas 

Выводы: если вы изучаете программирование под Windows, то вы можете остановить свой выбор на Masm; Tasm больше не поддерживается, но для обучения по старым классическим учебникам подойдёт.
Под Linux Gas подойдет тем, кто использует GCC, а тем, кому не нравится синтаксис Gas, подойдёт Nasm.

P.S. Следующие две части, в целом, посвящены обработке строки в цикле.
P.P.S. Little Man Computer — учебная модель компьютера с ограниченым набором ассемблерных инструкций рассматривается в этой статье.

Ассемблер. Базовый синтаксис | Уроки Ассемблера

  Обновл. 29 Сен 2019  | 

Программы на ассемблере могут быть разделены на три секции:

   Секция data

   Секция bss

   Секция text

Секции ассемблера

Секция data используется для объявления инициализированных данных или констант. Данные в этой секции не могут быть изменены во время выполнения. Вы можете хранить константные значения и названия файлов в этой секции. Синтаксис объявления:

Секция bss используется для объявления переменных. Синтаксис объявления:

Секция text используется для хранения кода программы. Данная секция должна начинаться с объявления global_start, которое сообщает ядру, откуда нужно начинать выполнение программы. Синтаксис объявления:

section.text global _start _start:

section.text

   global _start

_start:

Комментарии

Комментарии в ассемблере должны начинаться с точки с запятой (;). Они могут содержать любой печатный символ, включая пробел. Комментарий может находиться как на отдельной строке:

; эта программа выводит сообщение на экран

; эта программа выводит сообщение на экран

Так и на строке со стейтментом:

add eax, ebx ; добавляет ebx к eax

add eax, ebx     ; добавляет ebx к eax

Стейтменты

В ассемблере есть три вида стейтментов:

   Выполняемые инструкции (или просто «инструкции»), которые сообщают процессору, что нужно делать. Каждая инструкция хранит в себе код операции (или ещё «опкод») и генерирует одну инструкцию на машинном языке.

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

   Макросы, которые являются простым механизмом вставки кода.

В ассемблере на одну строку приходится один стейтмент, который должен соответствовать следующему формату:

[метка] mnemonic [операнды] [; комментарий]

[метка]   mnemonic   [операнды]   [; комментарий]

Базовая инструкция состоит из названия инструкции (mnemonic) и операндов (они же «параметры»). Вот примеры типичных стейтментов ассемблера:

INC COUNT ; выполняем инкремент переменной памяти COUNT MOV TOTAL, 48 ; перемещаем значение 48 в переменную памяти TOTAL ADD AH, BH ; добавляем содержимое регистра BH к регистру AH AND MASK1, 128 ; выполняем операцию AND с переменной MASK1 и 128 ADD MARKS, 10 ; добавляем 10 к переменной MARKS MOV AL, 10 ; перемещаем значение 10 в регистр AL

INC COUNT        ; выполняем инкремент переменной памяти COUNT

 

MOV TOTAL, 48    ; перемещаем значение 48 в переменную памяти TOTAL

  

ADD AH, BH       ; добавляем содержимое регистра BH к регистру AH

  

AND MASK1, 128   ; выполняем операцию AND с переменной MASK1 и 128

  

ADD MARKS, 10    ; добавляем 10 к переменной MARKS

MOV AL, 10       ; перемещаем значение 10 в регистр AL

Первая программа

Следующая программа на языке ассемблера выведет строку Hello, world! на экран:

section .text global _start ; необходимо для линкера (ld) _start: ; сообщает линкеру стартовую точку mov edx,len ; длина строки mov ecx,msg ; строка mov ebx,1 ; дескриптор файла (stdout) mov eax,4 ; номер системного вызова (sys_write) int 0x80 ; вызов ядра mov eax,1 ; номер системного вызова (sys_exit) int 0x80 ; вызов ядра section .data msg db 'Hello, world!', 0xa ; содержимое строки для вывода len equ $ - msg ; длина строки

section .text

   global _start    ; необходимо для линкера (ld)

_start:             ; сообщает линкеру стартовую точку

   mov edx,len     ; длина строки

   mov ecx,msg     ; строка

   mov ebx,1       ; дескриптор файла (stdout)

   mov eax,4       ; номер системного вызова (sys_write)

   int 0x80        ; вызов ядра

   mov eax,1       ; номер системного вызова (sys_exit)

   int 0x80        ; вызов ядра

 

section .data

msg db 'Hello, world!', 0xa  ; содержимое строки для вывода

len equ $ - msg              ; длина строки

Результат выполнения программы выше:

Hello, world!

Сборка программ

Убедитесь, что у вас установлен NASM. Запишите вашу программу в текстовом редакторе и сохраните её как hello.asm. Затем:

   откройте терминал;

   убедитесь, что вы находитесь в той же директории, в которой вы сохранили hello.asm;

   чтобы собрать программу, введите команду nasm -f elf hello.asm;

   если не было ошибок, то создастся объектный файл вашей программы под названием hello.o;

   чтобы ваш объектный файл прошёл линкинг и создался исполняемый файл под названием hello, введите команду ld -m elf_i386 -s -o hello hello.o;

   запустите программу, написав команду ./hello.

Если всё прошло успешно, то вам выведется Hello, world!.

Если у вас нет возможности скомпилировать программу, например, у вас нет Linux и вы пока не хотите на него переходить, то можете использовать одну из следующих онлайн-IDE:

   TutorialsPoint

   JDoodle

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

all: nasm –f elf $(source) ld –m elf_i386 –s –o $(source) $(source).o rm $(source).o

all:

    nasm –f elf $(source)

    ld –m elf_i386 –s –o $(source) $(source).o

    rm $(source).o

Для сборки hello.asm выполните следующие действия:

   откройте терминал;

   убедитесь, что вы находитесь в той же директории, в которой вы сохранили hello.asm и Makefile;

   введите команду make source=hello.


Оценить статью:

Загрузка...

Поделиться в социальных сетях:

Создание проекта на языке ассемблера в Microsoft Visual Studio Express 2010


 

Создание проекта консольного или оконного Windows-приложения не отличается от рассмотренного для языков программирования Си и C++.

После того, как в Visual Studio появилось окно проекта (в левой части появившегося окна отображается Обозреватель решений), для добавления нового файла программы в проект выбираем по правой кнопке мыши на папке Файлы исходного кода меню Добавить->Создать элемент.
В появившемся окне выбираем Файл C++ (.cpp), задаем имя файла и вручную добавляем к нему расширение asm. Нажимаем кнопку Добавить.

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

.686P
.MODEL FLAT, STDCALL
.STACK 4096
.DATA
MB_OK    EQU 0
STR1     DB «Моя первая программа»,0
STR2     DB «Привет всем!»,0
HW       DD ?
EXTERN MessageBoxA@16:NEAR
.CODE
START:
PUSH     MB_OK
PUSH     OFFSET STR1
PUSH     OFFSET STR2
PUSH     HW
CALL     MessageBoxA@16
RET
END START

Далее необходимо сообщить среде разработки, что данный файл является программой на языке ассемблера, и для корректного включения его в проект требуется использовать Microsoft Macro Assembler. Для этого выбираем для проекта (по правой клавише мыши) опцию Настройки построения.

В появившемся окне ставим галочку для masm (Microsoft Macro Assembler) и нажимаем OK.
Теперь нужно проверить, что для файла на языке ассемблера установился соответствующий инструмент сборки. По правой кнопке мыши для файла с расширением .asm выбираем опцию Свойства.

В появившемся окне для выбранного файла отмечаем инструмент сборки Microsoft Macro Assembler.

Для построения проекта выбираем меню Отладка->Построить решение.

В случае успешного построения в нижней части окна отображается Построение: успешно 1.

Для запуска приложения выбираем меню Отладка->Начать отладку.

Результат выполнения программы:

Изменить тип приложения с консольного на оконное

Чтобы убрать консоль (поменять тип приложения с консольного на оконное, или наоборот) необходимо обратиться к меню Свойства проекта, вызванного по правой кнопке мыши.
В появившемся окне выбрать раздел Компоновщик->Система, и в разделе Подсистема поменять тип с Консоль на Windows (или наоборот).

Повторная сборка и запуск программы на выполнения выдадут следующий результат (консоли нет):

Подсветка синтаксиса языка ассемблера

Для того, чтобы включить подсветку синтаксиса языка ассемблера в Microsoft Visual Studio Express 2010 необходимо загрузить файл usertype и распаковать его в папку

C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE

Для подключения подсветки синтаксиса выбираем меню Сервис->Параметры

В появившемся окне выбрать Текстовый редактор->Расширение файла и вручную добавляем расширение asm. Нажимаем кнопку Добавить, затем — OK.

После перезапуска Microsoft  Visual Studio Express 2010 подсветка синтаксиса языка ассемблера будет активна.

Назад


Назад: Язык ассемблера

Простая программа на ассемблере x86: Решето Эратосфена / Хабр

Вступительное слово

По своей профессии я не сталкиваюсь с низкоуровневым программированием: занимаюсь программированием на скриптовых языках. Но поскольку душа требует разнообразия, расширения горизонтов знаний или просто понимания, как работает машина на низком уровне, я занимаюсь программированием на языках, отличающихся от тех, с помощью которых зарабатываю деньги – такое у меня хобби.

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

До ее написания я сформулировал такие требования к будущей программе:

  • Моя программа не должна быть программой под DOS. Слишком много примеров ориентировано на нее в связи с простым API. Моя программа обязательно должна была запускаться на современных ОС.
  • Программа должна использовать кучу – получать в свое распоряжение динамически распределяемую память.
  • Чтобы не быть слишком сложной, программа должна работать с целыми беззнаковыми числами без использования переносов.

Задачей для своей программы я выбрал поиск простых чисел с помощью Решета Эратосфена. В качестве ассемблера я выбрал nasm.

Код я писал с упором больше на стиль и понятность, чем на скорость его выполнения. К примеру, обнуление регистра я проводил не с помощью xor eax, eax, а с помощью mov eax, 0 в связи с более подходящей семантикой инструкции. Я решил, что поскольку программа преследует исключительно учебные цели, можно распоясаться и заниматься погоней за стилем кода в ассемблере.

Итак, посмотрим, что получилось.

С чего начать?

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

Так же встает вопрос, каким образом на таком низком уровне реализуется обмен данными между внутренним миром программы и внешней средой. Тут на сцену выходит API операционной системы. В DOS, как уже было упомянуто, интерфейс был достаточно простой. К примеру, программа «Hello, world» выглядела так:

SECTION .text org 0x100 mov ah, 0x9 mov dx, hello int 0x21 mov ax, 0x4c00 int 0x21 SECTION .data hello: db "Hello, world!", 0xD, 0xA, '$' 

В Windows же для этих целей используется Win32 API, соответственно, программа должна использовать методы соответствующих библиотек:

%include "win32n.inc" extern MessageBoxA import MessageBoxA user32.dll extern ExitProcess import ExitProcess kernel32.dll SECTION code use32 class=code ..start: push UINT MB_OK push LPCTSTR window_title push LPCTSTR banner push HWND NULL call [MessageBoxA] push UINT NULL call [ExitProcess] SECTION data use32 class=data banner: db "Hello, world!", 0xD, 0xA, 0 window_title: db "Hello", 0 

Здесь используется файл win32n.inc, где определены макросы, сокращающие код для работы с Win32 API.

Я решил не использовать напрямую API ОС и выбрал путь использования функций из библиотеки Си. Так же это открыло возможность компиляции программы в Linux (и, скорее всего, в других ОС) – не слишком большое и нужное этой программе достижение, но приятное достижение.

Вызов подпрограмм

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

Подпрограммы представляют собой метку, по которой располагается код. Заканчивается подпрограмма инструкцией ret. К примеру, вот такая подпрограмма в DOS выводит в консоль строку «Hello, world»:

print_hello: mov ah, 0x9 mov dx, hello int 0x21 ret 

Для ее вызова нужно было бы использовать инструкцию call:

call print_hello 

Для себя я решил передавать аргументы подпрограммам через регистры и указывать в комментариях, в каких регистрах какие аргументы должны быть, но в языках высокого уровня аргументы передаются через стек. К примеру, вот так вызывается функция printf из библиотеки Си:

push hello call _printf add esp, 4 

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

При входе в подпрограмму необходимо создать новый стековый кадр. Делается это следующим образом:

print_hello: push ebp ;сохраняем указатель начала стекового кадра на стеке mov ebp, esp ;теперь началом кадра является вершина предыдущего 

Соответственно, перед выходом нужно восстановить прежнее состояние стека:

 mov esp, ebp pop ebp ret 

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

print_hello: push ebp mov ebp, esp sub esp, 8 ;опускаем указатель вершины стека на 8 байт, чтобы выделить память 

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

print_hello: enter 8, 0 ;создать новый кадр, выделить 8 байт для локальных переменных leave ;восстановить стек ret 

Второй параметр инструкции enter – уровень вложенности подпрограммы. Он нужен для линковки с языками высокого уровня, поддерживающими такую методику организации подпрограмм. В нашем случае это значение можно оставить нулевым.

Непосредственно программа

Проект содержит такие файлы:
  • main.asm – главный файл,
  • functions.asm – подпрограммы,
  • string_constants.asm – определения строковых констант,
  • Makefile – сценарий сборки

Рассмотрим код основного файла:main.asm
%define SUCCESS 0 %define MIN_MAX_NUMBER 3 %define MAX_MAX_NUMBER 4294967294 global _main extern _printf extern _scanf extern _malloc extern _free SECTION .text _main: enter 0, 0 ;ввод максимального числа call input_max_number cmp edx, SUCCESS jne .custom_exit mov [max_number], eax ;выделяем память для массива флагов mov eax, [max_number] call allocate_flags_memory cmp edx, SUCCESS jne .custom_exit mov [primes_pointer], eax ;отсеять составные числа mov eax, [primes_pointer] mov ebx, [max_number] call find_primes_with_eratosthenes_sieve ;вывести числа mov eax, [primes_pointer] mov ebx, [max_number] call print_primes ;освободить память от массива флагов mov eax, [primes_pointer] call free_flags_memory ;выход .success: push str_exit_success call _printf jmp .return .custom_exit: push edx call _printf .return: mov eax, SUCCESS leave ret %include "functions.asm" SECTION .data max_number: dd 0 primes_pointer: dd 0 %include "string_constants.asm" 


Видно, что программа поделена по смыслу на 5 блоков, оформленных в виде подпрограмм:
  1. input_max_number — с помощью консоли запрашивает у пользователя максимальное число, до которого производится поиск простых; во избежание ошибок значение ограничено константами MIN_MAX_NUMBER и MAX_MAX_NUMBER
  2. allocate_flags_memory — запросить у ОС выделение памяти для массива пометок чисел (простое/составное) в куче; в случае успеха возвращает указатель на выделенную память через регистр eax
  3. find_primes_with_eratosthenes_sieve — отсеять составные числа с помощью классического решета Эратосфена;
  4. print_primes — вывести в консоль список простых чисел;
  5. free_flags_memory — освободить память, выделенную для флагов

Для функций было условлено такое правило: значение возвращается через регистр eax, регистр edx содержит статус. В случае успеха он содержит значение SUCCESS, то есть, 0, в случае неудачи — адрес строки с сообщением об ошибке, которое будет выведено пользователю.

Файл string_constants.asm содержит определение строковых переменных, значения которых, как намекает название файла, менять не предполагается. Только ради этих переменных было сделано исключение к правилу «не использовать глобальные переменные». Я так и не нашел более удобного способа доставлять строковые константы функциям ввода-вывода – подумывал даже записывать на стек непосредственно перед вызовами функций, но решил, что эта идея куда хуже идеи с глобальными переменными.

string_constants.asm
;подписи ввода-вывода, форматы str_max_number_label: db "Max number (>=3): ", 0 str_max_number_input_format: db "%u", 0 str_max_number_output_format: db "Using max number %u", 0xD, 0xA, 0 str_print_primes_label: db "Primes:", 0xD, 0xA, 0 str_prime: db "%u", 0x9, 0 str_cr_lf: db 0xD, 0xA, 0 ;сообщения выхода str_exit_success: db "Success!", 0xD, 0xA, 0 str_error_max_num_too_little: db "Max number is too little!", 0xD, 0xA, 0 str_error_max_num_too_big: db "Max number is too big!", 0xD, 0xA, 0 str_error_malloc_failed: db "Can't allocate memory!", 0xD, 0xA, 0 


Для сборки применяется такой сценарий:Makefile
ifdef SystemRoot format = win32 rm = del ext = .exe else format = elf rm = rm -f ext = endif all: primes.o gcc primes.o -o primes$(ext) $(rm) primes.o primes.o: nasm -f $(format) main.asm -o primes.o 

Подпрограммы (функции)

input_max_number

Код подпрограммы
; Ввести максимальное число ; Результат: EAX - максимальное число input_max_number: ;создать стек-фрейм, ;4 байта для локальных переменных enter 4, 1 ;показываем подпись push str_max_number_label ;см. string_constants.asm call _printf add esp, 4 ;вызываем scanf mov eax, ebp sub eax, 4 push eax push str_max_number_input_format ;см. string_constants.asm call _scanf add esp, 8 mov eax, [ebp-4] ;проверка cmp eax, MIN_MAX_NUMBER jb .number_too_little cmp eax, MAX_MAX_NUMBER ja .number_too_big jmp .success ;выход .number_too_little: mov edx, str_error_max_num_too_little ;см. string_constants.asm jmp .return .number_too_big: mov edx, str_error_max_num_too_big ;см. string_constants.asm jmp .return .success: push eax push str_max_number_output_format ;см. string_constants.asm call _printf add esp, 4 pop eax mov edx, SUCCESS .return: leave ret 


Подпрограмма призвана ввести в программу максимальное число, до которого будет производиться поиск простых. Ключевым моментов тут является вызов функции scanf из библиотеки Си:
 mov eax, ebp sub eax, 4 push eax push str_max_number_input_format ;см. string_constants.asm call _scanf add esp, 8 mov eax, [ebp-4] 

Таким образом, сначала в eax записывается адрес памяти на 4 байта ниже указателя базы стека. Это память, выделенная для локальных нужд подпрограммы. Указатель на эту память передается функции scanf как цель для записи данных, введенных с клавиатуры.

После вызова функции, в eax из памяти перемещается введенное значение.

allocate_flags_memory и free_flags_memory

Код подпрограмм
; Выделить память для массива флагов ; Аргумент: EAX - максимальное число ; Результат: EAX - указатель на память allocate_flags_memory: enter 8, 1 ;выделить EAX+1 байт inc eax mov [ebp-4], eax push eax call _malloc add esp, 4 ;проверка cmp eax, 0 je .fail mov [ebp-8], eax ;инициализация mov byte [eax], 0 cld mov edi, eax inc edi mov edx, [ebp-4] add edx, eax mov al, 1 .write_true: stosb cmp edi, edx jb .write_true ;выход mov eax, [ebp-8] jmp .success .fail: mov edx, str_error_malloc_failed ;см. string_constants.asm jmp .return .success: mov edx, SUCCESS .return: leave ret ; Освободить память от массива флагов ; Аргумент: EAX - указатель на память free_flags_memory: enter 0, 1 push eax call _free add esp, 4 leave ret 


Ключевыми местами этих подпрограмм являются вызовы функций malloc и free из библиотеки Си.

malloc в случае удачи возвращает через регистр eax адрес выделенной памяти, в случае неудачи этот регистр содержит 0. Это самое узкое место программы касательно максимального числа. 32 бит вполне достаточно для поиска простых чисел до 4 294 967 295, но выделить разом столько памяти не получится.

find_primes_with_eratosthenes_sieve

Код подпрограммы
;Найти простые числа с помощью решета Эратосфена ;Аргументы: EAX - указатель на массив флагов, EBX - максимальное число find_primes_with_eratosthenes_sieve: enter 8, 1 mov [ebp-4], eax add eax, ebx inc eax mov [ebp-8], eax ;вычеркиваем составные числа cld mov edx, 2 ;p = 2 mov ecx, 2 ;множитель с = 2 .strike_out_cycle: ;x = c*p mov eax, edx push edx mul ecx pop edx cmp eax, ebx jbe .strike_out_number jmp .increase_p .strike_out_number: mov edi, [ebp-4] add edi, eax mov byte [edi], 0 inc ecx ;c = c + 1 jmp .strike_out_cycle .increase_p: mov esi, [ebp-4] add esi, edx inc esi mov ecx, edx inc ecx .check_current_number: mov eax, ecx mul eax cmp eax, ebx ja .return lodsb inc ecx cmp al, 0 jne .new_p_found jmp .check_current_number .new_p_found: mov edx, ecx dec edx mov ecx, 2 jmp .strike_out_cycle .return: leave ret 

Подпрограмма реализует классический алгоритм для вычеркивания составных чисел, решето Эратосфена, на языке ассемблера x86. Приятна тем, что не использует вызовы внешних функций и не требует обработки ошибок :)

print_primes

Код подпрограммы
; Вывести простые числа ; Параметры: EAX - указатель на массив флагов, EBX - максимальное число print_primes: enter 12, 1 mov [ebp-4], eax mov [ebp-8], ebx push str_print_primes_label call _printf add esp, 4 cld mov esi, [ebp-4] mov edx, esi add edx, [ebp-8] inc edx mov [ebp-12], edx mov ecx, 0 .print_cycle: lodsb cmp al, 0 jne .print jmp .check_finish .print: push esi push ecx push str_prime ;см. string_constants.asm call _printf add esp, 4 pop ecx pop esi mov edx, [ebp-12] .check_finish: inc ecx cmp esi, edx jb .print_cycle push str_cr_lf call _printf add esp, 4 leave ret 


Подпрограмма выводит в консоль простые числа. Ключевым моментом тут является вызов функции printf из библиотеки Си.
Заключение

Что ж, программа отвечает всем сформулированным требованиям и, кажется, проста для понимания. Хочется надеяться, кому-нибудь ее разбор поможет вникнуть в программирование на низком уровне и он получит от него такое же удовольствие, какое получил я.

Так же привожу полные исходники программы.

Могу так же привести интересный факт. Поскольку с детства нас учили, что программы на языке ассемблера выполняются быстрее, я решил сравнить скорость выполнения этой программы со скоростью программы на C++, которую я писал когда-то и которая искала простые числа с помощью Решета Аткина. Программа на С++, скомпилированная в Visual Studio с /O2 выполняла поиск до числа 230 примерно за 25 секунд на моей машине. Программа же на ассемблере показала 15 секунд с Решетом Эратосфена.

Это, конечно, скорее байка, чем научный факт, поскольку не было серьезного тестирования не было выяснения причин, но как интересный факт для завершения статьи подойдет, как мне кажется.

Полезные ссылки

  1. Список ресурсов для изучения ассемблера
  2. Организация памяти
  3. Решето Эратосфена
  4. Решето Аткина
  5. Стек
  6. Стековый кадр

Ассемблер для начинающих / Хабр

В любом деле главное — начать. Или вот еще хорошая поговорка: «Начало — половина дела». Но иногда даже не знаешь как подступиться к интересующему вопросу. В связи с тем, что воспоминания мои еще свежи, спешу поделиться своими соображениями с интересующимися.

Скажу сразу, что лично я ассемблирую не под PC, а под микроконтроллеры. Но это не имеет большого значения, ибо (в отличие от микроконтроллеров AVR) система команд данных микроконтроллеров с PC крайне схожа. Да и, собственно говоря, ассемблер он и в Африке ассемблер.

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

Для начала успокою любознательных новобранцев: ассемблер — это совсем не сложно, вопреки стереотипному мнению. Просто он ближе к «земле», то бишь к архитектуре. На самом деле, он очень прост, если ухватить основную идею. В отличие от языков высокого уровня и разнообразных специализированных платформ для программирования (под всем перечисленным я понимаю всякое вроде C++, MatLAB и прочих подобных штук, где требуются программерские навыки), команд тут раз-два и обчелся. По началу даже, когда мне нужно было посчитать двойной интеграл, эта задача вызывала лишь недоумение: как при помощи такого скудного количества операций можно совершить подобную процедуру? Ведь образно говоря, на ассемблере можно разве что складывать, вычитать и сдвигать числа. Но с помощью ассемблера можно совершать сколь угодно сложные операции, а код будет выходить крайне лёгкий. Вот даже для примера, нужно вам зажечь светодиод, который подключен, например, к нулевому контакту порта номер 2, вы просто пишете:
bset P2.0
И, как говорится, никаких проблем. Нужно включить сразу штуки четыре, подключенных последовательно? Да запросто:
mov P2, #000fh
Да, тут я подразумеваю, что начинающий боец уже знаком хотя бы со системами счисления. Ну хотя бы с десятичной. ;)

Итак, для достижения успеха в деле ассемблирования, следует разбираться в архитектуре (в моем случае) микроконтроллера. Это раз.

Кстати, одно из больных мест в познании архитектуры — это организация памяти. Тут на Хабре я видела соответствующую статью: habrahabr.ru/blogs/programming/128991. Еще могу упомянуть ключевые болевые точки: прерывания. Штука не сложная, но по началу (почему-то) тяжелая для восприятия.

Если перед вами стоит сложная задача и вы даже не знаете как по началу к ней подступиться, лучше всего написать алгоритм. Это воистину спасает. А по началу, даже если программа совершенно не сложная, лучше всё же начать с алгоритма, ибо этот процесс помогает разложить всё в голове по местам. Возвращаясь к примеру с вычислением двойного интеграла по экспериментальным данным, обдумывала алгоритм я весь день, но зато потом программку по нему написала всего за 20 минут. Плюс алгоритм будет полезен при дальнейшей модернизации и/или эксплуатации программы, а то ассемблерный код, временами, если и будет понятен построчно, то чтобы разобраться в чем же общая идея, придется немало потрудиться.

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

Собственно, хотелось бы как Фандорин написать: «Это т-т-три»… Но, боюсь, на этом пока можно остановиться. Хотя хотелось бы добавить еще несколько рекомендаций и пряников.

Подводя итог моему несколько сумбурному монологу, ключевые моменты в программировании на ассемблере — это знание архитектуры и связное построение мыслей. Конечно, не обязательно сразу с головой кидаться в штудировании литературы с описанием внутренностей того же PC, но общее представление (повторюсь, хотя бы для начала) будет очень нужно.

А теперь обещанные пряники! Вот я тут распинаюсь о каком-то непонятном ассемблере, а что же в нем, собственно говоря, хорошего? Да много всего! Во-первых, конечно, не нужно запоминать много команд, используемых библиотек и прочей сопутствующей дребедени. Всего парочка команд и, считайте, вы во всеоружии. Во-вторых, в связи с крайней близостью к машинным кодам, вы можете делать практически всё, что душе угодно (в отличие от тех же языков высокого уровня)! В-третьих, ассемблерный код, по причине максимальной лаконичности в формулировках, выполняется крайне быстро.

В общем, сплошные плюсы. На этой оптимистической ноте разрешите откланяться.

linux - Как сделать Makefile для программы на языке ассемблера?

Переполнение стека
  1. Около
  2. Товары
  3. Для команд
  1. Переполнение стека Общественные вопросы и ответы
  2. Переполнение стека для команд Где разработчики и технологи делятся частными знаниями с коллегами
  3. Вакансии Программирование и связанные с ним технические возможности карьерного роста
  4. Талант
.

linux - Как открыть файл на ассемблере и изменить его?

Переполнение стека
  1. Около
  2. Товары
  3. Для команд
  1. Переполнение стека Общественные вопросы и ответы
  2. Переполнение стека для команд Где разработчики и технологи делятся частными знаниями с коллегами
.

Как получить вывод ассемблера из исходного кода C / C ++ в gcc?

Переполнение стека
  1. Около
  2. Товары
  3. Для команд
  1. Переполнение стека Общественные вопросы и ответы
  2. Переполнение стека для команд Где разработчики и технологи делятся частными знаниями с коллегами
  3. Вакансии Программирование и связанные с ним технические возможности карьерного роста
  4. Талант Нанимайте технических специалистов и создавайте свой бренд работодателя
.

x86 - Как сделать ассемблер?

Переполнение стека
  1. Около
  2. Товары
  3. Для команд
  1. Переполнение стека Общественные вопросы и ответы
  2. Переполнение стека для команд Где разработчики и технологи делятся частными знаниями с коллегами
  3. Вакансии Программирование и связанные с ним технические возможности карьерного роста
  4. Талант Нанимайте технических специалистов и создавайте свой бренд работодателя
.

winapi - Как написать hello world на ассемблере под Windows?

Переполнение стека
  1. Около
  2. Товары
  3. Для команд
  1. Переполнение стека Общественные вопросы и ответы
  2. Переполнение стека для команд Где разработчики и технологи делятся частными знаниями с коллегами
.

Использование службы Assembler

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

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

Дизайнеры форм используют LiveC ycle Designer для создания фрагментов форм. Эти фрагменты представляют собой подчиненные формы с уникальными именами в форме XFA. Дизайнеры форм также используют Designer для создания форм XFA с уникальными именами точек вставки. Вы (программист) пишете документы DDX, в которых указывается, как фрагменты вставляются в форму XFA.

На следующем рисунке показаны две формы XML (шаблоны XFA). Форма слева содержит точку вставки с именем myInsertionPoint.Форма справа содержит фрагмент с именем myFragment.

.

Смотрите также