__asm __asm
Блок, относящийся только к системам Microsoft Microsoft Specific
Ключевое слово __asm вызывает встроенный ассемблер и может отображаться везде, где допустим оператор С или С++. The __asm keyword invokes the inline assembler and can appear wherever a C or C++ statement is legal. Он не может отображаться самостоятельно. It cannot appear by itself. За ним должна следовать инструкция по сборке, группа инструкций, заключенная в круглые скобки, либо, в крайнем случае, пустая пара круглых скобок. It must be followed by an assembly instruction, a group of instructions enclosed in braces, or, at the very least, an empty pair of braces. Термин «блок __asm » в этом разделе относится к любой инструкции или группе инструкций, в скобках или без них. The term » __asm block» here refers to any instruction or group of instructions, whether or not in braces.
Поддержка стандартного ключевого слова C++ asm в Visual C++ ограничена тем фактом, что компилятор не создаст ошибку по ключевому слову. Visual C++ support for the Standard C++ asm keyword is limited to the fact that the compiler will not generate an error on the keyword. Однако блок asm не создаст никакого значимого кода. However, an asm block will not generate any meaningful code. Используйте __asm вместо asm . Use __asm instead of asm .
Грамматика Grammar
блок ASM: asm-block:
__asm инструкции ассемблера ; opt __asm assembly-instruction ;opt
__asm <assembly-instruction-list > ;opt __asm <assembly-instruction-list > ;opt
список сборок инструкция: assembly-instruction-list:
инструкции ассемблера ; opt assembly-instruction ;opt
assembly-instruction ; assembly-instruction-list ;opt assembly-instruction ; assembly-instruction-list ;opt
Примечания Remarks
При использовании без круглых скобок ключевое слово __asm означает, что остальная часть строки — это оператор на языке сборки. If used without braces, the __asm keyword means that the rest of the line is an assembly-language statement. При использовании с фигурными скобками оно означает, что каждая строка между скобками — это оператор на языке сборки. If used with braces, it means that each line between the braces is an assembly-language statement. Для обеспечения совместимости с предыдущими версиями _asm является синонимом __asm . For compatibility with previous versions, _asm is a synonym for __asm .
Поскольку ключевое слово __asm является разделителем операторов, можно также помещать инструкции ассемблера на одной строке: Since the __asm keyword is a statement separator, you can put assembly instructions on the same line.
До Visual Studio 2005, инструкция Before Visual Studio 2005, the instruction
не вызывает машинный код будет создан при компиляции с /CLR; компилятор преобразовывал инструкцию в инструкцию прерывания среды CLR. did not cause native code to be generated when compiled with /clr; the compiler translated the instruction to a CLR break instruction.
__asm int 3 теперь приводит к созданию машинного кода для функции. __asm int 3 now results in native code generation for the function. Если функция вызывает точку останова в коде и этой функции, скомпилированные в MSIL, используйте __debugbreak. If you want a function to cause a break point in your code and if you want that function compiled to MSIL, use __debugbreak.
Для совместимости с предыдущими версиями _asm является синонимом __asm Если параметр компилятора /Za (отключить расширения языка) указан. For compatibility with previous versions, _asm is a synonym for __asm unless compiler option /Za (Disable language extensions) is specified.
Пример Example
Следующий фрагмент кода — это простой блок __asm , заключенный в фигурные скобки: The following code fragment is a simple __asm block enclosed in braces:
Кроме того, можно поставить __asm перед каждой инструкцией по сборке. Alternatively, you can put __asm in front of each assembly instruction:
Поскольку ключевое слово __asm является разделителем операторов, можно также помещать инструкции по сборке на одной строке. Because the __asm keyword is a statement separator, you can also put assembly instructions on the same line:
Все три примера создают один и тот же код, но первый стиль (где блок __asm заключен в фигурные скобки) имеет некоторые преимущества. All three examples generate the same code, but the first style (enclosing the __asm block in braces) has some advantages. Фигурные скобки четко отделяют код сборки от кода С или С++ и позволяют избежать лишнего повторения ключевого слова __asm . The braces clearly separate assembly code from C or C++ code and avoid needless repetition of the __asm keyword. Скобки также помогают избежать неоднозначности. Braces can also prevent ambiguities. Если требуется поместить оператор C или C++ на одной строке в виде блока __asm , необходимо заключить блок в фигурные скобки. If you want to put a C or C++ statement on the same line as an __asm block, you must enclose the block in braces. Без фигурных скобок компилятор не может определить, где прекращается код сборки и начинаются операторы C или C++. Without the braces, the compiler cannot tell where assembly code stops and C or C++ statements begin. Наконец, поскольку текст в фигурных скобках имеет тот же формат, что и обычный текст MASM, можно легко вырезать и вставить текст из существующих исходных файлов MASM. Finally, because the text in braces has the same format as ordinary MASM text, you can easily cut and paste text from existing MASM source files.
В отличие от фигурных скобок в C и C++ фигурные скобки, в которые заключается блок __asm , не влияют на область видимости переменной. Unlike braces in C and C++, the braces enclosing an __asm block don’t affect variable scope. Можно также разместить блоки __asm в виде вложения, вложение не влияет на область видимости переменной. You can also nest __asm blocks; nesting does not affect variable scope.
Завершение блока, относящегося только к системам Майкрософт END Microsoft Specific
Assembler. Система команд. Синтаксис Intel
Читайте также:
- Aloha системасы
- Cудебник 1550 г. Общая характеристика, система и источники
- I. Понятие и система принципов организации и деятельности прокуратуры РФ.
- II. Бреттон-Вудська система (створена в 1944 р.)
- II. Финансовая система РФ.
- III .1 Транспортная система городов и регионов
- III. Система мероприятий по реализации Концепции
- KOI8-R стал фактически стандартом для русской кириллицы в 1990-х годах в юникс-подобных операционных системах и электронной почте.
- TRAKO — Система керування рахунками клієнтів
- Автоматизована інформаційно-бібліотечна система
- Автономная система
Команды передачи данных.
mov (move – передать переасать) – скопировать.
dst – destination куда, src – source откуда.
reg — register регистр reg8/reg16/reg32
mem – memory mem8/mem16/mem32
imm – immediately непосредственное значение = const которое записано в коде.
mov работает с любыми представлениями. Запрещены пересылки из памяти в память mov mem, mem – ЗАПРЕЩЕНО. Все остальное можно
регистры CRi DRi TRi только с помощью mov с другими регистрами они работать не будут
Когда один из операндов eAX\AX\AL то общая длина команды сокращается на 1 байт, mov становится однооперандный.
exchange – меняет местами содержание источника и конца.
На время своего выполнения блокирует системную шину материнской платы. Гарантирует что на момент его исполнения никто другой не сможет вмешается, за счет того что блокируется системная шина на материнской плате.
Используется для проверки изменения значения семафоров, синхронизации асинхронных процессов.
Проблему синхронизации придумал и решил Деекстор.
XLALT – безоперандная команда. В 1 байт.
(translate) заменяет содержимое аккумулятора AL на байт из 256 байтов таблицы адрес которого находится в BX
YY | ß -> копируется в AL[XX(YY)] |
/|\ XX \|/ | |
ß BX |
Преобразует текущее значение AL в значение из какой-то таблицы.
из источника на вершину стека
PUSH srk – уменьшает значение
POP dst – увеличивает значение, извлекает с текущей вершины стека и помещает его в destination
PUSHA – помещает в стек содержание всех регистров 16 битных в след порядке AX/CX/DX/BX/SP/BP/SI/DI
POPA – извлекает с вершины стека в соответствующем порядке и заполняет их значение в регистрах, значение SP теряется.
PUSHAD – для 32 битных регистров
PUSHF PUSHFD — Помещает значение флагов
POPF POPFD — извлекает значение и записывает в регистры флагов
Команды ввода вывода.
in acc, port Ввод из фиксированного порта
in acc, dx Ввод из переменного порта
команда in помещает в аккумулятор 1 байт из некоторого адреса
out port, acc перемещает 1 байт из аккумулятор в порт
out dx, acc либо 1 байт в порт по адресу расположенному в dx
Обмен с периферией возможен только по байту, за передачу отвечает только процессор.
Арифметические команды.
add dst, src //dst=dst+src
add – складывает значение источника с текущим значением назначения, сумма попадает в dst
В src можно помещать разные регистры.
adc dst,src //dst=dst+src+CF
Если перенос (3+9=12) то CF=1
inc dst // dst=dst+1
int — Увеличение на единицу
150d = 0096h = 0000 0000 1001 0110b
1111 1111 0110 1010b = FF6Ah = -150d
sub dst,src ; dst=dst-src
sbl dst,src ; dst=dst-src-CF
dec dst ; dst=dst-1
Ы inc, dec – на CF не влияют
cmp dst,src ; dst-src
cmp – команда сравнения, на результат не влияет, результат сравнения находится в регистре флагов.
Команды умножения.
mul src ; AL=src*AL
умножается и результат помещается в аккумулятор
mul src ; ext:acc=acc*src
Умножение знаковых чисел
imul src ; идентичен умножению знаковых чисел
imul reg,src ; умножение регистра общего назначения на источник, результат попадает в регистр, старшая половина при этом теряется. reg=src*reg
imul reg,src,imm ; reg=src*imm также старшая половина теряется.
Команды деления.
div src ; DX/AX = acc/src DX – остаток от деления
относится к командам мультипликативным
idiv src ; знаковое деление, 2-х 3-х операндных вариантов нет
Команды преобразования.
CBW src ; convert bait to word расширение (был 8 битным стал 16 битным)
CND src ; convert word to double word (был 16 стал 32 битынм)
CDQ src ; был 32 стал 64 битным
Эти команды копируют знаковый бит в старшие разряды
BCD — числа упакованные в байт.
Логические операции.
NOT – унарная операция, конвертирует только биты.
Логические операции воздействуют друг на друга в межразрядных соединениях.
NOT – при реализации основного кода
остальные – для инициализации и булевой логики (XOR ax,ax).
Команды сдвига.
Единообразно воздействуют на все виды регистров, операндов, они передают побитовый сдвиг право i+1, при сдвиге лево i-1 если сдвигаем на 1 бит.
Сдвиги различаются тем как мы работаем с теми битами которые выдвигаются из регистров.
циклические (то что выпало, идёт на свободное место)
Циклические через CF (длина операндов увеличивается на 1)
Логические(то что выпало игнорируется и на свободное место помещается 0)
Арифметические (знаковый бит «размножается»)
Логические сдвиги влево, вправо SHLD SHRD (Shift Left\Right Double)
SHLD dst, src, count
объединяются в один регистр и производится сдвиг
Команды передачи управления.
Вызов, возврат из подпрограмм, обработка прерываний.
короткий/ближний/дальний переход (short, near, far)
короткий переход от -127 до 128 байт
ближний переход от -32 до 32 мб
дальний переход от -2 до 2 гб
Условные переходы.
JXX проверяются значения флагов.
Команд очень много, могут проверяться комбинации флагов.
JA — проверяют комбинацию регистров CF=0 и ZF=0 – Если выше
JBE — CF=0 и ZF=0 – Если не ниже/не равно.
Термины Выше, Не ниже, для без знаковых чисел.
JAE – проверяют значение CF=0 – выше/равно
JNB — проверяют значение CF=0
JNC — проверяют значение CF=0
JB — проверяют значение CF=1 — Ниже
JNAE- проверяют значение CF=1
JC — проверяют значение CF=1
JBE – проверяют значение CF=1 и ZF=1 – Ниже/равно
JNA — проверяют значение CF=1 и ZF=1
JE — проверка значение ZF=1 — Равно
JZ — проверка значение ZF=1
JNZ – проверяет на ZF≠0 – не равно 0
LOOP imm – CX-1 и передача управления на некоторое смещение, однооперандная команда, еще параметром называется непосредственное назначение, где-то выше loop должна быть метка, до тех пор пока не 0 происходит передача управления.
LOOPE – генерируют один и тот же код.
LOOPZ – кроме CX проверяется значение ZF==1 (результат предыдущей операции не 0). Если CX!=0 и ZF==1 то передача управления.
LOOPNE \ LOOPNZ – отрицание, CX==0 и ZF!=1
Вызов и возврат из подпрограмм.
CALL imm – программист вводит название функции, компилятор преобразует в адрес.
Подпрограмма выполняет свою логику, возможно использует фактические параметры из стека. По окончании логики функция помещает результат.
RET – целочисленное в AX, вещественное FPU и чотатам.
Команда RET может быть безоперандной и однооперандной RET imm
RET извлекает с вершины стека адрес точки возврата возвращает управление в этот адрес и тогда очистку стека от фактических параметров должен произвести обвязочный код (СИ конвенция).
Если с операндом то смещение на чотатам байт и очищается стек (Паскаль конвенция).
interruptеры.
INT n
n – в команде int это номер в команде которых необходимо обработать, номер из таблицы векторов прерываний, в результате процессор запоминает текущее состояние, которое прерывается, чотатам.
Интераптеры изменяют IP и передача управление в другой сегмент кода. Команды безусловного перехода которые jump.
Обработка цепочек.
Если нам надо произвести одинаковые действие над двумя массивами то мы предполагаем наличие циклов, но наличие цикла это однозначно ветвление и сброс с конвейера. Цепочечные команды конвейер не сбрасывают, позволяют реализовать часть циклических операций в одной строке.
Изменяют SI DI либо вперед либо назад, сдвигают начало массива и конец массива в значении от индекса.
Загружает регистры CX SI DI, потом вызываем команду цепочек и в одной строке повторяем все телодвижения над массивами, конвейер при этом не сбрасывается.
REP – повторяет чотатам
REPE/REPZ — проверяется флаг ZF
MOVS dst, src ; передает значение из источника в назначение
MOVSB – работа с байтами
MOVSW – работа со словами
MOVSD – работа с двойными словами.
CMPS dst, src ;не разрушающее вычитание, изменяются флаги, операнды не трогаются
SCAS dst ;однооперандная, вычитает содержимое просматриваемого массива и аккумулятора acc, dst. На равенство значения в аккумуляторе или не равенство.
STOS dst ;загружает в цепочку назначение цепочку аккумулятора. dst ç acc
LODS dst; acc ç dst
INS — ввод цепочек в некоторый порт
OUTS – вывод цепочек в некоторый порт
CLC (clear larry flag)
CMC (complementary carry)
STC (set carry)
CLD (clear Direction)
STD (Set Direction)
CLT (Clear Interrupt)
STI (Set Interrupt)
IF (Interrupt flag)
NOP (No Operation) – занимает по времени 1 такт процессора и по длине занимает 1 байт.
Работа с Ассемблером – Язык низкого уровня можно делать все что угодно в большинстве случаев затраты себя не окупают.
Ассемблер язык платформа зависимый. Это ограничивает сферы применения ассемблера:
1) Оптимизация (в большинстве по скорости);
2) Драйвера к железу.
3) Взлом программ.
Оптимизация. Возможно, оптимизация по скорости и возможно возникают иногда задачи оптимизации по объему памяти.
Требование к оптимизированным программам:
1) Результат работы оптимизированной программы должен совпадать с результатом работы до оптимизации.
2) Прирост производительности. (Можно разворачивать циклы, можно убирать условные переходы это дает прирост производительности в проценты)
SIMD (Simple Instruction Multiple Data)
Если мы можем входной поток данных (на вычислении) одной командой обрабатывать не одну, а множество операндов, то тогда мы получаем множественный так процессора.
Профайлером вылавливается самая напряженная функция, далее она оптимизируется (переписывается на ассемблер), далее компилируется в dll.
Шаблон ассемблерной dll:
.586 ; разрешать не привилегированные команды процессора 586
.XMM ; разрешает регистры XMM
PUBLIC _nameF@xx ;имя экспортируемой функции
mov al, 1 ; точка входа в dll
;если много функций то добавляем произвольно кол-во функций
push ebp ;стандартный заголовок функции
END_start@12 ;точка выхода и dll
Рекомендуемый редактор к программированию Notepad++ (npp)
Компиляция текстового файла:
Меняем .txt на .asl
ml.exe – создает объектный файл из текстового файла.
Для получения dll используется link32.exe – он из объектного файла делает файлы exp.lib и .dll (*.lnk)
в [name].lnk находится:
nameF – название нашей функции без искажения.
Модно создавать файл .bat или .cmd в который напишем
d:\work\..\ml.exe /c /coff /Cp /D_MASM ИМЯ.asm
d:\link32.exe ИМЯ.obj @ИМЯ.lnk
Рекомендуется перед началом компиляции удалять предыдущие результаты компилирования. .dll .obj exp
Рекомендуется создавать dll в другом каталоге
После компиляции скопировать
copy *.dll Куда/то/там
_ — подчеркивание, само!
XX – количество в байтах параметром передано функции.
Рекомендуется узнать, сколько параметров мы передаем, и написать прототип функции на языке высокого уровня.
Сравнить скорость работы функции на чистом си и с использование ассемблера.
По номеру варианта выбираем задание ассемблером программы.
Сделать Сишный эквивалент ассемблерной функции.
MMX (multi media edition)
8 регистров MMX:
Мантиссэ – регистры с плавающей запятой. 8BYTE
Декларируется что за одну операцию сложения в соответствующих словах результатов: мы получаем прирост производительности на упакованных байтах в 8 раз, на 4WORD в 4 раза и т.д. на учетверенных словах прирост производительности возможен также.
Набор команд:
movq dst,src ;копирует 64 бита 1QWORD из источника в назначение один из операторов должен быть MMX
Упаковка со знаком насыщения:
PACKSSWB mmx,src ; слова в байты
приемник регистр MMX
WB – копирует 4 слова со знаком из приемника в 4 младших байта приемника и копирует 4 слова из источника в старшие 4 байта приемника.
При этом если значение слова превышает 127, то в байт записывается 127. +127 d=7F h
Если меньше чем -128 то записывается минимальная из возможных -128. -128d=80h
Эти операции называются операциями насыщения.
Упаковка знаковая с насыщением слова в байты
Упаковка знаковая с насыщением двойных слов в слова
PACKSSWB – копирует 4 слова со знаком из приемника в 4 младших байта приемника и копирует 4 слова со знаком из источника в 4 старших байта приемника.
Команда PACKSSDW – тоже самое только из двойных слов в слова.
Упаковка без знакового насыщения.
PACKUSWB mmx, src.
Упаковывает без знаковые с насыщением слова в байты.
если больше 255d=FFh если меньше 0=0
РАСПАКОВКА ЧЕГОТОТАМ ТАМ ЧОТО ТАМ. Самостоятельно
Дата добавления: 2014-01-04 ; Просмотров: 2599 ; Нарушение авторских прав? ;
Нам важно ваше мнение! Был ли полезен опубликованный материал? Да | Нет
АСМ-модуль
Фабрика корпусной мебели «АСМ-Модуль»
Открытие фабрики «АСМ-Модуль» состоялось 1 августа 2008 года.
Фабрика выпускает спальные гарнитуры, гостиные, прихожие,малые формы.
Все серии мебели являются модульными – Вы можете собрать любой комплект по собственному желанию.
Фабрика «АСМ-Модуль» – это производство европейского уровня, гарантирующее точность выполнения технологических процессов на уровне международных стандартов. В процессе производства используются только проверенные материалы и надёжная мебельная фурнитура. Каждая стадия производства контролируется четырёхуровневой системой ОТК, повышающей надёжность выпускаемой продукции.
Фабрика оснащена передовым немецким оборудованием фирмы Homag, в том числе: форматно-раскроечный центр, обрабатывающий центр, фрезерные станки, мембранно-вакуумный пресс и другое оборудование, позволяющее выпускать собственные фасады и изголовья из МДФ, обрабатывать самые сложные детали и воплощать в жизнь идеи наших дизайнеров.
Вся продукция изготавливается из высококачественных комплектующих: ЛДСП Lamarty (Россия), кромка ПВХ Rehau (Германия), фурнитура Hettiсh (Германия), Boyard. Это обеспечивает производство эргономичной, функциональной, безопасной для здоровья, экологически чистой, не токсичной мебели.
Производство и склады фабрики расположены в городе Екатеринбург, столице Урала, а объёмы выпускаемой продукции позволяют охватить города не только Свердловской области, но и другие города России, а также стран ближнего зарубежья.
Наш девиз остаётся всегда неизменным: «АСМ-Модуль» – Для тех, кто любит свой дом!
HackWare.ru
Этичный хакинг и тестирование на проникновение, информационная безопасность
Введение в Ассемблер [черновик]
[В ПРОЦЕССЕ НАПОЛНЕНИЯ И РЕДАКТИРОВАНИЯ]
Оглавление
Руководство по программированию на Ассемблер
1. Введение в Ассемблер
1.1 Для кого эти уроки по ассемблеру
Что нужно для изучения Ассемблера
Что такое язык Ассемблер?
Преимущества языка Ассемблер
2. Системы счисления
Основные характеристики аппаратной составляющей ПК
Двоичная система счисления
Шестнадцатеричная система счисления
Отрицательные двоичные числа
Адресация данных в памяти
3. Настройка рабочего окружения для Ассемблер
Настройка локального рабочего окружения
4. Основы синтаксиса Ассемблера
Компиляция и связывание (Linking) программы на Ассемблере в NASM
5. Ассемблер: сегменты памяти
6. Ассемблер: регистры (Registers)
7. Ассемблер: Системные вызовы
Системные вызовы Linux
8. Ассемблер: Режимы адресации
Адресации на регистр
Адресация на память
Прямая адресация со смещением
Косвенная адресация на память
9. Ассемблер: Переменные
Выделение пространства хранения для инициализированных данных
Выделение дискового пространства для неинициализированных данных
10. Ассемблер: Константы
11. Ассемблер: Арифметические инструкции
Инструкции ADD и SUB
12. Ассемблер: Логические инструкции
Инструкция AND (И)
13. Ассемблер: Условия
Данный материал — это азы языка программирования Ассемблер для абсолютных новичков. Здесь говориться о том, как написать программу на Ассемблере, приводятся основные команды Ассемблера, имеются примеры программа на Ассемблер и подробно описано как скомпилировать первую программу.
В целом материал является переводом «Assembly — Introduction» — небольшого учебника, в котором рассматриваются основы языка Ассемблер, но также имеются дополнения — некоторые вопросы рассмотрены более подробно.
Если у вас есть опыт изучения или даже программирования на других языках, всё равно Ассемблер потребует понимания новых концепций.
Руководство по программированию на Ассемблер
Язык Ассемблер — это низкоуровневый язык программирования для компьютеров или других программируемых устройств, он специфичен для конкретной компьютерной архитектуры центрального процессора, что отличает его от большинства высокоуровневых языков программирования, которые обычно портативны среди разных систем. Язык Ассемблер преобразуется в исполняемый машинный код с помощью служебной программы, называемой ассемблером, такой как NASM, MASM и т. д.
Для кого эти уроки по ассемблеру
Этот учебник был разработан для тех, кто хочет изучить основы программирования на Ассемблере с нуля. Из этих уроков вы получите достаточное представление о программировании на Ассемблере, благодаря которому вы сможете продолжить обучения в данной области и подняться на высокий уровень знаний.
Что нужно для изучения Ассемблера
Прежде чем приступить к этому учебному пособию, вы должны иметь базовые знания по терминологии компьютерного программирования. Базовое понимание любого из языков программирования поможет вам понять концепции программирования на Ассемблере и быстро продвигаться в процессе обучения.
Что такое язык Ассемблер?
Каждый персональный компьютер имеет микропроцессор, который управляет арифметической, логической и контрольной активностью.
Каждая семья процессоров имеет свой собственный набор инструкций для обработки различных операций, таких как получения ввода с клавиатуры, отображение информации на экране и выполнения различных других работ. Этот набор инструкций называется «инструкции машинного языка» (‘machine language instructions’).
Процессор понимает только инструкции машинного языка, которые являются строками из единиц и нулей. При этом машинный язык слишком непонятный и сложный для использования его в разработки программного обеспечения. И низкоуровневый язык Ассемблер предназначен для определённый групп процессоров, он представляет различные инструкции в символическом коде и более понятной форме.
Преимущества языка Ассемблер
Знание языка ассемблера позволяет понять:
- Как программы взаимодействуют с ОС, процессором и BIOS;
- Как данные представлены в памяти и других внешних устройствах;
- Как процессор обращается к инструкции и выполняет её;
- Как инструкции получают доступ и обрабатывают данные;
- Как программа обращается к внешним устройствам.
Другие преимущества использования ассемблера:
- Программы на нём требует меньше памяти и времени выполнения;
- Это упрощает сложные аппаратные задачи;
- Подходит для работ, в которых время выполнения является критичным;
- Он наиболее подходит для написания подпрограмм обработки прерываний и других программ, полностью находящихся в оперативной памяти.
Системы счисления
Основные характеристики аппаратной составляющей ПК
Каждый компьютер содержит процессор и оперативную память. Процессор содержит регистры — компоненты, которые содержат данные и адреса. Для выполнения программы, система копирует её с устройства постоянного хранения во внутреннюю память. Процессор выполняет инструкции программы.
Фундаментальной единицей компьютерного хранилища является бит. Он может быть в состоянии Включён (1) или Выключен (0). Группа из девяти связанных битов составляет байт, из которых восемь бит используются для данных, а последний используется для контроля чётности. Согласно правилу чётности, количество битов, которые Включены (1) в каждом байте, всегда должно быть нечётным.
Таким образом, бит чётности используется для того, чтобы сделать количество битов в байте нечётным. Если соотношение является чётным, система предполагает, то что произошла ошибка соотношения (хотя и редко), которая могла быть вызвана неисправностью оборудования или электрическими помехами.
Процессор поддерживает следующие размеры данных —
- Word: 2-байтовый элемент данных
- Doubleword: a 4-байтовый (32 бита) элемент данных
- Quadword: 8-байтовый (64 бита) элемент данных
- Paragraph: 16-байтовая (128 бита) область
- Kilobyte: 1024 байт
- Megabyte: 1,048,576 байт
Двоичная система счисления
В каждой системе счисления используются позиционные обозначения, то есть каждая позиция, в которой записана цифра, имеет различное позиционное значение. Каждая позиция — это степень базы, которая равна 2 для двоичной системы счисления, и эти степени начинаются с 0 и увеличиваются на 1.
В следующей таблице приведены позиционные значения для 8-битного двоичного числа, где все биты установлены в положение ON (Включено).
Значение бита | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
---|---|---|---|---|---|---|---|---|
Значение позиции как степень основания 2 | 128 | 64 | 32 | 16 | 8 | 4 | 2 | 1 |
Номер бита | 7 | 6 | 5 | 4 | 3 | 2 | 1 |
Значение двоичного числа, как и в десятичном, зависит от составляющих его цифр и расположения этих цифр. Но в двоичном числе используются только цифры 1 и 0, и расположение цифр имеет другое значение степени. Первая цифра, как и в десятичном числе, может означать 0 или 1. Вторая цифра (смотрим число справа на лево) может означать 2 (если этот бит установлен на 1) или 0 (если бит установлен на 0). Третья цифра (смотрим число справа на лево) может означать 4 (если этот бит установлен на 1) или 0 (если бит установлен на 0). И так далее. В десятичном числе значение каждого символа нужно умножить на 10 в степени порядкового номера этой цифры за минусом единицы.
То есть число 1337 это 1 * 10 3 + 3 * 10 2 + 3 * 10 1 + 7 * 10 0 = 1337
В двоичной системе всё точно также, только вместо десятки в степени порядкового номера за минусом единицы, нужно использовать двойку — вот и всё!
Допустим число 110101 и мы хотим узнать, сколько это будет в десятичной системе счисления, для этого достаточно выполнить следующее преобразование:
1 * 2 5 * + 1 * 2 4 + 0 * 2 3 + 1 * 2 2 + 0 * 2 1 + 1 * 2 0 = 1 * 32 + 1 * 16 + 0 * 8 + 1 * 4 + 0 * 2 + 1 * 1 = 53
Итак, значение бинарного числа основывается на наличии битов 1 и их позиционном значении. Поэтому значение числа 11111111 в двоичной системе является:
1 + 2 + 4 + 8 +16 + 32 + 64 + 128 = 255
Кстати, это то же самое, что и 2 8 — 1.
Шестнадцатеричная система счисления
Шестнадцатеричная система счисления использует основание 16. Цифры в этой системе варьируются от 0 до 15. По соглашению, буквы от A до F используются для представления шестнадцатеричных цифр, соответствующих десятичным значениям с 10 по 15.
Шестнадцатеричные числа в вычислениях используются для сокращения длинных двоичных представлений. По сути, шестнадцатеричная система счисления представляет двоичные данные, деля каждый байт пополам и выражая значение каждого полубайта. В следующей таблице приведены десятичные, двоичные и шестнадцатеричные эквиваленты —
Десятичное число | Двоичный вид | Шестнадцатеричный вид |
---|---|---|
1 | 1 | 1 |
2 | 10 | 2 |
3 | 11 | 3 |
4 | 100 | 4 |
5 | 101 | 5 |
6 | 110 | 6 |
7 | 111 | 7 |
8 | 1000 | 8 |
9 | 1001 | 9 |
10 | 1010 | A |
11 | 1011 | B |
12 | 1100 | C |
13 | 1101 | D |
14 | 1110 | E |
15 | 1111 | F |
Чтобы преобразовать двоичное число в его шестнадцатеричный эквивалент, разбейте его на группы по 4 последовательные группы в каждой, начиная справа, и запишите эти группы в соответствующие цифры шестнадцатеричного числа.
Пример — двоичное число 1000 1100 1101 0001 эквивалентно шестнадцатеричному — 8CD1
Чтобы преобразовать шестнадцатеричное число в двоичное, просто запишите каждую шестнадцатеричную цифру в её 4-значный двоичный эквивалент.
Пример — шестнадцатеричное число FAD8 эквивалентно двоичному — 1111 1010 1101 1000
Отрицательные двоичные числа
Компьютерные процессы действуют по своей логике и своим алгоритмам. И привычные нам операции вычитания, деления, умножения выполняются необычным для нас, но удобным для микропроцессора способом.
Удобством для арифметических действий в процессоре обусловлено то, как записываются отрицательные двоичные числа. Вы должны помнить из курса информатики, что в одном байте содержится 8 бит. Но старший бит используется для установки знака. Чтобы правильно прочесть число, а также правильно поменять его знак, нужно выполнять следующие правила:
Во-первых, нужно помнить, что если старшие биты (крайние слева), равны нулю, то их иногда не записывают. Например, восьмибитное число 10 (в десятичной системе счисления оно равно 2), также можно записать как 0000 0010. Обе эти записи означают число 2.
Если старший бит равен нулю, то это положительное число. Например, возьмём число 110. В десятичной системе счисления это 6. Данное число является положительным или отрицательным? На самом деле, однозначно на этот вопрос можно ответить только зная разрядность числа. Если это восьмиразрядное число, то его полная запись будет такой: 0000 0110. Как можно увидеть, старший бит равен нулю, следовательно, это положительное число.
Для трёхбитовых чисел было бы справедливо следующее:
Десятичное значение | |
---|---|
000 | |
1 | 001 |
2 | 010 |
3 | 011 |
-4 | 100 |
-3 | 101 |
-2 | 110 |
-1 | 111 |
Как вы должны были понять после анализа предыдущей таблицы, для смены знака недостаточно просто поменять единицу на ноль — для преобразования числа в отрицательное, а также для чтения отрицательного числа существуют особые правила.
Отрицательные двоичные числа записываются без знака минус и для получения этого же числа со знаком минус (то есть для получения числа в Дополненном коде) нужно выполнить два действия:
- нужно переписать его полную форму с противоположным значением битов (то есть для единиц записываются нули, а для нулей записываются единицы)
- и затем добавить к этому числу 1.
Число 53 | 00110101 |
Замена битов на противоположные | 11001010 |
Добавляем 1 | 0000000 1 |
Число -53 | 11001011 |
На русском языке такая форма записи называется Дополнительный код, в англоязычной литературе это называется Two’s complement.
Примеры восьмибитного двоичного числа в Дополнительном коде (старший бит указывает на знак):
Десятичное значение | Двоичное значение трёхбитового числа со знаком (в представлении Дополнительный код) |
---|---|
0000 0000 | |
1 | 0000 0001 |
2 | 0000 0010 |
126 | 0111 1110 |
127 | 0111 1111 |
−128 | 1000 0000 |
−127 | 1000 0001 |
−126 | 1000 0010 |
−2 | 1111 1110 |
−1 | 1111 1111 |
Десятичное представление |
|
---|---|
127 | 0111 1111 |
1 | 0000 0001 |
0000 0000 | |
-0 | — |
-1 | 1111 1111 |
-2 | 1111 1110 |
-3 | 1111 1101 |
-4 | 1111 1100 |
-5 | 1111 1011 |
-6 | 1111 1010 |
-7 | 1111 1001 |
-8 | 1111 1000 |
-9 | 1111 0111 |
-10 | 1111 0110 |
-11 | 1111 0101 |
-127 | 1000 0001 |
-128 | 1000 0000 |
Числа в дополненном коде удобно применять для вычитания — это будет показано далее.
Для преобразования отрицательного числа, записанного в дополнительном коде, в положительное число, записанное в прямом коде, используется похожий алгоритм.
Рассмотрим пример с числом -5. Запись отрицательного восьмибитного числа:
Инвертируем все разряды отрицательного числа -5, получая таким образом:
Добавив к результату 1 получим положительное число 5 в прямом коде:
И проверим, сложив с дополнительным кодом
0000 0101 + 1111 1011 = 1 0000 0000, десятый разряд выбрасывается, то есть получается 0000 0000, то есть 0. Следовательно, преобразование выполнено правильно, так как 5 + (-5) = 0.
Двоичная арифметика
Следующая таблица иллюстрирует четыре простых правила для двоичного сложения:
(i) | (ii) | (iii) | (iv) |
---|---|---|---|
1 | |||
1 | 1 | 1 | |
+0 | +0 | +1 | +1 |
=0 | =1 | =10 | =11 |
Эту таблицу нужно читать по столбцам сверху вниз. В первом столбце складываются 0 и 0 — в результате получается 0. Во втором примере складываются 1 и 0 (или 0 и 1 — без разницы), в результате получается 1. В третьем столбце складываются две единицы — в результате в текущей позиции получается 0, но на одну позицию влево добавляется единица. Если в этой позиции уже есть единица — то применяется это же правило, то есть в позиции пишется 0, и 1 передаётся влево. В четвёртом примере складываются три единицы — в результате, в текущей позиции записывается 1, и ещё одна 1 передаётся влево.
Десятичные | Двоичные |
---|---|
60 | 00111100 |
+42 | 00101010 |
102 | 01100110 |
Рассмотрим, как делается вычитание.
Для вычитания число, которое вычитается, записывается в форме Дополнительного кода, а затем эти два числа складываются.
Пример: Вычесть 42 из 53
Число 53 | 00110101 |
Число 42 | 00101010 |
Инвертируем биты 42 | 11010101 |
Добавляем 1 | 00000001 |
Число -42 | 11010110 |
Выполняем операцию: 53 — 42 = 11 | 00110101 + 11010110 = |
Бит который вызывает переполнение — крайней левый, девятый по счёту, просто отбрасывается.
Адресация данных в памяти
Процесс, посредством которого процессор управляет выполнением инструкций, называется циклом fetch-decode-execute (выборки-декодирования-выполнения) или циклом выполнения (execution cycle). Он состоит из трёх непрерывных шагов —
- Извлечение инструкции из памяти
- Расшифровка или идентификация инструкции
- Выполнение инструкции
Процессор может одновременно обращаться к одному или нескольким байтам памяти. Давайте рассмотрим шестнадцатеричное число 0725H (буква H означает, что перед нами шестнадцатеричное число). Для этого числа потребуется два байта памяти. Байт старшего разряда или старший значащий байт — 07, а младший байт — 25.
Процессор хранит данные в последовательности обратного байта, то есть байт младшего разряда хранится в низком адресе памяти и байт старшего разряда в старшем адресе памяти. Таким образом, если процессор переносит значение 0725H из регистра в память, он сначала перенесёт 25 на нижний адрес памяти и 07 на следующий адрес памяти.
Когда процессор получает числовые данные из памяти для регистрации, он снова переворачивает байты. Есть два вида адресов памяти:
- Абсолютный адрес — прямая ссылка на конкретное место.
- Адрес сегмента (или смещение) — начальный адрес сегмента памяти со значением смещения.
Настройка рабочего окружения для Ассемблер
Настройка локального рабочего окружения
Язык ассемблера зависит от набора команд и архитектуры процессора. В этом руководстве мы сосредоточимся на процессорах Intel-32, таких как Pentium. Чтобы следовать этому уроку, вам понадобится:
- ПК IBM или любой другой совместимый компьютер
- Копия операционной системы Linux
- Копия программы ассемблера NASM
Есть много хороших ассемблерных программ, таких как:
- Microsoft Assembler (MASM)
- Borland Turbo Assembler (TASM)
- GNU ассемблер (GAS)
Мы будем использовать ассемблер NASM, так как он:
- Бесплатный
- Хорошо задокументированный — вы получите много информации в сети.
- Может использоваться как в Linux, так и в Windows.
Установка NASM
Если вы выбираете «Инструменты разработки» при установке Linux, вы можете установить NASM вместе с операционной системой Linux, и вам не нужно загружать и устанавливать его отдельно. Чтобы проверить, установлен ли у вас NASM, сделайте следующее:
Откройте терминал Linux.
и нажмите клавишу ВВОД.
Если он уже установлен, появляется строка типа
В противном случае вы увидите просто
значит вам нужно установить NASM.
NASM должен присутствовать в стандартных репозиториях, поэтому попробуйте поискать там этот пакет и установить его.
Например, для установки в Debian, Ubuntu, Linux Mint, Kali Linux и их производные выполните:
Для установки в Arch Linux, BlackArch и их производные выполните:
Чтобы установить NASM из исходного кода, сделайте следующее:
Проверьте веб-сайт ассемблера (NASM) на последнюю версию.
Загрузите исходный архив Linux nasm-X.XX.ta.gz, где X.XX — номер версии NASM в архиве.
Распакуйте архив в каталог, который создаст подкаталог nasm-X.XX.
Перейдите к nasm-X.XX
Этот скрипт оболочки найдёт лучший компилятор C для использования и сделает настройки в соответствии с Makefiles.
чтобы создать двоичные файлы nasm и ndisasm.
чтобы установить nasm и ndisasm в /usr/local/bin и установить справочные страницы (man).
Это должно установить NASM в вашей системе. Кроме того, вы можете использовать RPM-дистрибутив для Fedora Linux. Эта версия проще в установке, просто дважды щёлкните файл RPM.
Основы синтаксиса Ассемблера
Программу на языке Ассемблер можно разделить на три раздела:
- Раздел data
- Раздел bss
- Раздел text
Раздел data
Раздел data используется для объявления инициализированных данных или констант. Эти данные не изменяются во время выполнения. В этом разделе вы можете объявить различные постоянные значения, имена файлов или размер буфера и т. д.
Синтаксис объявления раздела data:
Раздел BSS
Секция bss используется для объявления переменных. Синтаксис объявления раздела bss:
Раздел text
Раздел text используется для хранения самого кода. Этот раздел должен начинаться с объявления global _start, которое сообщает ядру, где начинается выполнение программы.
Синтаксис объявления раздела text:
Комментарии
Комментарий на ассемблере начинается с точки с запятой (;). Он может содержать любой печатный символ, включая пробел. Он может появиться в строке сам по себе, например:
или в той же строке вместе с инструкцией, например:
Операторы Ассемблера
Программы на ассемблере состоят из трёх типов операторов:
- Исполняемые инструкции или инструкции,
- Директивы ассемблера или псевдооперации (pseudo-ops), и
- Макросы.
Исполняемые инструкции или просто инструкции говорят процессору, что делать. Каждая инструкция состоит из кода операции (opcode). Каждая исполняемая инструкция генерирует одну инструкцию на машинном языке.
Директивы ассемблера или псевдооперации говорят ассемблеру о различных аспектах процесса сборки. Они не являются исполняемыми и не генерируют инструкции машинного языка.
Макросы — это в основном механизм подстановки текста.
Синтаксис операторов ассемблера
Операторы языка ассемблера вводятся по одной инструкции в каждой строке. Каждое утверждение имеет следующий формат:
Поля в квадратных скобках являются необязательными. Основная инструкция состоит из двух частей: первая — это имя инструкции (или мнемоника), которая должна быть выполнена, а вторая — операнды или параметры команды.
Ниже приведены некоторые примеры типичных операторов языка ассемблера.
Программа Hello World на Ассамблее
Следующий код на ассемблере выводит на экран строку «Hello World»:
Когда приведённый выше код скомпилирован и выполнен, он даст следующий результат:
Компиляция и связывание (Linking) программы на Ассемблере в NASM
Убедитесь, что вы установили путь до исполнимых файлов nasm и ld в вашей переменной окружения PATH (в Linux это уже сделано). Теперь пройдите следующие шаги для компиляции и связывания приведённой выше программы:
- Наберите приведённый выше код используя текстовый редактор и сохраните как hello.asm.
- Убедитесь, что вы в той же самой директории, где вы сохранили hello.asm.
- Для сборки вашей программы выполните:
- Если в коде присутствуют какие-либо ошибки, то на этом этапе вам будет выведено сообщение о них. Если ошибок нет, то будет создан объектный файл вашей программы с именем hello.o.
- Для связывания объектного файла и создания исполнимого файла с именем hello выполните:
Выполните программу набрав:
Если вы всё сделали правильно, то она отобразит на экране ‘Hello, world!’.
Возможно, вас интересует, что такое связывание (Linking) и зачем оно требуется после сборки программы. Если коротко, то на этом этапе объектные файлы (если их несколько) собираются в один исполнимый файл, также благодаря этому процессу исполнимый файл теперь может использовать библиотеки. Линкеру указывается (обычно) целевой исполнимый формат. Если совсем коротко — это просто нужно. Я не буду в этом базовом курсе по ассемблеру останавливаться на этом более подробно — если вас интересует эта тема, то вы всегда сможете найти по ней дополнительную информацию в Интернете.
Ассемблер: сегменты памяти
Мы уже рассмотрели три раздела программы на ассемблере. Эти разделы также представляют различные сегменты памяти.
Обратите внимание, что если вы замените ключевое слово section на слово segment, вы получите тот же самый результат. Попробуйте этот код:
После компиляции и выполнения вышеприведённого кода он даст следующий результат:
Сегменты памяти
Модель сегментированной памяти делит системную память на группы независимых сегментов, на которые ссылаются указатели, расположенные в регистрах сегментов. Каждый сегмент используется для хранения данных определённого типа. Один сегмент используется для хранения кодов команд, другой — для хранения элементов данных, а третий — для программного стека.
В свете вышеизложенного мы можем выделить различные сегменты памяти, такие как:
- Сегмент Data. Он представлен разделом .data и .bss. Раздел .data используется для объявления области памяти, где хранятся элементы данных для программы. Этот раздел не может быть расширен после объявления элементов данных, и он остаётся статическим во всей программе.
Раздел .bss также является разделом статической памяти, который содержит буферы для данных, которые будут объявлены позже в программе. Эта буферная память заполнена нулями. - Сегмент Code. Он представлен разделом .text. Он определяет область в памяти, в которой хранятся коды команд. Это также фиксированная зона.
- Stack — этот сегмент содержит значения данных, передаваемые функциям и процедурам в программе.
Ассемблер: регистры (Registers)
Операции процессора в основном связаны с обработкой данных. Эти данные могут быть сохранены в памяти и доступны оттуда. Однако чтение данных из памяти и её сохранение в памяти замедляет процессор, поскольку включает сложные процессы отправки запроса данных через шину управления и в блок хранения памяти и получения данных по одному и тому же каналу.
Для ускорения работы процессора процессор включает в себя несколько мест хранения внутренней памяти, называемых регистрами (registers).
Регистры хранят элементы данных для обработки без необходимости доступа к памяти. Ограниченное количество регистров встроено в чип процессора.
Регистры процессора
В архитектуре IA-32 имеется десять 32-разрядных и шесть 16-разрядных процессорных регистров. Регистры сгруппированы в три категории:
- Общие регистры,
- Регистры управления и
- Сегментные регистры.
Общие регистры далее делятся на следующие группы:
- Регистры данных,
- Регистры указателя и
- Индексные регистры.
Регистры данных
Четыре 32-битных регистра данных используются для арифметических, логических и других операций. Эти 32-битные регистры можно использовать тремя способами:
- Как полные 32-битные регистры данных: EAX, EBX, ECX, EDX.
- Нижние половины 32-битных регистров могут использоваться как четыре 16-битных регистра данных: AX, BX, CX и DX.
- Нижняя и верхняя половины вышеупомянутых четырёх 16-битных регистров могут использоваться как восемь 8-битных регистров данных: AH, AL, BH, BL, CH, CL, DH и DL.
Некоторые из этих регистров данных имеют конкретное применение в арифметических операциях.
AX — основной аккумулятор; он используется во вводе/выводе и большинстве арифметических инструкций. Например, в операции умножения один операнд сохраняется в регистре EAX или AX или AL в соответствии с размером операнда.
BX известен как базовый регистр, поскольку его можно использовать при индексированной адресации.
CX известен как регистр подсчёта, так как регистры ECX, CX хранят счётчик циклов в итерационных операциях.
DX известен как регистр данных. Он также используется в операциях ввода/вывода. Он также используется с регистром AX вместе с DX для операций умножения и деления с большими значениями.
Регистры указателя
Регистры указателя являются 32-разрядными регистрами EIP, ESP и EBP и соответствующими 16-разрядными правыми частями IP, SP и BP. Есть три категории регистров указателей:
- Указатель инструкций (IP) — 16-битный регистр IP хранит адрес смещения следующей команды, которая должна быть выполнена. IP вместе с регистром CS (как CS:IP) даёт полный адрес текущей инструкции в сегменте кода.
- Указатель стека (SP) — 16-разрядный регистр SP обеспечивает значение смещения в программном стеке. SP в сочетании с регистром SS (SS:SP) относится к текущей позиции данных или адреса в программном стеке.
- Базовый указатель (BP) — 16-битный регистр BP в основном помогает ссылаться на переменные параметра, передаваемые подпрограмме. Адрес в регистре SS объединяется со смещением в BP, чтобы получить местоположение параметра. BP также можно комбинировать с DI и SI в качестве базового регистра для специальной адресации.
Индексные регистры
32-разрядные индексные регистры ESI и EDI и их 16-разрядные крайние правые части. SI и DI, используются для индексированной адресации и иногда используются для сложения и вычитания. Есть два набора указателей индекса:
- Исходный индекс (SI) — используется в качестве исходного индекса для строковых операций.
- Указатель назначения (DI) — используется как указатель назначения для строковых операций.
Регистры управления
Регистр указателя 32-битной инструкции и регистр 32-битных флагов рассматриваются как регистры управления.
Многие инструкции включают сравнения и математические вычисления и изменяют состояние флагов, а некоторые другие условные инструкции проверяют значение этих флагов состояния, чтобы перенести поток управления в другое место.
Популярные биты флага:
- Флаг переполнения (OF) — указывает на переполнение старшего бита (крайнего левого бита) данных после арифметической операции со знаком.
- Флаг направления (DF) — определяет направление влево или вправо для перемещения или сравнения строковых данных. Когда значение DF равно 0, строковая операция принимает направление слева направо, а когда значение равно 1, строковая операция принимает направление справа налево.
- Флаг прерывания (IF) — определяет, будут ли игнорироваться или обрабатываться внешние прерывания, такие как ввод с клавиатуры и т. д. Он отключает внешнее прерывание, когда значение равно 0, и разрешает прерывания, когда установлено значение 1.
- Trap Flag (TF) — позволяет настроить работу процессора в одношаговом режиме. Программа DEBUG, которую мы использовали, устанавливает флаг прерывания, чтобы мы могли пошагово пройтись по инструкциям — по одной инструкции за раз.
- Флаг знака (SF) — показывает знак результата арифметической операции. Этот флаг устанавливается в соответствии со знаком элемента данных после арифметической операции. Знак указывается старшим левым битом. Положительный результат очищает значение SF до 0, а отрицательный результат устанавливает его в 1.
- Нулевой флаг (ZF) — указывает результат арифметической операции или операции сравнения. Ненулевой результат очищает нулевой флаг до 0, а нулевой результат устанавливает его в 1.
- Вспомогательный флаг переноса (AF) — содержит перенос с бита 3 на бит 4 после арифметической операции; используется для специализированной арифметики. AF устанавливается, когда 1-байтовая арифметическая операция вызывает перенос из бита 3 в бит 4.
- Флаг чётности (PF) — указывает общее количество 1-битов в результате, полученном в результате арифметической операции. Чётное число 1-бит очищает флаг чётности до 0, а нечётное число 1-битов устанавливает флаг чётности в 1.
- Флаг переноса (CF) — содержит перенос 0 или 1 из старшего бита (крайнего слева) после арифметической операции. Он также хранит содержимое последнего бита операции shift или rotate.
В следующей таблице указано положение битов флага в 16-битном регистре флагов:
Флаг: | O | D | I | T | S | Z | A | P | C | |||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Номер бита: | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 |
Сегментные регистры
Сегменты — это специальные области, определённые в программе для хранения данных, кода и стека. Есть три основных сегмента:
- Сегмент Code — содержит все инструкции, которые должны быть выполнены. 16-битный регистр сегмента кода или регистр CS хранит начальный адрес сегмента кода.
- Сегмент Data — содержит данные, константы и рабочие области. 16-битный регистр сегмента данных или регистр DS хранит начальный адрес сегмента данных.
- Сегмент Stack — содержит данные и адреса возврата процедур или подпрограмм. Он реализован в виде структуры данных стека. Регистр сегмента стека или регистр SS хранит начальный адрес стека.
Помимо регистров DS, CS и SS существуют и другие регистры дополнительных сегментов — ES (дополнительный сегмент), FS и GS, которые предоставляют дополнительные сегменты для хранения данных.
При программировании на ассемблере программе необходим доступ к ячейкам памяти. Все области памяти в сегменте относятся к начальному адресу сегмента. Сегмент начинается с адреса, равномерно делимого на 16 или в шестнадцатеричном виде числа 10. Таким образом, крайняя правая шестнадцатеричная цифра во всех таких адресах памяти равна 0, что обычно не сохраняется в регистрах сегментов.
Сегментные регистры хранят начальные адреса сегмента. Чтобы получить точное местоположение данных или инструкции в сегменте, требуется значение смещения (или смещение). Чтобы сослаться на любую ячейку памяти в сегменте, процессор объединяет адрес сегмента в регистре сегмента со значением смещения местоположения.
Пример
Посмотрите на следующую простую программу, чтобы понять использование регистров в программировании на Ассемблере. Эта программа отображает 9 звёзд на экране вместе с простым сообщением.
После компиляции и выполнения эта программа выведет:
Ассемблер: Системные вызовы
Системные вызовы — это API для интерфейса между пространством пользователя и пространством ядра. Мы уже использовали системные вызовы sys_write и sys_exit для записи на экран и выхода из программы соответственно.
Системные вызовы Linux
Вы можете использовать системные вызовы Linux в ваших ассемблерных программах. Для использования системных вызовов Linux в вашей программе необходимо выполнить следующие шаги:
- Поместите номер системного вызова в регистр EAX.
- Сохраните аргументы системного вызова в регистрах EBX, ECX и т. д.
- Вызовите соответствующее прерывание (80h).
- Результат обычно возвращается в регистр EAX.
Существует шесть регистров, в которых хранятся аргументы используемого системного вызова. Это EBX, ECX, EDX, ESI, EDI и EBP. Эти регистры принимают последовательные аргументы, начиная с регистра EBX. Если существует более шести аргументов, ячейка памяти первого аргумента сохраняется в регистре EBX.
В следующем фрагменте кода показано использование системного вызова sys_exit:
В следующем фрагменте кода показано использование системного вызова sys_write:
Все системные вызовы перечислены в /usr/include/asm/unistd.h вместе с их номерами (значение, которое нужно указать в EAX перед вызовом int 80h). Точнее говоря, сейчас это файлы /usr/include/asm/unistd_32.h и /usr/include/asm/unistd_64.h.
Чтобы посмотреть содержимое файла /usr/include/asm/unistd_32.h:
Начало этого файла:
Чтобы получить справку по системным вызовам:
Чтобы получить справку по конкретному вызову, укажите вначале man 2, а затем название вызова. Например, чтобы узнать о вызове read:
Чтобы узнать о вызове mkdir:
В следующей таблице приведены некоторые системные вызовы, используемые в этом руководстве:
%eax | Имя | %ebx | %ecx | %edx | %esx | %edi |
---|---|---|---|---|---|---|
1 | sys_exit | int (целое число) | — | — | — | — |
2 | sys_fork | struct pt_regs | — | — | — | — |
3 | sys_read | unsigned int (целое беззнаковое число) | char * | size_t | — | — |
4 | sys_write | unsigned int (целое беззнаковое число) | const char * | size_t | — | — |
5 | sys_open | const char * | int (целое число) | int (целое число) | — | — |
6 | sys_close | unsigned int (целое беззнаковое число) | — | — | — | — |
Пример
Следующий пример читает число с клавиатуры и отображает его на экране:
Скомпилированный и запущенный вышеприведённый код даёт следующий результат:
Ассемблер: Режимы адресации
Большинство инструкций на ассемблере требуют обработки операндов. Адрес операнда предоставляет место, где хранятся данные, подлежащие обработке. Некоторые инструкции не требуют операнда, в то время как некоторые другие инструкции могут требовать один, два или три операнда.
Когда инструкции требуется два операнда, первый операнд обычно является пунктом назначения, который содержит данные в регистре или ячейке памяти, а второй операнд является источником. Источник содержит либо данные для доставки (немедленная адресация), либо адрес (в регистре или памяти) данных. Как правило, исходные данные остаются неизменными после операции.
Три основных режима адресации:
- Адресации на регистр
- Немедленная адресация
- Адресация на память
Адресации на регистр
В этом режиме адресации регистр содержит операнд. В зависимости от инструкции регистр может быть первым операндом, вторым операндом или обоими.
Поскольку обработка данных между регистрами не требует памяти, она обеспечивает самую быструю обработку данных.
Немедленная адресация
Непосредственный операнд имеет постоянное значение или выражение. Когда инструкция с двумя операндами использует немедленную адресацию, первый операнд может быть регистром или ячейкой памяти, а второй операнд является непосредственной константой. Первый операнд определяет длину данных.
Адресация на память
Когда операнды указываются в режиме адресации на память, требуется прямой доступ к основной памяти, обычно к сегменту данных. Этот способ адресации приводит к более медленной обработке данных. Чтобы найти точное местоположение данных в памяти, нам нужен начальный адрес сегмента, который обычно находится в регистре DS, и значение смещения. Это значение смещения также называется действующим адресом (effective address).
В режиме прямой адресации значение смещения указывается непосредственно как часть инструкции, обычно указывается именем переменной. Ассемблер вычисляет значение смещения и поддерживает таблицу символов, в которой хранятся значения смещения всех переменных, используемых в программе.
При прямой адресации в памяти один из операндов ссылается на ячейку памяти, а другой операнд ссылается на регистр.
Прямая адресация со смещением
Этот режим адресации использует арифметические операторы для изменения адреса. Например, посмотрите на следующие определения, которые определяют таблицы данных:
Следующие операции обращаются к данным из таблиц в памяти в регистрах:
Косвенная адресация на память
В этом режиме адресации используется способность компьютера Segment:Offset (Сегмент:Смещение). Обычно для этой цели используются базовые регистры EBX, EBP (или BX, BP) и регистры индекса (DI, SI), закодированные в квадратных скобках для ссылок на память.
Косвенная адресация обычно используется для переменных, содержащих несколько элементов, таких как массивы. Начальный адрес массива хранится, скажем, в регистре EBX.
В следующем фрагменте кода показано, как получить доступ к различным элементам переменной.
Инструкция MOV
Мы уже задействовали инструкцию MOV, которая используется для перемещения данных из одного пространства хранения в другое. Инструкция MOV принимает два операнда.
Синтаксис
Синтаксис инструкции MOV:
Инструкция MOV может иметь одну из следующих пяти форм:
Пожалуйста, обратите внимание, что:
- Оба операнда в операции MOV должны быть одинакового размера
- Значение исходного операнда остаётся неизменным
Инструкция MOV порой вызывает двусмысленность. Например, посмотрите на утверждения:
Не ясно, хотите ли вы переместить байтовый эквивалент или словесный эквивалент числа 110. В таких случаях целесообразно использовать спецификатор типа (type specifier).
В следующей таблице приведены некоторые общие спецификаторы типов:
Спецификатор типа | Байты |
---|---|
BYTE | 1 |
WORD | 2 |
DWORD | 4 |
QWORD | 8 |
TBYTE | 10 |
Пример
Следующая программа иллюстрирует некоторые из концепций, обсуждённых выше. Он сохраняет имя «Zara Ali» в разделе данных памяти, затем программно меняет его значение на другое имя «Nuha Ali» и отображает оба имени.
Когда приведённый выше код скомпилирован и выполнен, он даёт следующий результат:
Ассемблер: Переменные
NASM предоставляет различные директивы определения (define directives) для резервирования места для хранения переменных. Директива определения ассемблера используется для выделения пространства хранения. Его можно использовать для резервирования, а также для инициализации одного или нескольких байтов.
Выделение пространства хранения для инициализированных данных
Синтаксис для оператора распределения памяти для инициализированных данных:
Где имя-переменной — это идентификатор для каждого пространства хранения. Ассемблер связывает значение смещения для каждого имени переменной, определённого в сегменте данных.
Существует пять основных форм директивы определения:
Директива | Цель | Размер хранения |
---|---|---|
DB | Определить Byte | выделяет 1 байт |
DW | Определить Word | выделяет 2 байта |
DD | Определить Doubleword | выделяет 4 байта |
DQ | Определить Quadword | выделяет 8 байта |
DT | Определить Ten Bytes | выделяет 10 байта |
Ниже приведены некоторые примеры использования директив определения.
Пожалуйста, обратите внимание, что:
- Каждый байт символа хранится как его значение ASCII в шестнадцатеричном формате.
- Каждое десятичное значение автоматически преобразуется в его 16-разрядный двоичный эквивалент и сохраняется в виде шестнадцатеричного числа.
- Процессор использует little-endian порядок байтов.
- Отрицательные числа преобразуются в его представление Дополнительный код (рассмотрен выше).
- Короткие и длинные числа с плавающей запятой представлены с использованием 32 или 64 бит соответственно.
Следующая программа показывает использование директивы определения:
Когда приведённый выше код компилируется и выполняется, он даёт следующий результат:
Выделение дискового пространства для неинициализированных данных
Директивы резервирования используются для запаса места для неинициализированных данных. Директивы резервирования принимают один операнд, который определяет количество единиц пространства, которое будет зарезервировано. Каждая директива определения имеет связанную директиву резервирования.
Существует пять основных форм директив резервирования:
Директива | Цель |
---|---|
RESB | Зарезервировать Byte |
RESW | Зарезервировать Word |
RESD | Зарезервировать Doubleword |
RESQ | Зарезервировать Quadword |
REST | Зарезервировать 10 байт |
Множественность определений
Вы можете иметь несколько операторов определения данных в программе. Например:
Ассемблер выделяет непрерывную память для нескольких определений переменных.
Множественность инициализаций
Директива TIMES позволяет выполнить несколько инициализаций к одному и тому же значению. Например, массив с именем marks размера 9 может быть определён и инициализирован на начальное значение ноль с помощью следующего оператора:
Директива TIMES полезна при определении массивов и таблиц. Следующая программа отображает 9 звёздочек на экране:
Результат выполнения скомпилированной программы:
Ассемблер: Константы
NASM предоставляет несколько директив, определяющих константы. Мы уже использовали директиву EQU в предыдущих разделах. Особое внимание мы уделим трём директивам:
- EQU
- %assign
- %define
Директива EQU
Директива EQU используется для определения констант. Синтаксис директивы EQU следующий:
Затем вы можете использовать это постоянное значение в вашем коде, например:
Операндом оператора EQU может быть выражение:
Приведённый фрагмент кода определит AREA как 200.
Пример
Следующий пример иллюстрирует использование директивы EQU:
Скомпилированный и выполненный код даст следующие результаты:
Кстати, в коде программы мы использовали 0xA,0xD в качестве части строк. Точнее говоря, в качестве окончания строк. Как можно догадаться, это шестнадцатеричные цифры. При выводе на экран эти шестнадцатеричные цифры трактуются как коды символов ASCII. То есть, чтобы понять их значение, нужно заглянуть в таблицу ASCII символов, например в статье «ASCII и шестнадцатеричное представление строк. Побитовые операции со строками».
Там мы можем найти, что 0xA (в той таблице он обозначен как 0A) и означает он перевод строки. Во многих языках программирования символ обозначается как «\n». Нажатие на клавишу ↵ Enter при выводе текста переводит строку.
Что касается 0xD (там в таблице он обозначен как 0D) и означает enter / carriage return — возврат каретки. Во многих языках программирования — символ «CR» обозначается как «\r».
Итак, если вы программируете на каком либо языке, то последовательность из двух шестнадцатеричных чисел 0xA,0xD, соответствует последовательности «\n\r», то есть, упрощённо говоря, это универсальный способ (чтобы срабатывал и в Linux, и в Windows) перейти на новую строку.
Директива %assign
По аналогии с директивой EQU, директива %assign может использоваться для определения числовых констант. Эта директива допускает переопределение. Например, вы можете определить постоянную TOTAL следующим образом:
Позже в коде вы можете переопределить её так:
Эта директива чувствительна к регистру.
Директива %define
Директива %define позволяет определять как числовые, так и строковые константы. Эта директива похожа на #define в C. Например, вы можете определить постоянную PTR так:
Приведённый выше код заменяет PTR на [EBP+4].
Эта директива также допускает переопределение и учитывает регистр.
Ассемблер: Арифметические инструкции
Инструкция INC
Инструкция INC используется для увеличения операнда на единицу. Она работает с одним операндом, который может находиться либо в регистре, либо в памяти.
Инструкция INC имеет следующий синтаксис:
Операндом может быть 8-битный, 16-битный или 32-битный операнд.
Инструкция DEC
Инструкция DEC используется для уменьшения операнда на единицу. Она работает с одним операндом, который может находиться либо в регистре, либо в памяти.
Инструкция DEC имеет следующий синтаксис:
Операндом может быть 8-битный, 16-битный или 32-битный операнд.
Инструкции ADD и SUB
Команды ADD и SUB используются для выполнения простого сложения/вычитания двоичных данных размером в byte, word и doubleword, т.е. для сложения или вычитания 8-битных, 16-битных или 32-битных операндов соответственно.
Инструкции ADD и SUB имеют следующий синтаксис:
Инструкция ADD/SUB может выполняться между:
- Регистр к регистру
- Память к регистру
- Регистр к памяти
- Регистр к константе
- Память к константе
Однако, как и другие инструкции, операции с память-в-память невозможны с использованием инструкций ADD/SUB. Операция ADD или SUB устанавливает или очищает флаги переполнения (overflow) и переноса (carry).
Пример
В следующем примере программа спросит у пользователя две цифры; сохранит их в регистрах EAX и EBX, соответственно; сложит эти значения; сохранит результат в ячейке памяти «res» и, наконец, отобразит результат.
Скомпилированный и выполненный код даст следующие результаты:
Код программы сильно упрощается, если прописать значения переменных для арифметических действий прямо в самом коде:
Результат выполнения этого кода:
Инструкции MUL/IMUL
Есть две инструкции для умножения двоичных данных. Инструкция MUL (Multiply) обрабатывает беззнаковые данные, а IMUL (Integer Multiply) обрабатывает данные со знаком. Обе инструкции влияют на флаг переноса и переполнения.
Синтаксис для инструкций MUL/IMUL следующий:
Множимое в обоих случаях будет в аккумуляторе, в зависимости от размера множимоего и умножителя, и результат умножения также сохраняется в двух регистрах в зависимости от размера операндов. Следующий раздел объясняет инструкции MUL в трёх разных случаях:
Номер | Сценарии | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
1 |
Номер | Сценарии | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
1 |
Номер | Инструкция | Формат |
---|---|---|
1 | AND | AND операнд1, операнд2 |
2 | OR | OR операнд1, операнд2 |
3 | XOR | XOR операнд1, операнд2 |
4 | TEST | TEST операнд1, операнд2 |
5 | NOT | NOT операнд1 |
Первый операнд во всех случаях может быть либо в регистре, либо в памяти. Второй операнд может быть либо в регистре/памяти, либо в непосредственном (постоянном) значении. Однако операции память-и-память невозможны. Эти инструкции сравнивают или сопоставляют биты операндов и устанавливают флаги CF, OF, PF, SF и ZF.
Инструкция AND (И)
Инструкция AND используется для поддержки логических выражений путём выполнения побитовой операции AND. Побитовая операция AND возвращает 1, если совпадающие биты обоих операндов равны 1, в противном случае она возвращает 0. Например:
Операция AND может использоваться для очистки одного или нескольких битов. Например, допустим, регистр BL содержит 0011 1010. Если вам нужно очистить старшие биты до нуля, то вы выполняете операцию AND этого регистра с 0FH.
Давайте рассмотрим другой пример. Если вы хотите проверить, является ли данное число нечётным или чётным, простой тест будет проверять младший значащий бит числа. Если это 1, число нечётное, иначе число чётное.
Предполагая, что номер находится в регистре AL, мы можем написать:
Следующая программа иллюстрирует это.
Результат выполнения кода:
Измените значение в регистре ax на нечётную цифру, к примеру:
Программа будет отображать:
Точно так же очистить весь регистр вы можете сделав AND с 00H.
Инструкция OR
Инструкция OR (ИЛИ) используется для выполнения логической побитовой операции OR. Побитовый оператор OR возвращает 1, если совпадающие биты одного или обоих операндов равны единице. Возвращает 0, если оба бита равны нулю.
Операция OR может использоваться для установки одного или нескольких битов. Например, предположим, что регистр AL содержит 0011 1010, вам нужно установить на единицы четыре младших бита, тогда вы можете сделать OR со значением 0000 1111, т.е.
В следующем примере демонстрируется инструкция OR. Давайте сохраним значения 5 и 3 в регистрах AL и BL, соответственно, затем
затем в регистре AL в результате выполнения операции OR получится 7
Результат работы программы:
Инструкция XOR
Инструкция XOR реализует побитовую операцию XOR. Операция XOR устанавливает результирующий бит в 1, если и только если биты из операндов отличаются. Если биты из операндов одинаковы (оба 0 или оба 1), результирующий бит сбрасывается в 0.
XOR операнд числа с самим собой меняет операнд на 0. Это используется для очистки регистра.
Инструкция TEST
Инструкция TEST работает так же, как и операция AND, но в отличие от инструкции AND она не меняет первый операнд. Таким образом, если нам нужно проверить, является ли число в регистре чётным или нечётным, мы также можем сделать это, используя инструкцию TEST, не меняя исходного числа.
Инструкция NOT
Инструкция NOT реализует побитовую операцию NOT. Операция NOT меняет биты в операнде на противоположные. Операнд может быть либо в регистре, либо в памяти.
Ассемблер: Условия
Выполнение в зависимости от выполнения условия на ассемблере реализовано несколькими инструкциями зацикливания и ветвления. Эти инструкции могут изменить поток управления в программе. Условное исполнение рассматривается в двух сценариях:
№ | Инструкции условия | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
1 |
Инструкция | Описание | Тестируемые флаги |
---|---|---|
JE/JZ | Jump Equal or Jump Zero (равно или ноль) | ZF |
JNE/JNZ | Jump not Equal or Jump Not Zero (не равно или не ноль) | ZF |
JG/JNLE | Jump Greater or Jump Not Less/Equal (больше или не меньше/равно) | OF, SF, ZF |
JGE/JNL | Jump Greater/Equal or Jump Not Less (больше/равно или не меньше) | OF, SF |
JL/JNGE | Jump Less or Jump Not Greater/Equal (меньше или не больше/равно) | OF, SF |
JLE/JNG | Jump Less/Equal or Jump Not Greater (меньше/равно или не больше) | OF, SF, ZF |
Ниже приведены инструкции условного перехода, используемые для данных без знака, используемых для логических операций.
Инструкция | Описание | Тестируемые флаги |
---|---|---|
JE/JZ | Jump Equal или Jump Zero (равно или ноль) | ZF |
JNE/JNZ | Jump not Equal или Jump Not Zero (не равно или не ноль) | ZF |
JA/JNBE | Jump Above или Jump Not Below/Equal (больше или не меньше/равно) | CF, ZF |
JAE/JNB | Jump Above/Equal или Jump Not Below (больше/равно или не меньше) | CF |
JB/JNAE | Jump Below или Jump Not Above/Equal (меньше или не больше/равно) | CF |
JBE/JNA | Jump Below/Equal или Jump Not Above (меньше/равно или не больше) | AF, CF |
Следующие инструкции условного перехода имеют специальное использование и проверяют значение флагов:
Инструкция | Описание | Тестируемый флаг |
---|---|---|
JXCZ | Переход если CX равен нулю | нет |
JC | Переход если Перенос | CF |
JNC | Переход если нет Переноса | CF |
JO | Переход если переполнение | OF |
JNO | Переход если нет переполнения | OF |
JP/JPE | Переход при наличии чётности | PF |
JNP/JPO | Переход при отсутствии чётности | PF |
JS | Переход при наличии знака (отрицательная величина) | SF |
JNS | Переход при отсутствии знака (положительная величина) | SF |
Синтаксис для набора инструкций J :
Пример
Следующая программа отображает наибольшую из трёх переменных. Переменные являются двузначными переменными. Три переменные num1, num2 и num3 имеют значения 47, 22 и 31 соответственно:
Ассемблерные вставки в GCC (3 стр)
Вместо ключевого слова asm может быть также использовано __asm__ (двойные подчёркивания). Некоторые источники рекомендуют использовать именно __asm__ (asm является (для C) расширением GNU, и в режиме -ansi, и/или строгого соответствия стандартам (опции вида -std= ) может вызывать предупреждения/ошибки компиляции), в частности Linux kernel использует именно эту запись.
Существуют 2 формы ассемблерных вставок: базовая и расширенная.
Все примеры выше по тексту использовали базовую форму.
По умолчанию, компилятор воспринимает ассемблерную вставку, как производящую вычисления, и оптимизатор исходит из этого предположения (в частности, может считать ассемблерную вставку чистой функцией от входных операндов и, например, перенести или вынести наружу цикла). Это может привести к неправильной работе, если ассемблерная вставка содержит побочные эффекты. Для того, чтобы отключить соответствующие оптимизации, можно вслед за asm указать ключевое слово volatile (или __volatile__). Базовая форма всегда является volatile (но явное указание volatile всё-равно допустимо).
Цитата из документации ( https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html#Volatile ), описывающая (довольно уклончиво) семантику volatile:
Рекомендуется по возможности избегать volatile ассемблерных вставок (в частности — базовой формы ассемблера), и не препятствовать нормальной работе оптимизатора.
Базовая форма
В базовой форме AssemblerInstructions вставляются в выходной файл на ассемблере как есть, без макроподстановок.
В базовой форме отсутствуют операнды (позволяющие задать связи с переменными C++), что заметно затрудняет надёжное взаимодействие с кодом на C++.
Кроме того (опять же по причине отсутствия операндов) компилятор исходит из предположения, что базовая ассемблерная вставка не изменяет значения ни памяти, ни регистров.
Как отмечалось выше, оптимизатор имеет право выбрасывать/перемещать/дублировать ассемблерные вставки.
В общем случае, согласно документации, volatile не даёт гарантий против этого поведения.
В частности нет гарантий относительного порядка следования команд. Если таковой требуется, рекомендуется использовать одну общую ассемблерную вставку с несколькими инструкциями.
Таким образом базовая форма может быть использована для команд, ценных в основном своими побочными эффектами, вроде
Однако иногда базовой форме нет альтернативы:
1. Использование ассемблерных вставок на верхнем уровне единицы трансляции (расширенная форма обязана находиться внутри тела функции). Это может быть использовано для установки директив ассемблера.
2. Использование в функции с атрибутом naked (см. https://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html#Function-Attributes ).
По возможности рекомендуется использовать расширенную форму.
Расширенная форма
goto-версия может выполнять переход на одну из меток (определённых в коде на C/C++), указанных в GotoLabels. Она не содержит OutputOperands, и автоматически является volatile даже без явного указания.
Если есть необходимость изменить переменную внутри goto-вставки, то можно (вследствие отсутствия OutputOperands) воспользоваться записью в память, уведомив об этом компилятор, указав «memory» в clobbers.
Переходы между разными ассемблерными вставками запрещены. Переходы внутри одной ассемблерной вставки не требуют goto-версии, и рассмотрены ниже.
AssemblerTemplate — это строка, которая вставляется в выходной ассемблерный файл. Но, в отличии от базовой формы, компилятор производит над ней макроподстановки.
Макроподстановки используют символ «%». Кроме имён, сопоставленных операндам, доступны:
%% — генерирует одинарный символ % (экранирование символа %)
%= — генерирует уникальное целое число (полезно для создания уникальных идентификаторов)
%< - генерирует символ <
%| — генерирует символ |
%> — генерирует символ >
OutputOperands, InputOperands, Clobbers задают операнды ассемблерной вставки.
Суммарное количество операндов ограничено 30.
Операнды
OutputOperands
InputOperands
Clobbers
Note that this clobber does not prevent the processor from doing speculative reads past the asm statement. To prevent that, you need processor-specific fence instructions.
Flushing registers to memory has performance implications and may be an issue for time-sensitive code. You can use a trick to avoid this if the size of the memory being accessed is known at compile time. For example, if accessing ten bytes of a string, use a memory input like:
GotoLabels
Constraints
Constraint представляет собой C-строку, задающую ограничения на связанный операнд.
Неполный список constraints (более полный см. в https://gcc.gnu.org/onlinedocs/gcc/Constraints.html):
Constraint | Значение |
Общие | |
m | Адресуемая область памяти |
r | Регистр общего назначения |
i | Непосредственный константный операнд |
g | Регистр общего назначения, область памяти или непосредственный операнд |
X | Любой операнд |
0, 1, 2, . 9 | Операнд, совпадающий с операндом с указанным индексом (matching constraint) |
x86/x86_64 | |
a, b, c, d | Регистры ax, bx, cx, dx и их версии (ah,ecx,rdx) |
S, D | Регистры si, di и их версии |
A | Пара ax:dx и её версии |
f | Регистр стека FPU |
t | Верхний регистр стека FPU (ST(0)) |
u | ST(1) |
y | Любой регистр MMX |
x | Любой регистр SSE |
Yz | Регистр xmm0 |
G | Вещественная константа x387 |
C | Нулевая константа SSE |
Примечания:
1. Кроме m существуют также o и V обозначающие offsettable и non-offsettable адреса соответственно.
2. Matching constraint означает, что используется один операнд, но ассемблерная вставка различает несколько связанных с ним имён, например для использования одного и того же операнда в InputOperands и OutputOperands. Цитата из документации:
This number is allowed to be more than a single digit. If multiple digits are encountered consecutively, they are interpreted as a single decimal integer. There is scant chance for ambiguity, since to-date it has never been desirable that ‘10’ be interpreted as matching either operand 1 or operand 0. Should this be desired, one can use multiple alternatives instead.
This is called a matching constraint and what it really means is that the assembler has only a single operand that fills two roles which asm distinguishes. For example, an add instruction uses two input operands and an output operand, but on most CISC machines an add instruction really has only two operands, one of them an input-output operand:
addl #35,r12
Matching constraints are used in these circumstances. More precisely, the two operands that match must include one input-only operand and one output-only operand. Moreover, the digit must be a smaller number than the number of the operand that uses it in the constraint.
Constraints могут быть снабжены модификатором:
= — означает, что ассемблерная вставка производит запись в операнд (затирая предыдущее значение). Возможен только для OutputOperands.
+ — означает, что ассемблерная вставка производит и чтение (из операнда) и запись (в него)
& — (так называемый earlyclobber). Обычно компилятор исходит из предположения, что все чтения из InputOperands завершаются до начала записи в OutputOperands, и может, например отвести один и тот же регистр для независимых InputOperand и OutputOperand. Снабжение OutputOperand модификатором & сообщает компилятору, что запись в регистр может произойти раньше, препятствуя возникновению этой ситуации (также это препятствует, например, использованию этого регистра компилятором/ассемблером для хранения/вычисления адреса другого операнда). Цитаты из документации:
The same problem can occur if one output parameter (a) allows a register constraint and another output parameter (b) allows a memory constraint. The code generated by GCC to access the memory address in b can contain registers which might be shared by a, and GCC considers those registers to be inputs to the asm. As above, GCC assumes that such input registers are consumed before any outputs are written. This assumption may result in incorrect behavior if the asm writes to a before using b. Combining the ‘&’ modifier with the register constraint on a ensures that modifying a does not affect the address referenced by b. Otherwise, the location of b is undefined if a is modified before using b.
‘&’ applies only to the alternative in which it is written. In constraints with multiple alternatives, sometimes one alternative requires ‘&’ while others do not. See, for example, the ‘movdf’ insn of the 68000.
A operand which is read by the instruction can be tied to an earlyclobber operand if its only use as an input occurs before the early result is written. Adding alternatives of this form often allows GCC to produce better code when only some of the read operands can be affected by the earlyclobber. See, for example, the ‘mulsi3’ insn of the ARM.
Furthermore, if the earlyclobber operand is also a read/write operand, then that operand is written only after it’s used.
‘&’ does not obviate the need to write ‘=’ or ‘+’. As earlyclobber operands are always written, a read-only earlyclobber operand is ill-formed and will be rejected by the compiler.
Модификатор = или + обязателен для OutputOperands.
Assembler. Система команд. Синтаксис Intel
Читайте также:
- Aloha системасы
- Cудебник 1550 г. Общая характеристика, система и источники
- I. Понятие и система принципов организации и деятельности прокуратуры РФ.
- II. Бреттон-Вудська система (створена в 1944 р.)
- II. Финансовая система РФ.
- III .1 Транспортная система городов и регионов
- III. Система мероприятий по реализации Концепции
- KOI8-R стал фактически стандартом для русской кириллицы в 1990-х годах в юникс-подобных операционных системах и электронной почте.
- TRAKO — Система керування рахунками клієнтів
- Автоматизована інформаційно-бібліотечна система
- Автономная система
Команды передачи данных.
mov (move – передать переасать) – скопировать.
dst – destination куда, src – source откуда.
reg — register регистр reg8/reg16/reg32
mem – memory mem8/mem16/mem32
imm – immediately непосредственное значение = const которое записано в коде.
mov работает с любыми представлениями. Запрещены пересылки из памяти в память mov mem, mem – ЗАПРЕЩЕНО. Все остальное можно
регистры CRi DRi TRi только с помощью mov с другими регистрами они работать не будут
Когда один из операндов eAX\AX\AL то общая длина команды сокращается на 1 байт, mov становится однооперандный.
exchange – меняет местами содержание источника и конца.
На время своего выполнения блокирует системную шину материнской платы. Гарантирует что на момент его исполнения никто другой не сможет вмешается, за счет того что блокируется системная шина на материнской плате.
Используется для проверки изменения значения семафоров, синхронизации асинхронных процессов.
Проблему синхронизации придумал и решил Деекстор.
XLALT – безоперандная команда. В 1 байт.
(translate) заменяет содержимое аккумулятора AL на байт из 256 байтов таблицы адрес которого находится в BX
YY | ß -> копируется в AL[XX(YY)] |
/|\ XX \|/ | |
ß BX |
Преобразует текущее значение AL в значение из какой-то таблицы.
из источника на вершину стека
PUSH srk – уменьшает значение
POP dst – увеличивает значение, извлекает с текущей вершины стека и помещает его в destination
PUSHA – помещает в стек содержание всех регистров 16 битных в след порядке AX/CX/DX/BX/SP/BP/SI/DI
POPA – извлекает с вершины стека в соответствующем порядке и заполняет их значение в регистрах, значение SP теряется.
PUSHAD – для 32 битных регистров
PUSHF PUSHFD — Помещает значение флагов
POPF POPFD — извлекает значение и записывает в регистры флагов
Команды ввода вывода.
in acc, port Ввод из фиксированного порта
in acc, dx Ввод из переменного порта
команда in помещает в аккумулятор 1 байт из некоторого адреса
out port, acc перемещает 1 байт из аккумулятор в порт
out dx, acc либо 1 байт в порт по адресу расположенному в dx
Обмен с периферией возможен только по байту, за передачу отвечает только процессор.
Арифметические команды.
add dst, src //dst=dst+src
add – складывает значение источника с текущим значением назначения, сумма попадает в dst
В src можно помещать разные регистры.
adc dst,src //dst=dst+src+CF
Если перенос (3+9=12) то CF=1
inc dst // dst=dst+1
int — Увеличение на единицу
150d = 0096h = 0000 0000 1001 0110b
1111 1111 0110 1010b = FF6Ah = -150d
sub dst,src ; dst=dst-src
sbl dst,src ; dst=dst-src-CF
dec dst ; dst=dst-1
Ы inc, dec – на CF не влияют
cmp dst,src ; dst-src
cmp – команда сравнения, на результат не влияет, результат сравнения находится в регистре флагов.
Команды умножения.
mul src ; AL=src*AL
умножается и результат помещается в аккумулятор
mul src ; ext:acc=acc*src
Умножение знаковых чисел
imul src ; идентичен умножению знаковых чисел
imul reg,src ; умножение регистра общего назначения на источник, результат попадает в регистр, старшая половина при этом теряется. reg=src*reg
imul reg,src,imm ; reg=src*imm также старшая половина теряется.
Команды деления.
div src ; DX/AX = acc/src DX – остаток от деления
относится к командам мультипликативным
idiv src ; знаковое деление, 2-х 3-х операндных вариантов нет
Команды преобразования.
CBW src ; convert bait to word расширение (был 8 битным стал 16 битным)
CND src ; convert word to double word (был 16 стал 32 битынм)
CDQ src ; был 32 стал 64 битным
Эти команды копируют знаковый бит в старшие разряды
BCD — числа упакованные в байт.
Логические операции.
NOT – унарная операция, конвертирует только биты.
Логические операции воздействуют друг на друга в межразрядных соединениях.
NOT – при реализации основного кода
остальные – для инициализации и булевой логики (XOR ax,ax).
Команды сдвига.
Единообразно воздействуют на все виды регистров, операндов, они передают побитовый сдвиг право i+1, при сдвиге лево i-1 если сдвигаем на 1 бит.
Сдвиги различаются тем как мы работаем с теми битами которые выдвигаются из регистров.
циклические (то что выпало, идёт на свободное место)
Циклические через CF (длина операндов увеличивается на 1)
Логические(то что выпало игнорируется и на свободное место помещается 0)
Арифметические (знаковый бит «размножается»)
Логические сдвиги влево, вправо SHLD SHRD (Shift Left\Right Double)
SHLD dst, src, count
объединяются в один регистр и производится сдвиг
Команды передачи управления.
Вызов, возврат из подпрограмм, обработка прерываний.
короткий/ближний/дальний переход (short, near, far)
короткий переход от -127 до 128 байт
ближний переход от -32 до 32 мб
дальний переход от -2 до 2 гб
Условные переходы.
JXX проверяются значения флагов.
Команд очень много, могут проверяться комбинации флагов.
JA — проверяют комбинацию регистров CF=0 и ZF=0 – Если выше
JBE — CF=0 и ZF=0 – Если не ниже/не равно.
Термины Выше, Не ниже, для без знаковых чисел.
JAE – проверяют значение CF=0 – выше/равно
JNB — проверяют значение CF=0
JNC — проверяют значение CF=0
JB — проверяют значение CF=1 — Ниже
JNAE- проверяют значение CF=1
JC — проверяют значение CF=1
JBE – проверяют значение CF=1 и ZF=1 – Ниже/равно
JNA — проверяют значение CF=1 и ZF=1
JE — проверка значение ZF=1 — Равно
JZ — проверка значение ZF=1
JNZ – проверяет на ZF≠0 – не равно 0
LOOP imm – CX-1 и передача управления на некоторое смещение, однооперандная команда, еще параметром называется непосредственное назначение, где-то выше loop должна быть метка, до тех пор пока не 0 происходит передача управления.
LOOPE – генерируют один и тот же код.
LOOPZ – кроме CX проверяется значение ZF==1 (результат предыдущей операции не 0). Если CX!=0 и ZF==1 то передача управления.
LOOPNE \ LOOPNZ – отрицание, CX==0 и ZF!=1
Вызов и возврат из подпрограмм.
CALL imm – программист вводит название функции, компилятор преобразует в адрес.
Подпрограмма выполняет свою логику, возможно использует фактические параметры из стека. По окончании логики функция помещает результат.
RET – целочисленное в AX, вещественное FPU и чотатам.
Команда RET может быть безоперандной и однооперандной RET imm
RET извлекает с вершины стека адрес точки возврата возвращает управление в этот адрес и тогда очистку стека от фактических параметров должен произвести обвязочный код (СИ конвенция).
Если с операндом то смещение на чотатам байт и очищается стек (Паскаль конвенция).
interruptеры.
INT n
n – в команде int это номер в команде которых необходимо обработать, номер из таблицы векторов прерываний, в результате процессор запоминает текущее состояние, которое прерывается, чотатам.
Интераптеры изменяют IP и передача управление в другой сегмент кода. Команды безусловного перехода которые jump.
Обработка цепочек.
Если нам надо произвести одинаковые действие над двумя массивами то мы предполагаем наличие циклов, но наличие цикла это однозначно ветвление и сброс с конвейера. Цепочечные команды конвейер не сбрасывают, позволяют реализовать часть циклических операций в одной строке.
Изменяют SI DI либо вперед либо назад, сдвигают начало массива и конец массива в значении от индекса.
Загружает регистры CX SI DI, потом вызываем команду цепочек и в одной строке повторяем все телодвижения над массивами, конвейер при этом не сбрасывается.
REP – повторяет чотатам
REPE/REPZ — проверяется флаг ZF
MOVS dst, src ; передает значение из источника в назначение
MOVSB – работа с байтами
MOVSW – работа со словами
MOVSD – работа с двойными словами.
CMPS dst, src ;не разрушающее вычитание, изменяются флаги, операнды не трогаются
SCAS dst ;однооперандная, вычитает содержимое просматриваемого массива и аккумулятора acc, dst. На равенство значения в аккумуляторе или не равенство.
STOS dst ;загружает в цепочку назначение цепочку аккумулятора. dst ç acc
LODS dst; acc ç dst
INS — ввод цепочек в некоторый порт
OUTS – вывод цепочек в некоторый порт
CLC (clear larry flag)
CMC (complementary carry)
STC (set carry)
CLD (clear Direction)
STD (Set Direction)
CLT (Clear Interrupt)
STI (Set Interrupt)
IF (Interrupt flag)
NOP (No Operation) – занимает по времени 1 такт процессора и по длине занимает 1 байт.
Работа с Ассемблером – Язык низкого уровня можно делать все что угодно в большинстве случаев затраты себя не окупают.
Ассемблер язык платформа зависимый. Это ограничивает сферы применения ассемблера:
1) Оптимизация (в большинстве по скорости);
2) Драйвера к железу.
3) Взлом программ.
Оптимизация. Возможно, оптимизация по скорости и возможно возникают иногда задачи оптимизации по объему памяти.
Требование к оптимизированным программам:
1) Результат работы оптимизированной программы должен совпадать с результатом работы до оптимизации.
2) Прирост производительности. (Можно разворачивать циклы, можно убирать условные переходы это дает прирост производительности в проценты)
SIMD (Simple Instruction Multiple Data)
Если мы можем входной поток данных (на вычислении) одной командой обрабатывать не одну, а множество операндов, то тогда мы получаем множественный так процессора.
Профайлером вылавливается самая напряженная функция, далее она оптимизируется (переписывается на ассемблер), далее компилируется в dll.
Шаблон ассемблерной dll:
.586 ; разрешать не привилегированные команды процессора 586
.XMM ; разрешает регистры XMM
PUBLIC _nameF@xx ;имя экспортируемой функции
mov al, 1 ; точка входа в dll
;если много функций то добавляем произвольно кол-во функций
push ebp ;стандартный заголовок функции
END_start@12 ;точка выхода и dll
Рекомендуемый редактор к программированию Notepad++ (npp)
Компиляция текстового файла:
Меняем .txt на .asl
ml.exe – создает объектный файл из текстового файла.
Для получения dll используется link32.exe – он из объектного файла делает файлы exp.lib и .dll (*.lnk)
в [name].lnk находится:
nameF – название нашей функции без искажения.
Модно создавать файл .bat или .cmd в который напишем
d:\work\..\ml.exe /c /coff /Cp /D_MASM ИМЯ.asm
d:\link32.exe ИМЯ.obj @ИМЯ.lnk
Рекомендуется перед началом компиляции удалять предыдущие результаты компилирования. .dll .obj exp
Рекомендуется создавать dll в другом каталоге
После компиляции скопировать
copy *.dll Куда/то/там
_ — подчеркивание, само!
XX – количество в байтах параметром передано функции.
Рекомендуется узнать, сколько параметров мы передаем, и написать прототип функции на языке высокого уровня.
Сравнить скорость работы функции на чистом си и с использование ассемблера.
По номеру варианта выбираем задание ассемблером программы.
Сделать Сишный эквивалент ассемблерной функции.
MMX (multi media edition)
8 регистров MMX:
Мантиссэ – регистры с плавающей запятой. 8BYTE
Декларируется что за одну операцию сложения в соответствующих словах результатов: мы получаем прирост производительности на упакованных байтах в 8 раз, на 4WORD в 4 раза и т.д. на учетверенных словах прирост производительности возможен также.
Набор команд:
movq dst,src ;копирует 64 бита 1QWORD из источника в назначение один из операторов должен быть MMX
Упаковка со знаком насыщения:
PACKSSWB mmx,src ; слова в байты
приемник регистр MMX
WB – копирует 4 слова со знаком из приемника в 4 младших байта приемника и копирует 4 слова из источника в старшие 4 байта приемника.
При этом если значение слова превышает 127, то в байт записывается 127. +127 d=7F h
Если меньше чем -128 то записывается минимальная из возможных -128. -128d=80h
Эти операции называются операциями насыщения.
Упаковка знаковая с насыщением слова в байты
Упаковка знаковая с насыщением двойных слов в слова
PACKSSWB – копирует 4 слова со знаком из приемника в 4 младших байта приемника и копирует 4 слова со знаком из источника в 4 старших байта приемника.
Команда PACKSSDW – тоже самое только из двойных слов в слова.
Упаковка без знакового насыщения.
PACKUSWB mmx, src.
Упаковывает без знаковые с насыщением слова в байты.
если больше 255d=FFh если меньше 0=0
РАСПАКОВКА ЧЕГОТОТАМ ТАМ ЧОТО ТАМ. Самостоятельно
Дата добавления: 2014-01-04 ; Просмотров: 2600 ; Нарушение авторских прав? ;
Нам важно ваше мнение! Был ли полезен опубликованный материал? Да | Нет
Модуль int_ffh asm
Создание программ на языке Assembler.
[администратор рассылки: Лысков Игорь Витальевич (Старший модератор)]
Лучшие эксперты в этом разделе
Коцюрбенко Алексей Владимирович Статус: Модератор Рейтинг: 1150 |
Зенченко Константин Николаевич Статус: Старший модератор Рейтинг: 454 |
solowey Статус: Бакалавр Рейтинг: 234 |
Перейти к консультации №: |
Здравствуйте, уважаемые эксперты! Прошу Вас ответить на следующий вопрос: реализовать функцию 6 прерывания int 30h. В приложении модуль на Pascal (используются ассемблерные вставки). Там функция 6 реализована, однако, преподаватель сказал что реализована она «коряво», прошу Вас предложить вариант получше. Искренне благодарю за помощь!
Состояние: Консультация закрыта
Здравствуйте, Alexkharkov!
Вот Вам реализация функций 5 и 6
В функции 5 сдвигаем edx влево так, чтобы наше число стало на левом крае регистра,
затем выводим по тетрадам (раз Вам так хочется менять атрибут у тетрад), начиная со старшего бита
В функции 6 мы сначала обнуляем старшие ненужные биты, сдвигая влево/вправо на число ненужных бит,
затем формируем в стеке последовательность цифр, как остатки от деления на 10 нашего числа,
ну и, наконец, выводим на экран цифры из стека в обратном порядке.
АСМ-модуль
Фабрика корпусной мебели «АСМ-Модуль»
Открытие фабрики «АСМ-Модуль» состоялось 1 августа 2008 года.
Фабрика выпускает спальные гарнитуры, гостиные, прихожие,малые формы.
Все серии мебели являются модульными – Вы можете собрать любой комплект по собственному желанию.
Фабрика «АСМ-Модуль» – это производство европейского уровня, гарантирующее точность выполнения технологических процессов на уровне международных стандартов. В процессе производства используются только проверенные материалы и надёжная мебельная фурнитура. Каждая стадия производства контролируется четырёхуровневой системой ОТК, повышающей надёжность выпускаемой продукции.
Фабрика оснащена передовым немецким оборудованием фирмы Homag, в том числе: форматно-раскроечный центр, обрабатывающий центр, фрезерные станки, мембранно-вакуумный пресс и другое оборудование, позволяющее выпускать собственные фасады и изголовья из МДФ, обрабатывать самые сложные детали и воплощать в жизнь идеи наших дизайнеров.
Вся продукция изготавливается из высококачественных комплектующих: ЛДСП Lamarty (Россия), кромка ПВХ Rehau (Германия), фурнитура Hettiсh (Германия), Boyard. Это обеспечивает производство эргономичной, функциональной, безопасной для здоровья, экологически чистой, не токсичной мебели.
Производство и склады фабрики расположены в городе Екатеринбург, столице Урала, а объёмы выпускаемой продукции позволяют охватить города не только Свердловской области, но и другие города России, а также стран ближнего зарубежья.
Наш девиз остаётся всегда неизменным: «АСМ-Модуль» – Для тех, кто любит свой дом!
Модуль int_ffh asm
Контакты: о проблемах с регистрацией, почтой и по другим вопросам пишите сюда — alarforum@yandex.ru, проверяйте папку спам! Обязательно пройдите активизацию e-mail.
|
Регистрация
Здесь нужно купить рекламу за 25 тыс руб в месяц! ) пишите сюда — alarforum@yandex.ru
|