Как запустить программу на ассемблере
Перейти к содержимому

Как запустить программу на ассемблере

  • автор:

MASM под Windows: быстрый старт

В этом документе кратко описан процесс установки учебной среды на основе ассемблера MASM под ОС Windows, а также порядок работы с ней.

Установка MASM

  1. Скачайте архив c MASM с сайта arch32.cs.msu.su.
  2. Распакуйте архив в каталог C:\masm32 (необходимо использовать именно такой путь). Убедитесь, что в каталоге C:\masm32 оказались каталоги bin , lib , include .
  3. Создайте где-либо в удобном месте рабочий каталог для ваших программ, например, C:\asm или D:\study\sem2 . Рабочий стол Windows или папка «Мои документы» не являются подходящими местами для рабочего каталога — проследите, чтобы в полном пути к каталогу не было русских букв или пробелов. Далее таким каталогом будет считаться C:\work .
  4. Скачайте файл prompt.bat и положите его в ваш рабочий каталог.

Простейшая программа

Для следующего шага вам потребуется текстовый редактор, пригодный для работы с программным кодом. Заметим, что Microsoft Word или встроенный в Windows редактор WordPad являются текстовыми процессорами и для работы с программным кодом непригодны. Редактор Notepad (Блокнот) подходит для работы с текстовыми файлами (plain text), но неудобен в качестве программистского редактора — в нем отсутствует подсветка синтаксиса и другие стандартные для таких редакторов функции.

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

Примечание: Если вы решили скачать Notepad2, при первом запуске установите ширину табуляции (Tabulator width) в значение 8 при помощи меню Settings > Tab Settings.

Создайте в вашем рабочем каталоге файл hello.asm следующего содержания:

include console.inc .code Start: mov ecx, 5 again: outstrln 'Hello World' dec ecx jnz again exit end Start 

Примечание: В Notepad2 при сохранении введите имя файла hello.asm , и подсветка синтаксиса включится автоматически.

Эта программа выводит пять раз строчку “Hello World” на экран. Для вывода текста используется макрос outstrln , который определен в файле console.inc .

Трансляция и запуск программы

Дважды щелкните по файлу prompt.bat , который вы скачали на шаге установки. Откроется окно командной строки. Убедитесь, что все сделано правильно, набрав команду dir и нажав Enter . Вы должны увидеть, что в текущем каталоге (вашем рабочем каталоге) находятся файлы hello.asm и prompt.bat :

C:\work>dir Volume in drive C has no label. Volume Serial Number is C00A-56A9 Directory of C:\work 03/05/2017 11:16 AM . 03/05/2017 11:16 AM .. 03/05/2017 11:11 AM 158 hello.asm 03/05/2017 10:29 AM 81 prompt.bat 2 File(s) 239 bytes 2 Dir(s) 133,457,559,552 bytes free 

Примечание для тех, кто никогда прежде не работал с командной строкой. Взаимодействие с ней устроено следующим образом: командная строка выводит приглашение (в примере выше это C:\work> ), далее пользователь вводит команду (выше — dir ) и нажимает клавишу Enter , после чего на экране появляется вывод команды, то есть результат ее работы.

Для запуска программы требуется ее оттранслировать. Первый шаг — запуск ассемблера MASM, который построит по исходному тексту програмы объектный файл:

C:\work>ml /c /coff hello.asm Microsoft (R) Macro Assembler Version 6.14.8444 Copyright (C) Microsoft Corp 1981-1997. All rights reserved. Assembling: hello.asm 

Аргумент /c инструктирует ассемблер выполнить только трансляцию в объектный файл, без компоновки (которую мы выполним чуть позже). Аргумент /coff указывает формат объектного файла — COFF (Common Object File Format).

В рабочем каталоге появится файл hello.obj . Запустите компоновщик:

C:\work>link /subsystem:console hello.obj Microsoft (R) Incremental Linker Version 5.12.8078 Copyright (C) Microsoft Corp 1992-1998. All rights reserved. 

Аргумент /subsystem:console говорит компоновщику, что нужно построить консольное Windows-приложение.

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

C:\work>hello Hello World Hello World Hello World Hello World Hello World 

Как это устроено

Командный файл prompt.bat запускает окно командной строки и задает переменные окружения так, чтобы программы ml и link были доступны без указания пути к ним, а пути к include- и lib-файлам MASM также были известны.

Пути заданы жестко, поэтому и требовалось распаковать архив в строго определенный каталог.

Командный файл для упрощения запуска

Когда вам надоест каждый раз набирать три команды для трансляции и запуска программ, создайте такой командный файл (назвать его можно, например, mkr.bat — то есть make/run):

@echo off if .%1.==.. goto USAGE ml /nologo /c /coff %1 && link /nologo /subsystem:console %~n1.obj && %~n1 goto END :USAGE echo usage: mkr program.asm :END 

Использовать его можно будет следующим образом:

C:\work>mkr hello.asm Assembling: hello.asm Hello World Hello World Hello World Hello World Hello World 

Несколько комментариев по устройству этого командного файла:

  • Команда @echo off отключает дублирование каждой исполняемой команды в окне командной строки.
  • Аргумент /nologo при вызове ассемблера и компоновщика убирает строчку “Copyright (C) Microsoft”, захламляющую экран.
  • %1 меняется на аргумент, который передан командному файлу, то есть имя программы на ассемблере (выше — hello.asm ).
  • %~n1 меняется на тот же аргумент, но без расширения (выше — hello ).
  • Связка && выполняет очередную команду, только если предыдущая завершилась успешно. В случае ошибок трансляции ваша программа запущена не будет.

Файл mkr.bat можно или копировать в каждый каталог, где вы планируете размещать исходные тексты программ на ассемблере, или поместить его в каталог C:\masm32\bin , и тогда выполнять его будет можно из любого каталога, при условии, что вы запустили командную строку при помощи prompt.bat .

Итог

То, что получилось в итоге — это простейшая система программирования, состоящая из транслятора (ассемблера MASM), текстового редактора (Notepad2 или иного, если вы его предпочли) и примитивной системы сборки на единственном командном файле.

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

Как запустить программу на ассемблере

NASM является кроссплатформенным ассемблером, который доступен в том числе и на Windows. Рассмотрим, как использовать NASM на Windows.

Установка

Для работы с NASM нам надо установить непосредственно сам ассемблер. Для этого на официальном сайте перейдем на страницу https://www.nasm.us/pub/nasm/releasebuilds/2.16.01/win64/, где находятся файлы ассемблера NASM версии 2.16.01 для 64-разрядной версии Windows:

Установка NASM на Windows

Здесь нам доступен ассемблер в виде двух пакетов. Один пакет установщика ( nasm-2.16.01-installer-x64.exe ), а второй — архив ( nasm-2.16.01-win64.zip). Загрузим zip-архив. . Например, загрузим zip-архив и после загрузки распакуем его. В папке распакованного архива мы можем найти два файла

ассемблер NASM на Windows

Это прежде всего сам ассемблер — файл nasm.exe и дизассемблер — файл ndisasm.exe

Чтобы не прописывать весь путь к ассемблеру, занесем его в переменные среды. Для этого можно в окне поиска в Windows ввести «изменение переменных среды текущего пользователя»:

изменение переменных среды текущего пользователя в Windows

Нам откроется окно Переменныех среды:

Добавление GCC в переменные среды на Windows

И добавим путь к ассемблеру. Например, в моем случае архив ассемблера распакован в папку C:\nasm-2.16.01 , соответственно я указываю в переменной Path среды эту папку:

ассемблер NASM на Windows

Если все настроено правильно, то мы можем запустить командную строку и с помощью команды nasm -v узнать текущую версию ассемблера:

C:\Users\eugen>nasm -v NASM version 2.16.01 compiled on Dec 21 2022 C:\Users\eugen>

Начало работы с NASM

Определим в файловой системе каталог для файлов с исходным кодом и создадим в нем следующий файл hello.asm :

global _start ; делаем метку метку _start видимой извне section .text ; объявление секции кода _start: ; метка _start - точка входа в программу mov rax, 22 ; произвольный код возврата - 22 ret ; выход из программы

Рассмотрим поэтапно данный код. Вначале идет директива global :

global _start

Данная директива делает видимой извне определенную метку программы. В данном случае метку _start , которая является точкой входа в программу. Благодаря этому компоновщик при компоновке программы в исполняемый файл сможет увидеть данную метку.

Затем идет секция кода программы, которая и определяет выполняемые программой действия. Для определения секции применяется директива section , после которой указывается имя секции. Причем секция кода программы должна называться .text .

section .text

Далее собственно идет код программы. И он начинается с определения метки _start , на которую собственно и проецируется программа. Сама по себе метка представляет произвольное название, после которого идет двоеточие. После двоеточия могут идти инструкции программы или определения данных.

Метка _start выступает в качестве точки входа в программу. Название подобной метки произвольное, но обычно это или _start или _main

Наша программа не производит какой-то феноменальной работы. Все что она делает — это помещает в регистр rax число 22 и завершается. Для помещения числа в регистр применяется инструкция mov :

mov rax, 22

Инструкция mov помещает в первый операнд — регистр rax значение из второго операнда — число 22.

Затем идет вызов инструкции ret , которая завершает программу

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

global _start ; делаем метку метку _start видимой извне - это текст комментария

Компиляция

Для компиляции откроем командную строку, перейдем в ней к папке с исходным кодом (где располагается файл hello.asm ) и выполним следующую команду

nasm -f win64 hello.asm -o hello.o

Здесь с помощью опции -f указывается формат файла, в который мы хотим скомпилировать код. Для 64-разрядной ОС Windows это — win64 . После этого указывается файл, который мы хотим скомпилировать — наш файл hello.asm . Затем опция -o hello.o указывает на имя скомпилированного файла. В результате выполнения этой команды будет создан объектный файл hello.o

Однако файл hello.o — это объектный файл, а не исполняемый. Он содержит машинный код, который понимает компьютер, но чтобы его можно было запускать как обычную программу, его надо скомпоновать в исполняемый файл. И для этого нужна программа компоновщика (он же линковщик/линкер или linker ). Недостатком NASM является то, что он не предоставляет встроенного компоновщика. И нам надо использовать внешнюю программу компоновки. Где ее взять? Далее я рассмотрю два варианта — использование компоновщика из пакета GCC и использование компоновщика компилятора Visual C/C++, который идет вместе с Visual Studio. Оба варианта равноценны.

Компоновка с помощью GCC

Вначале нам надо установить пакет GCC. Пакет компиляторов GCC для Windows не имеет какого-то одного единого разработчика, разные организации могут выпускать свои сборки. Для Windows одной из наиболее популярных версий GCC является пакет средств для разработки от некоммерческого проекта MSYS2 . Следует отметить, что для MSYS2 требуется 64-битная версия Windows 7 и выше (то есть Vista, XP и более ранние версии не подходят)

Итак, загрузим программу установки MSYS2 с официального сайта MSYS2:

Установка MSYS для разработки под С

После загрузки запустим программу установки:

Установка пакета mingw-w64 и msys2 на Windows

На первом шаге установки будет предложено установить каталог для установки. По умолчанию это каталог C:\msys64:

Установка компиляторов Си MSYS2 на Windows

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

Установка компиляторов MSYS2 на Windows

После завершения установки запустится консольное приложение MSYS2.exe. Если по каким-то причинам оно не запустилось, то в папке установки C:/msys64 надо найти файл usrt_64.exe :

компиляторы MSYS2.exe на Windows

Теперь нам надо установить собственно набор компиляторов GCC. Для этого введем в этом приложении следующую команду:

pacman -S mingw-w64-ucrt-x86_64-gcc

Для управления пакетами MSYS2 использует пакетный менеджер Packman. И данная команда говорит пакетному менелжеру packman установить пакет mingw-w64-ucrt-x86_64-gcc , который представляет набор компиляторов GCC (название устанавливаемого пакета указывается после параметра -S ).

Установка компиляторов MSYS2 на Windows

Если после завершения установки мы откроем каталог установки и зайдем в нем в папку C:\msys64\ucrt64\bin , то найдем там файл компоновщика ld

GNU ассемблер на Windows

Для упрощения запуска компоновщика мы можем добавить путь к нему в Переменные среды:

Определение пути к компилятору GCC в переменных среды на Windows

Чтобы убедиться, что нам доступен компоновщик GCC — программа ld , введем следующую команду:

ld --version

В этом случае нам должна отобразиться версия компоновщика

c:\asm>ld --version GNU ld (GNU Binutils) 2.40 Copyright (C) 2023 Free Software Foundation, Inc. This program is free software; you may redistribute it under the terms of the GNU General Public License version 3 or (at your option) a later version. This program has absolutely no warranty. c:\asm>

Теперь скомпонуем файл hello.o в исполняемый файл hello.exe с помощью следующей команды:

ld hello.o -o hello.exe

Мы можем запустить этот файл, введя в консоли его название и нажав на Enter. Но наша программа ничего не выводит на консоль, поэтому после запуска программы мы ничего не увидим. Тем не менее наша программа устанавливает регистр RAX. А значение этого регистра при завершении программы рассматривается в Windows как статусный код возврата, который сигнализирует, как завершилась программа (успешно или не успешно). И мы можем этот статусный код получить, если после выполнения программы введем команду

echo %ERRORLEVEL%

И нам должно отобразится число 22 — значение регистра RAX. Полный консольный вывод:

c:\asm>nasm -f win64 hello.asm -o hello.o c:\asm>ld hello.o -o hello.exe c:\asm>hello.exe c:\asm>echo %ERRORLEVEL% 22 c:\asm>
Установка компоновщика link.exe

Компоновщик от GCC — не единственный компоновщик, который можно использовать для компоновки программы на Windows. Еще один вариант представляет компоновщик link.exe из пакета инструментов разработки для C/C++ для Visual Studio. Условным плюсом этого компоновщика может быть то, что его разработчик — Microsoft, поэтому можно ожидать более лучшей интеграции с Windows. Поэтому также рассмотрим и этот способ.

Сперва нам надо установить для Visual Studio инструменты разработки для C/C++. Установщик для среды Visual Studio можно загрузить по следующему адресу: Microsoft Visual Studio 2022. После загрузки программы установщика Visual Studio запустим ее и в окне устанавливаемых опций выберем пункт Разработка классических приложений на C++ :

Установка MASM 64 в Windows

И нажмем на кнопку установки.

В зависимости от конкретной подверсии и номера сборки Visual Studio точное расположение файлов может варьироваться. Например, в моем случае это папка C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.37.32822\bin\Hostx64\x64\ . И в этой папке можно найти программу компоновщика link.exe . Причем при обновлениях Visual Studio этот расположение может измениться, так как при обновлении меняется и версия Visual Studio. Поэтому к конкретным путям можно не цепляться. Вместо этого мы можем перейти к меню Пуск и в списке программ найти пункт Visual Studio и подпункт x64 Native Tools Command Prompt for VS 2022

Build Tools for Visual Studio 2022 и MASM64 в Windows

Нам должна открыться консоль. Введем в нее link , и нам отобразится версия ассемблера и некоторая дополнительная информация:

********************************************************************** ** Visual Studio 2022 Developer Command Prompt v17.7.4 ** Copyright (c) 2022 Microsoft Corporation ********************************************************************** [vcvarsall.bat] Environment initialized for: 'x64' C:\Program Files\Microsoft Visual Studio\2022\Community>link Microsoft (R) Incremental Linker Version 14.37.32824.0 Copyright (C) Microsoft Corporation. All rights reserved. usage: LINK [options] [files] [@commandfile] options: /ALIGN:# /ALLOWBIND[:NO] /ALLOWISOLATION[:NO] /APPCONTAINER[:NO] /ASSEMBLYDEBUG[:DISABLE] /ASSEMBLYLINKRESOURCE:filename /ASSEMBLYMODULE:filename /ASSEMBLYRESOURCE:filename[,[name][,PRIVATE]] /BASE: /CLRIMAGETYPE: /CLRLOADEROPTIMIZATION: /CLRSUPPORTLASTERROR[:] /CLRTHREADATTRIBUTE: /CLRUNMANAGEDCODECHECK[:NO] /DEBUG[:] .

В частности, можно увидеть, что версия компоновщика — 14.37.32824.0 и все опции, которые можно передать программе при компоновке. Стоит отметить, что запуск этой этой утилиты фактически представляет запуск файла C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvars64.bat — он по сути вызывает другой файл — vcvarsall.bat, который собственно и настраивает окружение для выполнения ассемблера.

Используем этот компоновщик. Для этого откроем программу x64 Native Tools Command Prompt for VS 2022 и перейдем в ней к папке, где располагается объектный файл hello.o . Затем выполним следующую команду

link hello.o /entry:_start /subsystem:console /out:hello2.exe

В данном случае компоновщику передаем ряд параметров:

  • собственно объектный файл hello.o , который будет компилироваться в исполняемое приложение
  • Параметр /entry:_start указывает компоновщику на точку входа в программу — это наша метка «_start».
  • Параметр /subsystem:console указывает компоновщику, что создается консольное (не графическое) приложение.
  • Параметр /out:hello2.exe устанавливает имя генерируемого файла приложение — оно будет называться «hello2.exe».

В результате будет создан файл hello2.exe , который мы также можем запускать:

c:\asm>link hello.o /entry:_start /subsystem:console /out:hello2.exe Microsoft (R) Incremental Linker Version 14.37.32824.0 Copyright (C) Microsoft Corporation. All rights reserved. c:\asm>hello2.exe c:\asm>echo %ERRORLEVEL% 22 c:\asm>

Создание первой программы на Windows

Теперь создадим более осмысленную программу, которая выводит на экран строку,kernel32.lib и для этого изменим файл hello.asm следующим образом:

global _start ; делаем метку метку _start видимой извне extern WriteFile ; подключем функцию WriteFile extern GetStdHandle ; подключем функцию GetStdHandle section .data ; секция данных message: db "Hello METANIT.COM!",10 ; строка для вывода на консоль section .text ; объявление секции кода _start: ; метка _start - точка входа в программу sub rsp, 40 ; Для параметров функций WriteFile и GetStdHandle резервируем 40 байт (5 параметров по 8 байт) mov rcx, -11 ; Аргумент для GetStdHandle - STD_OUTPUT call GetStdHandle ; вызываем функцию GetStdHandle mov rcx, rax ; Первый параметр WriteFile - в регистр RCX помещаем дескриптор файла - консоли mov rdx, message ; Второй параметр WriteFile - загружаем указатель на строку в регистр RDX mov r8d, 18 ; Третий параметр WriteFile - длина строки для записи в регистре R8D xor r9, r9 ; Четвертый параметр WriteFile - адрес для получения записанных байтов mov qword [rsp + 32], 0 ; Пятый параметр WriteFile call WriteFile ; вызываем функцию WriteFile add rsp, 40 ret ; выход из программы

Разберем эту программу. Для вывода строки на консоль нам надо использовать нативные функции GetStdHandle и WriteFile . И чтобы воспользоваться этими функциями подключаем их с помощью директивы extern

extern WriteFile extern GetStdHandle

Далее идет определение секции данных — секции .data :

section .data ; секция данных message: db "Hello METANIT.COM!",10 ; строка для вывода на консоль

В секции .data определена метка message , на которую проецируется строка. По сути message — это переменная, которая представляет строку. После метки указывается тип данных. Строка в ассемблере — это просто набор байтов, поэтому имеет тип db . Затем в кавычках определяется собственно выводимая строка — «Hello METANIT.COM!»,10 . Обратите внимание на 10 после строки — это код символа перевода строки. То есть при выводе мы ожидаем, что будет происходить перевод на другую строку.

Затем идет секция кода — секция .text и метка _start — точка входа в программу

section .text ; объявление секции кода _start: ; метка _start - точка входа в программу

В программе для вызова функций первым делом необходимо настроить стек. В частности, резервируем в стеке 40 байт для параметров функций GetStdHandle и WriteFile и при этом учитываем выравнивание стека по 16-байтной границе. Указатель на верхушку стека хранится в регистре rsp . Поэтому вычитаем с помощью инструкции sub из значения в регистре rsp 40 байт

sub rsp, 40

Почему 40? Прежде всего при вызове функций WinAPI (как в данном случае функций GetStdHandle и WriteFile) необходимо зарезервировать в стеке как минимум 32 байта — так называемое «shadow storage» (теневое хранилище). Далее нам надо учитывать количество параметров функции. Пеовые 4 параметра функций передаются через регистры, а параметры начиная с 5-го передаются через стек. Соответственно для 5-го и последующих параметров надо выделить в стеке область. Для каждого параметра вне зависимости от его размера выделяется 8 байт. Функция WriteFile как раз принимает 5 параметров, поэтому для нее надо выделить дополнительные 8 байт в стеке. Поэтому получаем 32 байта + 8 байт (5-й параметр WriteLine) = 40 байт. Количество параметров смотрим по функции с наибольшим количеством параметров. Третий момент — нам надо учитывать, что перед вызовом функций WinAPI стек имел выравнивание по 16 байтовой границе, то есть значение в RSP должно быть кратно 16. По умолчанию при вызове функции в стек помещается адрес возврата функии размером 8 байт. Поэтому наши 40 байт + 8 байт (адрес возврата из функции) дадут 48 байт — число кратное 16.

Вначале нам надо использовать встроенную функцию GetStdHandle() , которая позволяет получить дескриптор на устройство ввода-вывода. Она имеет следующее определение на C:

HANDLE WINAPI GetStdHandle( _In_ DWORD nStdHandle );

Функция GetStdHandle() получает числовой код устройства, с которым мы хотим взаимодействовать. В нашем случае нам надо получить устройство стандартного вывода (для вывода строки), которым по умолчанию является консоль. Для обращения к консоли надо передать число -11, которое надо поместить в регистр rcx :

mov rcx, -11 ; Аргумент для GetStdHandle - STD_OUTPUT

После установки параметра этой функции вызываем ее с помощью инструкции call :

call GetStdHandle

В результате выполнения функция GetStdHandle возвращает дескриптор — объект, через который мы можем взаимодействовать с консолью. Этот дескриптор помещается в регистр rax . Получив этот дескриптор, используем его для вывода на консоль строки с помощью функции WriteFile . Для справки ее определение на С++

BOOL WriteFile( [in] HANDLE hFile, [in] LPCVOID lpBuffer, [in] DWORD nNumberOfBytesToWrite, [out, optional] LPDWORD lpNumberOfBytesWritten, [in, out, optional] LPOVERLAPPED lpOverlapped );

Вызов функции GetStdHandle помещает в регистр rax дескриптор консоли. И для вывода строкии на консоль с помощью функции WriteFile нам надо поместить этот дескриптор в регистр rcx

mov rcx, rax

Затем также с помощью инструкции mov загружаем в регистр rdx адрес выводимой строки

mov rdx, message

Далее в регистр r8d помещаем длину выводимой строки в байтах — в данном случае это 18 байт:

mov r8d, 18

Поскольку у нас строка с символами ASCII, и каждый символ эквивалентен 1 байту, то получаем, что в строке message с учетом последнего символа с числовым кодом 10 будет 18 байт.

Затем в регистре r9 устанавливаем адрес четвертого параметра функции WriteFile:

xor r9, r9

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

Последний — пятый параметр функции WriteFile должен иметь значение NULL, по сути 0. Поэтому устанавливаем для него значение 0, смещаясь в стеке вперед на 32 байта (4 параметра * 8):

mov qword [rsp + 32], 0

Инструкция mov помещает значение в определенное место. Здесь в качестве значения служит число 0. А место определяется выражением qword [rsp + 32] . qword указывает, что этот операнд описывает адрес размером в четыре слова, что означает 8 байтов (слово имеет длину 2 байта). То есть число 0 представляет 8-байтное значение и помещается по адресу rsp + 32 .

И далее собственно вызываем функцию WriteFile:

call WriteFile

Этот вызов должен привести к выводу строки на консоль. После этого восстанавливаем значение верхушки стека. Для этого с помощью инструкции add прибавляем к значению в регстре rsp ранее отнятые 40 байт:

add rsp, 40

И с помощью инструкции ret выходим из программы.

Компиляция

Поскольку теперь программа использует внешние функции WinApi — GetStdHandle и WriteFile, которые определены во внешней библиотеке kernel32.lib , то при компоновке нам надо подключить эту библиотеку. В зависимости от используемого компоновщика/линкера этот процесс может немного отличаться. Например, при использовании компоновщика ld из комплекта инструментов GCC все подключаемые библиотеки передаются с помощью опции -l :

ld hello.o -o hello.exe -l kernel32

Здесь последний параметр -l kernel32 как раз указывает, какую библиотеку надо подлючить, при чем название библиотеки указывается без расширения файла.

При использовании компоновщика link.exe от Microsoft подключаемая библиотека просто передается вместе с компонуемыми файлами:

link hello.o kernel32.lib /entry:_start /subsystem:console /out:hello2.exe

Итак, повторно скомпилируем файл и скомпонуем одним из компоновщиков. Затем запустим полученный исполняемый файл, и консоль должна вывести нам строку. Полный процесс при использовании компоновщика ld из комплекта GCC:

c:\asm>nasm -f win64 hello.asm -o hello.o c:\asm>ld hello.o -o hello.exe -l kernel32 c:\asm>hello.exe Hello METANIT.COM! c:\asm>

Полный процесс при использовании компоновщика link от Microsoft (компоновка выполняется в программе x64 Native Tools Command Prompt for VS 2022 ):

c:\asm>nasm -f win64 hello.asm -o hello.o c:\asm>link hello.o kernel32.lib /entry:_start /subsystem:console /out:hello2.exe Microsoft (R) Incremental Linker Version 14.37.32824.0 Copyright (C) Microsoft Corporation. All rights reserved. c:\asm>hello2.exe Hello METANIT.COM! c:\asm>

Assembler. Установка интерпретатора и запуск первой программы через DOSBox

В данной статье разбирается способ установки интерпретатора и запуск файла EXE через DOSBox. Планировалось погрузить читателя в особенности программирования на TASM, но я согласился с комментаторами. Есть много учебников по Ассемблер и нет смысла перепечатывать эти знания вновь. Лично мне в изучении очень помог сайт av-assembler.ru. Рекомендую. В комментариях также вы найдёте много другой литературы по Assembler. А теперь перейдём к основной теме статьи.

Для начала давайте установим наш старенький интерпретатор.
Ссылка

Почему именно vk.com?

Я прекрасно понимаю, что это ещё тот колхоз делиться файлами через обсуждения VK, но кто знает, во что может превратиться эта маленькая группа в будущем.

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

Для запуска интерпретатора нам так же потребуется эмулятор DOSBox. Он и оживит все наши компоненты. Скачаем и установим его!
Ссылка

В папке Asm я специально оставил файл code.asm. Именно на нём мы и потренируемся запускать нашу программу. Советую сохранить его копию, ибо там хранится весь код, который в 99% случаев будет присутствовать в каждом вашем проекте.

s_s segment s_s ends d_s segment d_s ends c_s segment assume ss:s_s, ds:d_s, cs:c_s begin: mov ax, d_s mov ds, ax mov ax, 0 ; Your code needs to be here mov ah, 4ch int 21h c_s ends end begin

Итак. Запускаем наш DOSBox и видим следующее:

Для простоты сопоставим имя пути, по которому лежит наша папка Asm. Чтобы это сделать, пропишем следующую команду:

mount d: c:\asm

Здесь вместо d: мы можем использовать любую другую букву. Например назвать i или s. А C это наш реальный диск. Мы прописываем путь до наших файлов ассемблера.

Теперь, откроем смонтированный диск:

Прописав команду dir, мы сможем увидеть все файлы, которые там хранятся. Здесь можно заметить и наш файл CODE с расширением ASM, а также дату его создания.

И только теперь мы начинаем запускать наш файл! Бедные программисты 20 века, как они только терпели всё это? Пропишем следующую команду:

tasm code.asm

После мы увидим следующее сообщение, а наша директория пополнится новым файлом с расширением OBJ.

Теперь пропишем ещё одну команду:

tlink code.obj

В нашей папке появилась ещё пара файлов – CODE.MAP и CODE.EXE. Последний как раз и есть исполняемый файл нашего кода assembler.

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

td code

Этот старинный интерфейс насквозь пропитан духом ушедшей эпохи старых операционных систем. Тем не менее…

Нажав F7 или fn + F7 вы сможете совершить 1 шаг по коду. Синяя строка начнёт движение вниз, изменяя значения регистров и флагов. Пока это всего лишь шаблон, на котором мы потренировались запускать нашу программу в режиме дебага. Реальное “волшебство” мы увидим лишь с полноценным кодом на asm.

Небольшой пример для запуска

Прога проверяет, было ли передано верное число открывающих и закрывающих скобок:

s_s segment dw 20 dup('$') s_s ends d_s segment string db '()','$'; result db 0 d_s ends c_s segment assume ss:s_s,ds:d_s,cs:c_s begin: ; начало программы mov ax,d_s mov ds,ax xor ax,ax lea si, string ;Ищем в строке скобку search: lodsb ;Проверка, это конец строки? cmp al, '$' je endString ;Это открывающая или закрывающая скобка? ;Это открывающие скобки? cmp al, '(' je inStack cmp al, '' je outStack cmp al, ']' je outStack jmp search ;Помещаем скобку в Stack, увеличиваем счётчик inStack: inc cx push ax jmp search ;Выниманием из Stack скобку, проверяем пару outStack: ;Была передана лишняя закрыв. скобка? cmp cx, 0 je error3 dec cx pop bx ;Вскрытая скобка закрыта верно? cmp bl, '(' jne close1 cmp al, ')' jne error1 jmp search close1: cmp bl, '[' jne close2 cmp al, ']' jne error1 jmp search close2: cmp bl, '' jne error1 jmp search ;Остались ли незакрытые скобки? endString: cmp cx, 0 jne error2 jmp exit ;Скобки остались, это ошибка №2 error2: mov result, 2 jmp exit ;Лишняя скобка передана, ошибка №3 error3: mov result, 3 jmp exit ;Закрывающая скобка несоответствует открывающей, ош №1 error1: mov result, 1 jmp exit ;Пред-завершение. Каков результат программы? exit: cmp result, 1 jne enough ;Ищем нужную скобку для исправления ошибки №1 cmp bl, '(' jne next1 mov bl, ')' jmp enough next1: cmp bl, '' jmp enough next2: cmp bl, '[' mov bl, ']' jmp enough enough: mov dl, result xor dx, dx mov dl, bl mov ah,4ch int 21h c_s ends end begin

Давайте ознакомимся с имеющимися разделами.

CS

Code segment – место, где turbo debug отражает все найденные строки кода. Важное замечание – все данные отражаются в TD в виде 16-ричной системы. А значит какая-нибудь ‘12’ это на самом деле 18, а реальное 12 это ‘C’. CS аналогичен разделу “Begin end.” на Pascal или функции main.

DS

Data segment, отражает данные, которые TD обнаружил в d_s. Справа мы видим их символьную (char) интерпретацию. В будущем мы сможем увидеть здесь наш “Hello, world”, интерпретируемый компилятором в числа, по таблице ASCII. Хорошей аналогией DS является раздел VAR, как в Pascal. Для простоты можно сказать, что это одно и тоже.

SS

Stack segment – место хранения данных нашего стека.

Регистры

Все эти ax, bx, cx, si, di, ss, cs и т. д. – это наши регистры, которые используются как переменные для хранения данных. Да, это очень грубое упрощение. Переменные из Pascal и регистры Assembler это не одно и тоже, но надеюсь, такая аналогия даёт более чёткую картину. Здесь мы сможем хранить данные о циклах, арифметических операциях, системных прерываниях и т. д.

Флаги

Все эти c, z, s, o, p и т.д. это и есть наши флаги. В них хранится промежуточная информация о том, например, было ли полученное число чётным, произошло ранее переполнение или нет. Они могут хранить результат побитого сдвига. По опыту, могу сказать, на них обращаешь внимание лишь при отладке программы, а не во время штатного исполнения.

Ещё одно замечание. Если вы измените данные исходного файла с расширением .ASM, то вам придётся совершить все ранее описанные операции вновь, ибо обновив например code.asm вы не меняете code.obj или code.exe.

Маленькая шпаргалка для заметок:

  1. mount d: c:\asm – создаём виртуальный диск, где корень –папка asm
  2. d: — открываем созданный диск
  3. tasm code.asm – компилируем исходный код
  4. tlink code.obj – создаём исполняемый файл
  5. td code – запускаем debug
  6. F7 – делаем шаг в программе

Буду ждать комментарии от всех, кому интересен Assembler. Чувствую, я где-то мог накосячить в терминологии или обозначении того или иного элемента. Но статья на Habr отличный повод всё повторить.

Выполнение программы

Если у вас всё получилось, то у вас появилась программа atest.com. Теперь её можно запустить на выполнение. Делается это обычным способом для операционной системы. Для ДОС – из командной строки. Для Windows – двойным щелчком по значку программы или также из командной строки.

Однако если вы работаете в Windows, то вы не успеете увидеть результат работы программы, так как окно сразу же закроется после её выполнения. Чтобы этого не произошло, щёлкните правой кнопкой по файлу atest.com и в контекстном меню выберите СВОЙСТВА. В открывшемся окне перейдите на вкладку ПРОГРАММА и снимите галочку «Закрывать окно по завершении работы» (в зависимости от версии Windows эта процедура может немного отличаться).

После того, как выполнение программы завершится, мы увидим на экране то, что и должны были увидеть – английскую букву А (рис. 1.9). Можете немного поэкспериментировать и вместо кода буквы «А» (41h) записать другой код (41h, 42h и т.п. вплоть до 0FFh).

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *