Обзор архитектуры компьютера и синтаксис ассемблера

Обновлено: 30.06.2024

Понятие программирования и виды языков программирования. История развития ассемблера, формирования машинного кода. Обзор языков ассемблера и акроассемблера. Примеры программ MASM, TASM и NASM. Сводные программные характеристики данных ассемблеров.

Рубрика Программирование, компьютеры и кибернетика
Вид курсовая работа
Язык русский
Дата добавления 25.05.2014
Размер файла 344,5 K

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

по дисциплине "Системное программирование"

на тему: "Сравнение средств разработки ассемблерных программ (TASM, MASM, NASM)"

Программирование - это процесс создания программных продуктов, которые написаны на языке программирования.

Язык программирования - это формальная знаковая система, которая предназначена для написания программ, понятных для компьютера.

Языки программирования делятся на три вида:

- машинные языки. Очень непонятно для человека, но очень понятно для компьютера.

-языки низкого уровня. Например, Ассемблер.

- языки программирования высокого уровня. Например, Паскаль, Си, Java.

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

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

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

Морис Уилкс назвал мнемоническую схему для "Эдсака" и библиотеку подпрограмм собирающей системой (по-английски assembly system - отсюда слово "ассемблер"), поскольку она собирала последовательности подпрограмм.

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

Ttl db 'First ASSEMBLER program',0h

Msg db 'Hello, World. ',0h

push offset Msg

push offset Ttl

Ассемблер можно отнести к языкам второго поколения, если за первый принять машинный код. Появление этого языка на свет, очень облегчало жизнь программистов. Теперь вместо рябящих в глазах нулей и единиц, они могли писать программу командами, состоящими из символов приближенных к обычному языку. Для того времени этот язык был новшеством и пользовался популярностью т.к. позволял писать программы небольшого размера, что при тех машинах критерий значительный. Но сложность разработки в нём больших программных комплексов привела к появлению языков третьего поколения - языков высокого уровня. Но на этом жизнь ассемблера не закончилась, он жив и посей день и не только жив, но и пользуется популярностью в узких кругах. Сейчас его используют в написании отдельных фрагментов программ или иногда в написании самих программ. Примеров может быть много, но самые яркие это использование ассемблера в написании драйверов, игр и загрузчиков ОС. Не стоит забывать, что у хакеров этот язык так же пользуется популярностью, да ещё какой!

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

Представление алгоритма в виде последовательности двоичных кодов называется программой. Программа записывается в отдельном исполняемом файле ( например exe для windows-систем). Таким образом, программа - это алгоритм, предназначенный для выполнения компьютером. Двоичное представление команд компьютера называется машинным кодом.

асссемблер акроассемблер программирование язык

1. Обзор языков ассемблера и макроассемблера.

Продукт жизнедеятельности ранней компании Microsoft, которой тот был нужен для создания MS-DOS, а позднее и для Windows 9x/NT. После выхода версии 6.13 продукт на некоторое время тормознул в развитии, но потом здравый смысл взял вверх и последняя версия (на момент написания этих строк - 6.13.8204) поддерживает уникод, все SSE/SSEII/SEEIII расширения (объявляемые двумя директивами .686/.XMM), а также архитектуру AMD x86-64. Платформа Intel IA64 не поддерживается, но Microsoft поставляет Intel-ассемблер IAS.EXE.

Аббревиатура MASM расшифровывается отнюдь не как Microsoft Assembler, а как Macro Assembler, то есть Ассемблер с поддержкой Макросов, покрывающих своими возможностями широкий круг задач: повторения однотипных операций с параметризацией (шаблоны), циклические макросы, условное ассемблирование и т.д., по сравнению с которым препроцессор языка Си выглядит жалкой подделкой. Имеется даже зачаточная поддержка основных парадигм ООП, впрочем, так и не получившая большого распространения, поскольку ассемблер и ООП концептуально несовместимы. Многие пишут даже без макросов на чистом ассемблере, считая свой путь идеологически наиболее правильным. Но о вкусах не спорят.

Рисунок 1 Упаковка официального дистрибутивного пакета MASM

Сначала MASM распространялся в виде самостоятельного (и притом весьма дорогостоящего) пакета, но позже он был включен в состав DDK (Driver Development Kit ), которое вплоть до Windows 2000 DDK раздавалось бесплатно, а сейчас доступно только подписчикам MSDN (Microsoft Developer Network). Впрочем, вполне полноценное DDK (с ассемблером) для Windows Server 2003 входит в Kernel-Mode Driver Framework, а сам транслятор MASM'а еще и в Visual Studio Express, которая бесплатна.

Стив Хатчессон собрал последние версии транслятора MASM'а, компоновщик от Microsoft, включаемые файлы, библиотеки, обширную документацию, статьи разных авторов, посвященные ассемблеры и даже простенькую IDE в один дистрибутив, известный под "пакетом хатча" (hutch), бесплатно раздаваемый всем желающим на вполне лицензионной основе, так что это не хак, а вполне удобный комплект инструментов для программирования под Windows на ассемблере.

MASM'у посвящено множество книг, что упрощает процесс обучения, а в сети можно найти кучу исходных текстов ассемблерных программ и библиотек, освобождающих программиста от необходимости изобретать велосипед. Также MASM является выходным языков для многих дизассемблеров (Sourcer, IDA Pro). Все это делает MASM транслятором номером один в программировании под Windows/Intel.

Поддерживаются два выходных формата: 16/32 Microsoft OMF и (16)/32/64 COFF, что позволяет транслировать 16/32-разрядные программы под MS-DOS, работающие в реальном и защищенном режиме, 16-разрядные приложения и драйвера для Windows 3.x, 32-разрядные приложения и драйвера для Windows 9x/NT, а также 64-разрядные приложения и драйвера для Windows NT 64-bit Edition. Для создания бинарных файлов потребуется линкер, который умеет это делать (например, ulink от Юрия Харона). Кстати говоря, последние версии штатного Microsoft Linker'а, входящие в SDK и DDK, утратили способность собирать 16-разряные файлы под MS-DOS/Windows 3.x и приходится возвращаться к старой версии, которая лежит в папке NTDDK\win_me\bin16.

MASM генерирует отладочную информацию в формате CodeView, которую Microsoft Linker может преобразовывать в PDB-формат, хоть и не документированный, но поддерживаемый библиотекой dbghelp.dll, позволяющей сторонним разработчикам "переваривать" отладочную информацию, поэтому файлы, оттранслированные MASM'ом, можно отлаживать в Soft-Ice, дизассемблировать в IDA Pro и прочих продуктах подобного типа.

Главный недостаток MASM'а - его жуткая багистность. Стоит только открыть Knowledge Base, посмотреть на список официально подтвержденных багов и. ужаснуться! Как только после этого на MASM'е вообще можно программировать?! Особенно много ошибок в штатной библиотеке. Вот только несколько примеров: dwtoa и atodw_ex не понимают знака и по скорости очень тормозят, хотя в документации написано: "A high speed ascii decimal string to DWORD conversion for applications that require high speed streaming of conversion data"; ucFind не находит в строке подстроку, если длина подстроки равна 1 символу; функции BMHBinsearch и SBMBinSearch (поиск алгоритмом Бойера-Мура) реализованы с ошибками; некоторые функции обрушивают программу (если передать ustr2dw строку длиннее пяти байт - программа падает).

Другой минус - отсутствие поддержки некоторых инструкций и режимов адресации процессора, например, невозможно сделать jmp far seg:offset, а попытка создания смешанного 16/32 разрядного кода - это настоящий кошмар, который приходится разгребать руками и всячески извращаться, преодолевая сопротивление "менталитета" транслятора.

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

Тем не менее, несмотря на все эти недостатки, MASM остается самым популярным профессиональным транслятором ассемблера при программировании под Windows NT, хотя разработчикам приходится сталкиваться с трудностями, но реальных альтернатив ему нет.

Самый популярный транслятор ассемблера времен MS-DOS, созданный фирмой Borland, и полностью совместимый с MASM'ом вплоть до версий 6.x и поддерживающий свой собственный режим IDEAL с большим количеством улучшений и расширений. TASM до сих пор используется для обучения программированию на ассемблере под архитектуру x86.

Удобство программирования, скромные системные требования и высокая скорость трансляции обеспечивали TASM'у лидерство на протяжении всего существования MS-DOS. Но с появлением Windows популярность TASM'а стала таять буквально на глазах. Не сумев (или не захотев) добиться совместимости с заголовочными файлами и библиотеками, входящими в комплект SDK/DDK, фирма Borland решила поставлять свой собственный порт, причем далеко не идеальный. К тому же штатный линкер tlink/tlink32 не поддерживает возможности создания драйверов, а формат выходных файлов (Microsoft OMF, IBM OMF, Phar Lap), не поддерживается текущими версиями линкера от Microsoft (впрочем, 16-битные версии это умеют). В довершение ко всему, формат отладочной информации несовместим с CodeView и реально поддерживается только TurboDebugger'ом и soft-ice.

И хотя эти проблемы в принципе разрешимы: возможность низкоуровневого ассемблерного программирования (без включаемых файлов и макросов) осталась там же, где и была, несовместимость форматов компенсируется наличием конверторов, но. преимущества режима IDEAL над стандартным синтаксисам MASM'а день ото дня казались все менее и менее значительными, ряды поклонников редели и в конце концов проект закрылся. Последней версией транслятора стал TASM 5.0, поддерживающий команды вплоть до 80486 процессора. Отдельно был выпушен патч, обновляющий TASM до версии 5.3 и поднимающий его вплоть до Pentium MMX, однако команды Pentium II - такие, например, как SYSENTER, как не работали, так и не работают. Поддержка уникода тоже отсутствует.

В настоящее время Borland прекратила распространение своего ассемблера и достать его можно только в магазинах торгующих старыми CD-ROM или у какого-нибудь коллекционера. !tE выпустил пакет TASM 5+, включающий в себя транслятор, линкер, библиотекарь, какое-то подобие документации, несколько заголовочных файлов под Windows и пару демонстрационных примеров. Когда будете искать это добро, не перепутайте его с TASM32 фирмы Squak Valley Software - это совершенно независимый кроссассемблер, ориентированный на процессоры 6502,6800/6801/68HC11, 6805, TMS32010, TMS320C25, TMS7000, 8048, 8051,8080/8085, Z80, 8096/80C196KC, о которых большинство из нас в лучшем случае просто слышало, что они есть.

TASM - это закрывшийся проект. Однако для разработки прикладных приложений под Windows 16/32 и MS-DOS он все-таки подходит, особенно если вы уже имеете опыт работы с ним и некоторые собственные наработки (библиотеки, макросы), с которыми жалко расставаться, а конвертировать под MASM - весьма проблематично. Возможно, вам понравится бесплатный Lazy Assembler (автор - Половников Степан), совместимый с режимом IDEAL TASM и поддерживающим команды из наборов MMX, SSE, SSEII, SSEIII, 3DNow!Pro.

Транслятор NASM (расшифровывается как Netwide Assembler - Ассемблер Шириной во Всю Сеть или просто Расширенный Ассемблер) вырос из идеи, поданной на comp.lang.asm.x86 (или возможно на alt.lang.asm - сейчас точно никто и не помнит), когда не было ни одного хорошего свободного ассемблера под x86. FASM'а тогда еще не существовало. MASM/TASM стоили денег и работали только под MS-DOS/Windows. Единственный более-менее работающий транслятор под UNIX - GAS (GNU Assembler) завязан на компилятор GCC и имеет такой ужасный синтаксис, что писать на нем могут только мазохисты (и ведь примеров программ, запрограммированных на GAS'е практически нет!). Остальные ассемблеры (типа A86, AS86) не позволяют писать 16/32-разрядный код или раздаются практически без документации.

Рисунок 2 Официальный логотип NASM'a

Кончилось это дело тем, что группа программистов во главе с Петром Анвином (Peter Anvin) решила разработать собственный ассемблер и это у нее получилось! MASM-подобный синтаксис, достаточно мощная макросистема (впрочем, несовместимая с MASM'ом и ничего не знающая об union'ах вместе с кучей других полезных фич), поддержка всей линейки x86-процессоров вплоть до IA64 в x86-режиме, богатство выходных файлов (bin, aout, aoutb, coff, elf, as86, obj, win32, rdf, ieee), генерация отладочной информации в форматах Borland, STABS и DWARF2 вкупе с портами под MS-DOS, Windows, Linux и BSD обеспечили NASM'у неслабую популярность, однако без ярко выраженного фанатизма, характерного для FASM'а. Количество ошибок в трансляторе довольно велико, причем в отличии от работающих продуктов (MASM/TASM) при "хитрых ошибках" NASM не падает, а генерирует ошибочный (по структуре) объектный файл. Вот и ищи, как он его сгенерировал, чем хочешь (даже матерым хакерам это сложно, а нормальный программист может даже и не пытаться). И, как это принято в Open Source-community, полное игнорирование баг-репортов "неудобных" для авторов (разработки даже утверждают, что ошибок в их трансляторе вообще нет, в смысле им не известен ни один). Тем не менее, в последней версии NASM'а, в зависимости от значения ключа -On, код может сгенерироваться в двух или более экземплярах или может пропасть весь экспорт (pubdef'ы).

К минусам NASM'а можно отнести отсутствие поддержки уникода, платформы AMD x86-64, формата отладочной информации CodeView и некоторые странности синтаксиса. В частности, команда "mov eax, 1" не оптимизируется и транслятор умышленно оставляет место для 32-разрядного операнда. Если же мы хотим получить "короткий" вариант, размер операнда необходимо специфицировать явно: "mov eax, byte 1", что очень сильно напрягает или. использовать опцию "-On" для автоматической оптимизации.

Также необходимо принудительно указывать длину переходов short или near, иначе очень легко нарваться на специфическую ошибку "short jump out of range". Впрочем, опять-таки, существует возможность настроить транслятор на генерацию near-переходов по умолчанию.

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

Из мелких недочетов можно называть невозможность автоматического генерации короткого варианта инструкции "push imm8" и отсутствие контроля за соответствием транслируемых инструкций типу указанного процессора (команда "cpuid" под ".486" ассемблируется вполне нормально, а ведь не должна).

Непосредственная трансляция примеров из SDK/DDK под NASM'ом невозможна, так что разрабатывать на нем драйвера под Windows может только очень большой поклонник. NASM - один из лучших ассемблеров под Linux/BSD, а вот под Windows его позиции уже не так сильны (в основном из-за неполной совместимости с MASM'ом).

2. Пример программы (MASM)


Статья посвящена языку ассемблер с учетом актуальных реалий. Представлены преимущества и отличия от ЯВУ, произведено небольшое сравнение компиляторов, скрупулёзно собрано значительное количество лучшей тематической литературы.

1. Язык. Преимущества и отличия от ЯВУ

Ассемблер (Assembly) — язык программирования, понятия которого отражают архитектуру электронно-вычислительной машины. Язык ассемблера — символьная форма записи машинного кода, использование которого упрощает написание машинных программ. Для одной и той же ЭВМ могут быть разработаны разные языки ассемблера. В отличие от языков высокого уровня абстракции, в котором многие проблемы реализации алгоритмов скрыты от разработчиков, язык ассемблера тесно связан с системой команд микропроцессора. Для идеального микропроцессора, у которого система команд точно соответствует языку программирования, ассемблер вырабатывает по одному машинному коду на каждый оператор языка. На практике для реальных микропроцессоров может потребоваться несколько машинных команд для реализации одного оператора языка.

Язык ассемблера обеспечивает доступ к регистрам, указание методов адресации и описание операций в терминах команд процессора. Язык ассемблера может содержать средства более высокого уровня абстракции: встроенные и определяемые макрокоманды, соответствующие нескольким машинным командам, автоматический выбор команды в зависимости от типов операндов, средства описания структур данных. Главное достоинство языка ассемблера — «приближенность» к процессору, который является основой используемого программистом компьютера, а главным неудобством — слишком мелкое деление типовых операций, которое большинством пользователей воспринимается с трудом. Однако язык ассемблера в значительно большей степени отражает само функционирование компьютера, чем все остальные языки.

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

Для успешного использования ассемблера необходимы сразу три вещи:

  • знание синтаксиса транслятора ассемблера, который используется (например, синтаксис MASM, FASM и GAS отличается), назначение директив языка ассемблер (операторов, обрабатываемых транслятором во время трансляции исходного текста программы);
  • понимание машинных инструкций, выполняемых процессором во время работы программы;
  • умение работать с сервисами, предоставляемыми операционной системой — в данном случае это означает знание функций Win32 API. При работе с языками высокого уровня очень часто к API системы программист прямо не обращается; он может даже не подозревать о его существовании, поскольку библиотека языка скрывает от программиста детали, зависящие от конкретной системы. Например, и в Linux, и в Windows, и в любой другой системе в программе на Си/Си++ можно вывести строку на консоль, используя функцию printf() или поток cout, то есть для программиста, использующего эти средства, нет разницы, под какую систему делается программа, хотя реализация этих функций будет разной в разных системах, потому что API систем очень сильно различается. Но если человек пишет на ассемблере, он уже не имеет готовых функций типа printf(), в которых за него продумано, как «общаться» с системой, и должен делать это сам.

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

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

  • объем используемой памяти (программы-загрузчики, встраиваемое программное обеспечение, программы для микроконтроллеров и процессоров с ограниченными ресурсами, вирусы, программные защиты и т.п.);
  • быстродействие (программы, написанные на языке ассемблера выполняются гораздо быстрее, чем программы-аналоги, написанные на языках программирования высокого уровня абстракции. В данном случае быстродействие зависит от понимания того, как работает конкретная модель процессора, реальный конвейер на процессоре, размер кэша, тонкостей работы операционной системы. В результате, программа начинает работать быстрее, но теряет переносимость и универсальность).

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

2. О компиляторах

Какой ассемблер лучше?

Для процессора x86-x64, имеется более десятка различных ассемблер компиляторов. Они отличаются различными наборами функций и синтаксисом. Некоторые компиляторы больше подходят для начинающих, некоторые ― для опытных программистов. Некоторые компиляторы достаточно хорошо документированы, другие вообще не имеют документации. Для некоторых компиляторов разработано множеством примеров программирования. Для некоторых ассемблеров написаны учебные пособия и книги, в которых подробно рассматривается синтаксис, у других нет ничего. Какой ассемблер лучше?

Учитывая множество диалектов ассемблеров для x86-x64 и ограниченное количество времени для их изучения, ограничимся кратким обзором следующих компиляторов: MASM, TASM, NASM, FASM, GoASM, Gas, RosAsm, HLA.

Какую операционную систему вы бы хотели использовать?

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

Windows DOS Linux BSD QNX MacOS, работающий на
процессоре Intel/AMD
FASM x x x x
GAS x x x x x x
GoAsm x
HLA x x
MASM x x
NASM x x x x x x
RosAsm x
TASM x x

Поддержка 16 бит

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

Поддержка 64 бит

За исключением TASM, к которому фирма Borland охладела в середине нулевых, и, который не поддерживает в полном объеме даже 32-разрядные программы, все остальные диалекты поддерживают разработку 64-разрядных приложений.

Переносимость программ

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

Предполагаете ли вы писать приложение на ассемблере и затем портировать, это приложение с одной ОС на другую с «перекомпиляцией» исходного кода? Эту функцию поддерживает диалект HLA. Предполагаете ли вы иметь возможность создавать приложения Windows и Linux на ассемблере с минимальными усилиями для этого? Хотя, если вы работаете с одной операционной системой и абсолютно не планируете работать в какой-либо другой ОС, тогда эта проблема вас не касается.

Поддержка высокоуровневых языковых конструкций

Некоторые ассемблеры предоставляют расширенный синтаксис, который обеспечивает языковые высокоуровневые структуры управления (типа IF, WHILE, FOR и так далее). Такие конструкции могут облегчить обучение ассемблеру и помогают написать более читаемый код. В некоторые ассемблеры встроены «высокоуровневые конструкции» с ограниченными возможностями. Другие предоставляют высокоуровневые конструкции на уровне макросов.

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

Качество документации

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

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

Учебники и учебные материалы

Документация на самом ассемблере, конечно, очень важна. Еще больший интерес для новичков и других, изучающих язык ассемблера (или дополнительные возможности данного ассемблера), ― это наличие документации за пределами справочного руководства для языка. Большинство людей хотят, чтобы учебник, объясняющий, как программировать на ассемблере, не просто предоставляет синтаксис машинных инструкций и ожидает, что читателю объяснят, как объединять эти инструкции для решения реальных проблем.

MASM является лидером среди огромного объема книг, описывающих, как программировать на этом диалекте. Есть десятки книг, которые используют MASM в качестве своего ассемблера для обучения ассемблеру.

Большинство учебников по ассемблеру MASM/TASM продолжают обучать программированию под MS-DOS. Хотя постепенно появляются учебники, которые обучают программированию в Windows и Linux.

3. Литература и веб ресурсы

Beginners

Advanced

4. Практика

Итак, вы уже знаете, что такое ассемблер и с чем его едят. Вы запаслись парой/тройкой книг и веб мануалами, возможно определились и с компилятором… К сожалению уроки программирования выходят за рамки данной статьи, но для тех чей выбор пал на MASM/FASM можете воспользоваться следующими макетами:

  • MASM64 простое окно masm64SimpleWindow.asm
  • FASM64 простое окно fasm64SimpleWindow.asm
Так получилось, что среда не полностью бесплатная. Вы можете поддержать проект приобретая Pro лицензию за свободную стоимость. Используйте код: HABR_zzdBG1qadG (если бесплатно установите бегунок в позицию 0).

Желаем вам, друзья, значительных достижений и новых знаний в 2018 году!

Ps1: Уважаемый, Хабрахабр! Добавьте в ваш редактор подсветку ассемблера (Intel-синтаксис), это пригодится для будущих статей!

2. Организация и разрядность интерфейсов

При рассмотрении общей схемы ЭВМ под интерфейсами подразумевают шины обмена информацией (на схеме ША - шина адреса, ШД - шина данных, ШУ - шина управления). Посредством этих шин организован интерфейс (связь, взаимодействие) между МП и другими частями компьютера.

Для ПК на базе процессора i8086 эти шины имеют следующие разрядности:

шина адреса - 20;

шина управления - 1.

3. Набор и доступность регистров

а). Регистры общего назначения

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

Для регистров ax, bx, cx, dx можно получить доступ отдельно к старшему или младшему байту слова. Старший байт обозначается h (high), младший - l (low). Так регистр ax может использоваться и целиком, и по частям: ah - старший байт регистра ax, al - младший байт регистра ax (см. Рис 2).

Регистры si и di называют индексными регистрами, их можно использовать по своему усмотрению, но официально они предназначены для поддержки цепочечных команд.

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

б). Сегментные регистры

Название "сегментные" данные регистры получили от применяемого в МП Intel способа адресации памяти. Этот способ состоит в поблочном использовании памяти, блоки называются сегментами. Каждая программа может одновременно использовать 4 сегмента (блока) памяти:

  • сегмент с кодом программы;
  • сегмент с данными;
  • сегмент стека;
  • дополнительный сегмент данных.

Для задания местоположения этих сегментов в памяти (для адресации) и применяются сегментные регистры. В данную группу входят 4 регистра:

  • cs (code segment) - сегмент кода;
  • ds (data degment) - сегмент данных;
  • ss (stack segment) - сегмент стека;
  • es (extension data segment) - дополнительный сегмент данных.

в). Регистры состояния и управления

Регистры данной группы предоставляют доступ к некоторой дополнительной информации о состоянии МП и о результате выполнения последней арифметико-логической операции.

В регистре ip содержится адрес команды, которая будет выполняться следующей. Фактически этот адрес содержится в паре регистров cs:ip. cs содержит адрес сегмента кода, а ip - смещение (положение относительно начала сегмента) нужной команды (см организация памяти).

Флаговый регистр особо выделяется среди всех остальных. У этого регистра каждый бит (для флагового регистра биты именуется флагами) имеет определённое назначение и собственное название. Флаги отражают состояние микропроцессора и особенности результата выполнения последней арифметической или логической операции. Флаг либо установлен, либо сброшен (0, 1).

  • CF (Carry) - флаг переноса. Используется арифметическими командами и командами сдвигов;
  • PF (Parity) - флаг контроля четности. Содержит 1, если сумма единиц в восьми младших разрядах регистра является четным числом, или 0, если она нечетна;
  • AF (Auxiliary) - дополнительный флаг переноса. Содержит значение переноса из 3-го бита восьмибитовых данных;
  • ZF (Zero) - флаг ноля. Содержит 1, если результат арифметической операции или операции сравнения нулевой, в противном случае содержит 0;
  • SF (Sign) - знаковый флаг. Содержит знак результата (старший бит) после выполнения арифметических операций (0 = '+',1= '-');
  • OF (Overflow) - флаг переполнения. Указывает, что в результате выполнения арифметической команды возникло переполнение старшего разряда результата;
  • DF (Direction) - флаг направления. Определяет, в каком направлении будут обрабатываться данные цепочечными командами - от младших адресов к старшим или наоборот;
  • IF (Interrupt) - флаг разрешения прерываний. Указывает на возможность внешних прерываний;
  • TF (Trap) - флаг пошагового выполнения. Устанавливает выполнение команд процессора в пошаговом режиме (для отладки программ).

4. Организация памяти

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

Доступ а любому байту ОЗУ осуществляется по шине адреса, которая, как мы уже говорили, является 20-разрядной (для i8086). Разрядность шины ограничивает объём доступной памяти, так 20 разрядов шины позволяют записать адреса от 0 до 2 20 -1 (1 Мбайт). Все адреса удобнее записывать в шестнадцатеричном виде (диапазон физических адресов от 00000h до FFFFFh).

Для записи любого физического адреса необходимо 20 разрядов. Но все регистры МП 16-разрядные. Для получения 20-разрядного адреса используется следующий механизм:

  1. Всю физическую память условно можно разбить на параграфы (1 параграф = 16 байт);
  2. Память будем выделять программам только небольшими блоками, данные блоки будем в дальнейшем называть сегментами памяти или просто сегментами.
  3. Программа может состоять из любого количества сегментов;
  4. Каждый сегмент может начинаться только с начала параграфа, т.е. адресом сегмента будет номер его первого параграфа (такой адрес уже 16-разрядный);

Такой механизм называется сегментацией, он поддерживается на аппаратном уровне (мы не можем его заменить или изменить). В МП имеются специальные сегментные регистры, которые содержат адреса сегментов в памяти.

Так как под адресом сегмента понимается номер параграфа, с которого он начинается, то для получения физического адреса начала сегмента необходимо умножить его на 16. Например, сегмент 0032h имеет физический адрес 0032h x 10h = 00320h.

Для определения адреса конкретной ячейки памяти недостаточно знать адрес сегмента, в котором она находится. Необходимо знать позицию этой ячейки относительно начала сегмента. Этот относительный адрес называют эффективным адресом или смещением. Поскольку максимальный размер сегмента ограничен 64 килобайтами, то смещение будет записываться 16-разрядным значением.

Таким образом, от 20-разрядного физического адреса мы перешли к адресу, состоящему из двух 16-разрядных частей: адрес сегмента и смещение. Такие составные адреса называются логическими и записываются так:

Механизм сегментации служит не только для перехода от 20-разрядных физических адресов к 16-разрядным логическим, он необходим для защиты программ от взаимного влияния.

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

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

Содержание

История

Когда компьютеры были большими как динозавры, а объем мозгов у них был примерно такой же, как у этих рептилий, и даже меньше, единственным способом программирования было забивание программы в память ЭВМ непосредственно в цифровом виде (иногда даже с помощью щелканья переключателями). Затем человек начал совершенствовать эту технику, постепенно перекладывая труд по генерации кода на плечи самой машины. Ибо заставлять высококвалифицированного человека перебирать биты и помнить кучу шестнадцатеричных (а то и восьмеричных) кодов обходится весьма дорого, как в деньгах, так и во времени.

Простейший транслятор (ассемблер первого поколения) позволял фактически просто писать машинные команды на «человеческом» языке, что позволило программистам немного расслабиться. Затем появились языки высокого уровня и компиляторы (более интеллектуальные генераторы кода с более понятного человеку языка) и интерпретаторы (исполнители человекописанной программы на лету). Которые совершенствовались, совершенствовались, совершенствовались, и досовершенствовались до «программирования мышкой», а компиляторы научились генерировать код, которому по скорости написанный человеком начал сливать. Речь идёт только о последнем эшелоне оптимизации — оптимальном генерировании машинных операций. А в наше время узкое место и так давно уже не в нём, а в бездарной архитектуре комплексов ПО в целом, из-за чего машина занимается переливанием из пустого в порожнее — но с предельно оптимизированной скоростью струи, да — это в первую очередь. А еще очень негативно на скорости сказывается незнание быдлокодерами основ теории алгоритмов и структур данных. Второй пункт в последнее время нивелируется невъебенно навороченными стандартными (или специальными) библиотеками языков и сред разработки, что в конечном итоге способствует терминальному отупению тех же быдлокодеров и возвратом к пункту 1 этой ссылки.

В целом история языков программирования протекает в направлении от программирования на языке компьютера до манипуляции абстракциями вроде Послать.Лесом(всех(кому не нравится эта статья)) Ну или (послать (лесом (всех (кому-не-нравится "эта-статья")))) или

На языке ассемблера этот код занял бы более 9000 строк. И потребовал бы долгой и кропотливой работы по своему созданию.

Область применения

  • Популярен для допиливания зацикливаемых кусков программы… в роли напильника. Перепиливание критических участков кода может принести PROFIT, а может и не принести. В любом случае, заниматься таким перепиливанием стоит только тогда, когда у вас на руках уже полностью работающий алгоритм, который можно было бы ускорить, а не наоборот.
  • Для использования в софте новых команд, доступных в новых процессорах. Компилятор хоть и оптимизирует код высокого уровня при компиляции, но… практически никогда не способен генерировать инструкции из расширенных наборов команд типа AVX, СTV, XOP и т. п. Потому что команды в процессоры добавляют быстрее, чем в компиляторах появляется логика для генерации этих команд.
  • Используется для написания кода, создание которого невозможно (или затруднено) на языках высокого уровня, например, получение дампа памяти/стека. Даже когда аналог на языке высокого уровня возможен, профит от языка ассемблера может быть значительным в разы.
  • В вузах изучается для вливания в МНУ студентов познаний об устройстве и работе компьютера.
  • Является единственным языком программирования для создания достойных инфекторов — Cih, Sality, Sinowal.
  • Оптимизация затратных математических алгоритмов.
  • Программирование под устаревшие платформы (NES, например), которым мешает использовать «сбалансированный» Си.

Ассемблирование и компилирование

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

Дизассемблер

Дизассе́мблер — транслятор, преобразующий машинный код, объектный файл или библиотечные модули в текст программы на языке ассемблера.

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

Архитектуры

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

  • Ассемблеры для DOS: TASM,MASM.
  • Ассемблеры для Windows: TASM 5+,MASM,MASM32,FASM(для настоящих "ценителей" языка.
  • Ассемблеры для Linux:gas (GNU Assembler),
  • Переносимые ассемблеры:Также существует открытый проект ассемблера, версии которого доступны под различные операционные системы, и который позволяет получать объектные файлы для этих систем. Называется этот ассемблер NASM (Netwide Assembler).YASM,FASM.

Язык ассемблера

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

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

Директивы ассемблера позволяют включать в программу блоки данных (описанные явно или считанные из файла); повторить определённый фрагмент указанное число раз; компилировать фрагмент по условию; задавать адрес исполнения фрагмента, менять значения меток в процессе компиляции; использовать макроопределения с параметрами и др.

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

Достоинства и недостатки

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

Синтаксис

Общепринятого стандарта для синтаксиса языков ассемблера не существует. Однако, существуют стандарты де-факто — традиционные подходы, которых придерживаются большинство разработчиков языков ассемблера. Основными такими стандартами являются Intel-синтаксис и AT&T-синтаксис.

Общий формат записи инструкций одинаков для обоих стандартов:

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

Опкод — непосредственно мнемоника инструкции процессору. К ней могут быть добавлены префиксы (повторения, изменения типа адресации и пр.). В качестве операндов могут выступать константы, названия регистров, адреса в оперативной памяти и пр.. Различия между стандартами Intel и AT&T касаются, в основном, порядка перечисления операндов и их синтаксиса при различных методах адресации.

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

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