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

Добавил пользователь Владимир З.
Обновлено: 04.10.2024

  1. Постановка задачи. Написать программу, которая выводит на экран строчку "Привет!".
  2. Разработка алгоритма программы. Алгоритм линейный, разработки не требует.
  3. Формализация (запись) алгоритма
    В текстовом редакторе создаем файл privet.asm и записываем в него следующий код (без номеров строк) :

Описание программы privet.asm

Строки 1 - 3 программы privet.asm содержат описание сегмента данных. Сегмент данных - область память, в которой будут храниться данные для наших программ.
Строки 5 - 17 — это код программы, её исполняемая часть.
В 8 и 9 строках выполняется настройка сегмента данных программы.
Строки 11 - 13 — вывод строки на экран при помощи функции №9 прерывания 21h (подробнее о функциях и работе с ними на следующей лабораторной работе).
15 и 16 строки — стандартное завершение программы.
После символа ';' пишутся комментарии, они не обрабатываются компилятором.

Переход на новую строку

Задание для выполнения

Если что-то не так, то доредактируем файл. После этого dosbox будет сконфигурирован.

Сегментация программы

Stack (стек)

Добавление элемента, называемое также проталкиванием (push), возможно только в вершину стека (добавленный элемент становится первым сверху). Удаление элемента, называемое также выталкивание (pop), возможно также только из вершины стека, при этом, второй сверху элемент становится верхним.

Итак, стек - это тот же сегмент данных. На вершину стека указывает адрес ss:sp. В 8086 процессоре есть несколько команд, которые меняют содержимое стека, мы их рассмотрим позднее.

Написание программ на ассемблере

ВНИМАНИЕ.

Имя файла в MS-DOS и в DOSBox не может превышать восьми символов.

Главная особенность com-программ: cs, ds, es и ss указывают на один и тот же сегмент, тем самым смешивая всё в одну кучу! При компиляции tasm тщательно следит за тем, чтобы вы не меняли содержимого системных регистров. Так как на всю программу выделен всего один сегмент, то максимальный размер программы: 64 КБайт. Обычно этот тип файлов используется в качестве драйверов для MS-DOS.

Программы типа *.exe

В exe-программе под код данные и стек может отводится несколько сегментов. Компилятор никак не отслеживает изменение системных регистров, в этом варианте вы можете делать с компьютером всё, что хотите (или можете).

Программа на ассемблере

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

Команды

где опкод (код операции) — непосредственно мнемоника инструкции процессору. К ней могут быть добавлены префиксы (повторения, изменения типа адресации и пр.). Метка (если имеется), команда и операнд (если имеется) pазделяются по крайней мере одним пробелом или символом табуляции. Максимальная длина строки - 132 символа, однако, большинство предпочитают работать со строками в 80 символов (соответственно ширине экрана). Примеры кодирования:

Метки

Метка в языке ассемблера может содержать следующие симво лы:

Первым символом в метке должна быть буква или спецсимвол. Ассемблер не делает различия между заглавными и строчными буквами. Максимальная длина метки - 31 символ. Примеры меток: COUNT, PAGE25, $E10. Рекомендуется использовать описательные и смысловые метки. Имена регистров, например, AX, DI или AL являются зарезервированными и используются только для указания соответствующих регистров. Например, в команде

ассемблер воспримет имя REGSAVE только в том случае, если оно будет определено в сегменте данных. В приложении 3 приведен cписок всех зарезервированных слов ассемблера.

Команда

Мнемоническая команда указывает ассемблеру какое действие должен выполнить данный оператор. В сегменте данных команда (или директива) определяет поле, рабочую oбласть или константу. В сегменте кода команда определяет действие, например, пересылка (MOV) или сложение (ADD).

Операнд

Если команда специфирует выполняемое действие, то операнд определяет а) начальное значение данных или б) элементы, над которыми выполняется действие по команде. В следующем примере байт COUNTER определен в сегменте данных и имеет нулевое значение:

Команда может иметь один или два операнда, или вообще быть без операндов. Рассмотрим следующие три примера:

Метка, команда и операнд не обязательно должны начинаться с какой-либо определенной позиции в строке. Однако, рекомен дуется записывать их в колонку для большей yдобочитаемости программы. Для этого, например, редактор DOS EDLIN обеспечи вает табуляцию чепез каждые восемь позиций.

Комментарии

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

Директивы

всевозможные абстракции (то есть элементы языков высокого уровня) — от оформления процедур и функций (для упрощения реализации парадигмы процедурного программирования) до условных конструкций и циклов (для парадигмы структурного программирования),

Директивы управления листингом: PAGE и TITLE

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

Директива PAGE

В начале программы можно указать количест во строк, распечатываемых на одной странице, и максимальное количество символов на одной строке. Для этой цели cлужит директива PAGE. Следующей директивой устанавливается 60 строк на страницу и 132 символа в строке:

Количество строк на странице межет быть в пределах от 10 до 255, а символов в строке - от 60 до 132. По умолчанию в ассемблере установлено PAGE 66,80. Предположим, что счетчик строк установлен на 60. В этом случае ассемблер, распечатав 60 строк, выполняет прогон листа на начало следующей страницы и увеличивает номер страницы на eдиницу. Кроме того можно заставить ассемблер сделать прогон листа на конкретной строке, например, в конце сегмента. Для этого необходимо записать директиву PAGE без операндов. Ассемблер автоматически делает прогон листа при обработке диpективы PAGE.

Директива TITLE

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

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

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

Директива SEGMENT

Любые ассемблерные программы содержат по крайней мере один сегмент - сегмент кода. В некоторых программах используется сегмент для стековой памяти и сегмент данных для определения данных. Асcемблерная директива для описания сегмента SEGMENT имеет следующий формат:

Имя сегмента должно обязательно присутствовать, быть уникальным и соответствовать соглашениям для имен в ассемблере. Директива ENDS обозначает конец сегмента. Обе директивы SEGMENT и ENDS должны иметь одинаковые имена. Директива SEGMENT может содержать три типа параметров, определяющих выравнивание, объединение и класс.

Выравнивание

Данный параметр определяет границу начала сегмента. Обычным значением является PARA, по которму сегмент устанавливается на границу параграфа. В этом случае начальный адрес делится на 16 без остатка, т.е. имеет шест. адрес nnn0. В случае отсутствия этого операнда ассемблер принимает по умолчанию PARA.

Объединение

Когда отдельно ассемблированные программы должны объединяться компановщиком, то можно использовать типы: PUBLIC, COMMON и MEMORY. В случае, если программа не должна объединяться с другими программами, то данная опция может быть опущена.

Класс

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

Фрагмент программы на рис. 3.1. в следующем разделе иллюстрирует директиву SEGMENT и ее различные опции.

Директива PROC

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

Имя процедуры должно обязательно присутствовать, быть уникальным и удовлетворять соглашениям по именам в ассембле ре. Операнд FAR указывает загрузчику DOS, что начало данной процедуры является точкой входа для выполнения программы. Директива ENDP определяет конец процедуры и имеет имя, аналогичное имени в директиве PROC. Команда RET завершает выполнение программы и в данном случае возвращает управление в DOS. Сегмент может содержать несколько процедур.

Директива ASSUME

Процессор использует регистр SS для адресации стека, ркгистр DS для адресации сегмента данных и регистр CS для адресации cегмента кода. Ассемблеру необходимо сообщить назначение каждого сегмента. Для этой цели служит директива ASSUME, кодируемая в сегменте кода следующим образом:

Например, SS:имя_стека указывает, что ассемблер должен ассоциировать имя сегмента стека с регистром SS. Операнды могут записываться в любой последовательности. Регистр ES также может присутствовать в числе операндов. Если программа не использует регистр ES, то его можно опустить или указать ES:NOTHING.

Директива END

Как уже показано, директива ENDS завершает сегмент, а директива ENDP завершает процедуру. Директива END в свою очередь полностью завершает всю программу:

Операнд может быть опущен, если программа не предназначе на для выполнения, например, если ассемблируются только определения данных, или эта программа должна быть скомпанована с другим (главным) модулем. Для обычной программы с одним модулем oперанд содержит имя, указанное в директиве PROC, которое было oбозначено как FAR.

ассемблер

Основы ассемблера

Я буду исходить из того, что ты уже знаком с программированием — знаешь какой-нибудь из языков высокого уровня (С, PHP, Java, JavaScript и тому подобные), тебе доводилось в них работать с шестнадцатеричными числами, плюс ты умеешь пользоваться командной строкой под Windows, Linux или macOS.

Если наборы инструкций у процессоров разные, то на каком учить ассемблер лучше всего?

Знаешь, что такое 8088? Это дедушка всех компьютерных процессоров! Причем живой дедушка. Я бы даже сказал — бессмертный и бессменный. Если с твоего процессора, будь то Ryzen, Core i9 или еще какой-то, отколупать все примочки, налепленные туда под влиянием технологического прогресса, то останется старый добрый 8088.

SGX-анклавы, MMX, 512-битные SIMD-регистры и другие новшества приходят и уходят. Но дедушка 8088 остается неизменным. Подружись сначала с ним. После этого ты легко разберешься с любой примочкой своего процессора.

Больше того, когда ты начинаешь с начала — то есть сперва выучиваешь классический набор инструкций 8088 и только потом постепенно знакомишься с современными фичами, — ты в какой-то миг начинаешь видеть нестандартные способы применения этих самых фич.

Что и как процессор делает после запуска программы

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

Некоторые инструкции занимают один байт памяти, другие два, три или больше. Они выглядят как-то так:

Вернее, даже так:

Хотя погоди! Только машина может понять такое. Поэтому много лет назад программисты придумали более гуманный способ общения с компьютером: создали ассемблер.

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

Согласись, такое читать куда легче. Хотя, с другой стороны, если ты видишь ассемблерный код впервые, такая мнемоника для тебя, скорее всего, тоже непонятна. Но мы сейчас это исправим.

Регистры процессора: зачем они нужны, как ими пользоваться

Что делает инструкция mov ? Присваивает число, которое указано справа, переменной, которая указана слева.

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

Когда программируешь на ассемблере, ты сам решаешь, какие переменные хранить в памяти, а какие в регистрах. В языках высокого уровня эту задачу выполняет компилятор.

У процессора 8088 регистры 16-битные, их восемь штук (в скобках указаны типичные способы применения регистра):

  • AX — общего назначения (аккумулятор);
  • BX — общего назначения (адрес);
  • CX — общего назначения (счетчик);
  • DX — общего назначения (расширяет AX до 32 бит);
  • SI — общего назначения (адрес источника);
  • DI — общего назначения (адрес приемника);
  • BP — указатель базы (обычно адресует переменные, хранимые на стеке);
  • SP — указатель стека.

Несмотря на то что у каждого регистра есть типичный способ применения, ты можешь использовать их как заблагорассудится. Четыре первых регистра — AX , BX , CX и DX — при желании можно использовать не полностью, а половинками по 8 бит (старшая H и младшая L ): AH , BH , CH , DH и AL , BL , CL , DL . Например, если запишешь в AX число 0x77AA ( mov ax , 0x77AA ), то в AH попадет 0x77 , в AL — 0xAA .

Подготовка рабочего места

Сейчас ты напишешь свою первую программу на ассемблере. Назови ее как хочешь (например, first . asm ) и скопируй в папку, где установлен nasm .

Основы ассемблера. Hello World

Теперь запусти командную строку, в Windows это cmd.exe. Потом зайди в папку nasm и скомпилируй программу, используя вот такую команду:

Чтобы запустить этот файл в современной ОС, открой DOSBox и введи туда вот такие три команды:

Основы ассемблера. Hello World

Инструкции, директивы

В нашей с тобой программе есть только три вещи: инструкции, директивы и метки.

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

Директивы (в нашей программе их две: org и db ) — это распоряжения, которые ты даешь компилятору. Каждая отдельно взятая директива говорит компилятору, что на этапе ассемблирования нужно сделать такое-то действие. В машинный код директива не переводится, но она влияет на то, каким образом будет сгенерирован машинный код.

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

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

В нашем случае: db "Hello, world" , '!' , 0 .

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

Метки, условные и безусловные переходы

Метки используются для двух целей: задавать имена переменных, которые хранятся в памяти (такая метка в нашей программе только одна: string ), и помечать участки в коде, куда можно прыгать из других мест программы (таких меток в нашей программе три штуки — те, которые начинаются с двух символов собаки).

У тебя в распоряжении есть одна инструкция безусловного перехода ( jmp ) и штук двадцать инструкций условного перехода.

В нашей программе задействованы две инструкции перехода: je и jmp . Первая выполняет условный переход (Jump if Equal — прыгнуть, если равно), вторая (Jump) — безусловный. С их помощью мы организовали цикл.

Обрати внимание: метки начинаются либо с буквы, либо со знака подчеркивания, либо со знака собаки. Цифры вставлять тоже можно, но только не в начало. В конце метки обязательно ставится двоеточие.

Комментарии, алгоритм, выбор регистров

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

Как добавлять комментарии? Просто поставь точку с запятой, и все, что напишешь после нее (до конца строки), будет комментарием. Давай добавим комментарии в нашу программу.

Основы ассемблера. Комментарии, алгоритм, выбор регистров

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

  1. Поместить в BX адрес строки.
  2. Поместить в AL очередную букву из строки.
  3. Если вместо буквы там 0, выходим из программы — переходим на 6-й шаг.
  4. Выводим букву на экран.
  5. Повторяем со второго шага.
  6. Конец.

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

Взаимодействие с пользователем: получение данных с клавиатуры

От программ, которые не могут взаимодействовать с пользователем, толку мало. Так что смотри, как можно считывать данные с клавиатуры. Сохрани вот этот код как second . asm .

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

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

Прежде чем приступить к данным урокам, вы должны иметь базовые знания по терминологии программирования, а также базовое понимание любого из языков программирования (например, изучить уроки по C++), которые помогут вам быстрее разобраться с базовыми концепциями программирования на Ассемблере и быстрее прогрессировать.

Читайте также: