Модуль int_ffh asm


Содержание

__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

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

  1. Aloha системасы
  2. Cудебник 1550 г. Общая характеристика, система и источники
  3. I. Понятие и система принципов организации и деятельности прокуратуры РФ.
  4. II. Бреттон-Вудська система (створена в 1944 р.)
  5. II. Финансовая система РФ.
  6. III .1 Транспортная система городов и регионов
  7. III. Система мероприятий по реализации Концепции
  8. KOI8-R стал фактически стандартом для русской кириллицы в 1990-х годах в юникс-подобных операционных системах и электронной почте.
  9. TRAKO — Система керування рахунками клієнтів
  10. Автоматизована інформаційно-бібліотечна система
  11. Автономная система

Команды передачи данных.

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. нужно переписать его полную форму с противоположным значением битов (то есть для единиц записываются нули, а для нулей записываются единицы)
  2. и затем добавить к этому числу 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

Двоичное представление (8 бит)

(в виде Дополнительного кода)

Десятичное
представление
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 = 100001011, то есть = 00001011

Бит который вызывает переполнение — крайней левый, девятый по счёту, просто отбрасывается.

Адресация данных в памяти

Процесс, посредством которого процессор управляет выполнением инструкций, называется циклом 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 в трёх разных случаях:

Когда перемножаются два байта

Множимое находится в регистре AL, а множитель — это байт в памяти или в другом регистре. Результат произведения находится в AX. Старшие 8 битов произведения хранятся в AH, а младшие 8 битов хранятся в AL.

Когда умножаются два значения word

Множимое должно быть в регистре AX, а множитель — это word в памяти или в другом регистре. Например, для такой инструкции, как MUL DX, вы должны сохранить множитель в DX и множимое в AX.

В результате получается двойное word, для которого понадобятся два регистра. Часть высшего порядка (крайняя слева) сохраняется в DX, а часть нижнего порядка (крайняя справа) сохраняется в AX.

Когда умножаются два значения doubleword

Когда умножаются два значения doubleword, множимое должно быть в EAX, а множитель — это значение doubleword, хранящееся в памяти или в другом регистре. Результат умножения сохраняется в регистрах EDX:EAX, то есть 32-разрядные старшие разряды сохраняются в регистре EDX, а 32-разрядные младшие разряды сохраняются в регистре EAX.

В следующем примере 3 умножается на 2 и отображается результат:

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

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

Инструкция DIV (Divide) используется для данных без знака, а IDIV (Integer Divide) используется для данных со знаком.

Формат для инструкции DIV/IDIV:

Делимое находится в аккумуляторе. Обе инструкции могут работать с 8-битными, 16-битными или 32-битными операндами. Операция влияет на все шесть флагов состояния. Следующий раздел объясняет три случая деления с различным размером операнда:

Номер Сценарии
1

Когда делитель равен 1 байту

Предполагается, что делимое находится в регистре AX (16 бит). После деления частное переходит в регистр AL, а остаток — в регистр AH.


Когда делителем является 1 word

Предполагается, что делимое имеют длину 32 бита и оно размещено в регистрах DX:AX. Старшие 16 битов находятся в DX, а младшие 16 битов — в AX. После деления 16-битное частное попадает в регистр AX, а 16-битное значение попадает в регистр DX.

Когда делитель doubleword

Предполагается, что размер делимого составляет 64 бита и оно размещено в регистрах EDX:EAX. Старшие 32 бита находятся в EDX, а младшие 32 бита находятся в EAX. После деления 32-битное частное попадает в регистр EAX, а 32-битный остаток попадает в регистр EDX.

В следующем примере 8 делится на 2. Делимое 8 сохраняется в 16-битном регистре AX, а делитель 2 сохраняется в 8-битном регистре BL.

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

Ассемблер: Логические инструкции

Набор команд процессора содержит инструкции логики AND, OR, XOR, TEST и NOT, которые проверяют, устанавливают и очищают биты в соответствии с потребностями программы.

Формат для этих инструкций:

Номер Сценарии
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 меняет биты в операнде на противоположные. Операнд может быть либо в регистре, либо в памяти.

Ассемблер: Условия

Выполнение в зависимости от выполнения условия на ассемблере реализовано несколькими инструкциями зацикливания и ветвления. Эти инструкции могут изменить поток управления в программе. Условное исполнение рассматривается в двух сценариях:

Безусловный прыжок

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

Условный переход

Он выполняется с помощью набора инструкций перехода j и зависит от выполнения условия. Условные инструкции передают управление, прерывая последовательный поток, и делают это, изменяя значение смещения в IP.

Давайте обсудим инструкцию CMP, прежде чем обсуждать условные инструкции.

Инструкция CMP

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

CMP сравнивает два числовых поля данных. Операнд-адресат может быть либо в регистре, либо в памяти. Исходным операндом могут быть постоянные (непосредственные) данные, регистр или память.

CMP часто используется для сравнения того, достигло ли значение счётчика количества раз, которое цикл должен быть выполнен. Рассмотрим следующее типичное условие:

Безусловный переход

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

Инструкция JMP предоставляет имя метки, куда поток управления передаётся немедленно. Синтаксис инструкции JMP:

Следующий фрагмент кода иллюстрирует инструкцию JMP:

Условный переход

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

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

Инструкции условия
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

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

  1. Aloha системасы
  2. Cудебник 1550 г. Общая характеристика, система и источники
  3. I. Понятие и система принципов организации и деятельности прокуратуры РФ.
  4. II. Бреттон-Вудська система (створена в 1944 р.)
  5. II. Финансовая система РФ.
  6. III .1 Транспортная система городов и регионов
  7. III. Система мероприятий по реализации Концепции
  8. KOI8-R стал фактически стандартом для русской кириллицы в 1990-х годах в юникс-подобных операционных системах и электронной почте.
  9. TRAKO — Система керування рахунками клієнтів
  10. Автоматизована інформаційно-бібліотечна система
  11. Автономная система

Команды передачи данных.

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.

Форум программистов > Низкоуровневое программирование > Assembler — Ассемблер
Как взять модуль числа
Регистрация
Поиск по форуму
Расширенный поиск
К странице.

Здесь нужно купить рекламу за 25 тыс руб в месяц! ) пишите сюда — alarforum@yandex.ru

19.04.2007, 07:45 #1

я бы использовал NEG AX. =) Синтаксис BTR:

btr r16,r/m16 Bit Test and Reset

Т.е. если я не ошибаюсь, а скорее всего ошибаюсь =), то поменять знак числа с отрицательного на положительное можно так:
dec ax
btr ax, ax

Не, фигня какая-то. Не до тестирования мне сейчас, извини.

19.04.2007, 19:57 #2
19.04.2007, 20:08 #3

Отрицательное число хранится в дополнительном коде, и просто заменить знаковый бит нельзя. Например, если имеем -1d = 11111111b, и обнулим старший бит — 0111111b, то получим 127d, а не 1d, как требовалось. Чтобы получить из отрицательного числа положительное, нужно инвертировать все биты числа, и к результату прибавить 1. Т.е. на примере -1d: 00000000b + 1b = 00000001b = 1d Таким же образом из положительного числа получается отрицательное . На асме можно сделать так:

Модуль int_ffh asm

Welcome to Hogwarts!
(Cosplay by Ginny Di)

Вновь возвращаюсь к фирменной среде разработки — ST Visual Develop, для чего есть две причины. Во-первых, оказалось, что писать на ассемблере сколь-либо сложные прошивки без отладчика невозможно, у меня по крайней мере не получилось, т.к. программа все-равно так или иначе отлаживается с помощью светодиода или по UART, через отладочный интерфейс это просто делается быстрее. Во-вторых, мне показалось, что изучать архитектуру только лишь руководствуясь datasheet’ом не совсем правильно. Что-то может быть неправильно понято, что-то может быть упущено. С такими штуками как DMA, встроенный RTC или выполнение кода из ОЗУ, будет проще разобраться с помощью отладчика, не забывая при этом посматривать в datasheet.

STVD — довольно простая среда разработки, я ее освоил за вечер. В этой статье я хочу рассказать, как «с нуля» начать писать и отлаживать прошивки на ассемблере STM8, используя ST Visual Develop.

STVD — работает в ОС семейства Windows, начиная с XP и выше. При этом она прекрасно работает из-под виртуальной машины в Linux. В этой статье я использую STVD 4.3.12, последнюю доступную версию на этот момент, и Windows XP SP3 в качестве гостевой ОС. В качестве микроконтроллера я буду использовать 20-пиновый STM8S103F3P6.

В качестве дизассемблера я буду использовать комплект утилит stm8-binutils. Бинарные файлы этого комплекта для Windows скомпилированы для работы в CYGWIN, т.е. они понимают unix’овский формат пути файла с прямым слешем в качестве разделителя. CYGWIN для Windows 7 и выше ставится без проблем следуя инструкциям на сайте https://cygwin.com/install.html, что касается Windows XP, инструкция которая имеется на оф. сайте устарела, и более неработоспособна. Ссылка с инстркуций по которой ставил я, — сдохла за время написания статьи. В качестве альтернативной ссылки могу указать — windows xp — Is it still possible to get Cygwin for XP? — Super User. Сколько проживет эта ссылка, не берусь сказать.

В качестве альтернативы связке binutils+cygwin, можно использовать naken_util из комплекта naken_asm.

Содержание статьи:

I. Создание минимального проекта Blink

II. Язык ассемблера STVD

III. Процесс отладки

1. Открытие шаблонного проекта на ассемблере

Если сам ассемблер STVD заслуживает всяческих похвал, то система управления проектами сделана довольно бестолково. В STVD существует понятие Workspace — хранящее настройки самой среды. Workspace может содержать в себе несколько проектов.

Первым делом, нужно будет создать пустую папку для workspace и вложенную в нее папку будущего проекта. Штатными средствами STVD это будет сделать затруднительно, т.к. там нет диалоговых форм выбора каталога:

Теперь запускаем STVD и через меню выбираем New Workspace:

В диалоговом окне задаём имя workspace и указываем ранее созданную папку для сохранения:

Аналогично для проекта выбираем название, указываем ранее созданную папку проекта, и в выпадающем списке в качестве Toolchain указываем ST Assembler Linker:

Осталось указать целевой микроконтроллер:

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

Осталось указать формат прошивки в настройках проекта:

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

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

Так же обращу внимание, что там можно задать ширину отступа — Tab size. По умолчанию используется два пробела, мне же более привычно использовать четыре пробела.

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

Файл прошивки можно дизассемблировать из CYGWIN:

Или прошивку можно дизассемблировать из командной строки Windows с помощью дизассемблера naken_utils:

Шаблон проекта состоит из файлов: main.asm, mapping.inc и mapping.asm. Файл mapping.inc содержит константы деления ОЗУ на сегменты:

Здесь первые две страницы ОЗУ выделены в сегменты RAM0 и RAM1, остальные 513 байт отданы под сегмент стека. Т.е. всё согласно datasheet:

В файле mapping.asm определяются сегменты:

Хочу обратить внимание на то, что ассемблерный файл в STVD начинается строкой ‘stm8/’ и заканчивается строкой ‘end’. Вместо ‘stm8’ можно указать ‘st7’, но это для тех кто пишет для архитектуры stm7.

Теперь посмотрим на файл main.asm

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

Первое на что бросается взгляд в выше-приведенном листинге, это оригинальное задание таблицы векторов в виде опкода инструкции INT и адреса метки. Если попробовать заменить эту конструкцию например на такую строку: «INT main», то выдаст ошибку: «Неизвестный опкод». Сложно сказать почему так сделано, но факт есть факт. Инструкции INT в ассемблере STVD нет.

2. Добавление в проект файла с таблицей векторов и обработчиками прерываний

Теперь попробуем перекинуть таблицу векторов с обработчиками прерываний в отдельный файл irq.asm, оставив в main.asm только главный цикл.

1) Для этого сначала нужно создать новый файл:

2) После создания файл нужно будет сохранить к директории текущего проекта:

3) В диалоговом окне выбираем директорию, имя файла, и сохраняем его:

4) После чего нужно будет добавить файл в проект. Для этого нужно правой кнопкой мыши щёлкнуть по Source Files и выбрать добавление файла, после чего новый файл отобразится в файловой структуре проекта:

В файл irq.asm скинем таблицу прерываний и все их обработчики:

В итоге, в main.asm останется лишь главный цикл:

При компиляции может выдать ошибку на некорректный EOF, то исправляется в самом редакторе, для этого нужно нажать enter после end.

3. Добавление в проект констант с адресами периферии

Теперь нам нужно добавить адреса периферийных регистров ввода/вывода. Для этого нужно найти файлы STM8S103F.asm и STM8S103F.inc в каталоге с установки STVD и скопировать их в папку проекта:

После чего нужно добавить файл STM8S103F.asm в проект, а содержимое main.asm привести к виду:

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

4. Добавление в проект файла с подпрограммой

Осталось добавить задержку для главного цикла, что бы программа корректно работала. Для этого, аналогично тому как добавляли в проект файл irq.asm, добавим в него ещё файл utils.asm следующего содержания:

Теперь в файле main.asm после #include «STM8S103F.inc» нужно добавить: «extern delay», а в главный цикл можно вставить вызов подпрограммы: «call delay»:

Минимальная программа на ассемблере STVD готова. Теперь будем разбираться с тем, что мы напрограммировали.

5. Основные сведения об ассемблере STVD

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

Строка ассемблера STVD имеет следующий формат:

Вначале идёт необязательная метка, затем идёт необязательный опкод, затем идут операнды (если они есть), и завершает строку необязательный комментарий.

Заметьте, что даже при опущенной метке, ассемблерной инструкции или директиве, должен ОБЯЗАТЕЛЬНО предшествовать ПРОБЕЛ или ТАБУЛЯЦИЯ. ВНАЧАЛЕ СТРОКИ ИДЕТ ТОЛЬКО МЕТКА! Исключением из этого правила можно считать директиву «stm8/» вначале исходного кода.

Соответственно, если написать «segment ‘rom'» или «#include «STM8S103F.inc»» сначала строки то, при компиляции выдаст ошибку. Видимо разработчики STVD были неравнодушны к питону ;)

6. Формат числовых констант

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

С помощью директив: MOTOROLA, INTEL, TEXAS, ZILOG можно менять формат числовой константы:

Новый формат будет применим к тексту программы после директивы.

7. Формат метки

Метка обязательно должна начинаться с начала строки, и может содержать в себе: заглавные и строчные буквы английского алфавита, цифры и символ подчёркивания. Начинаться метка должна с буквы или символа подчёркивания, т.е. никак не с цифры. Метка может содержать до 30 символов.

Метка может заканчиваться символом двоеточия или не содержать его вообще. Символ двоеточия игнорируется.

Метка может содержать суффикс вида: label[.b|.w|.l], который состоит из точки и добавочных букв: b, w, l.

С помощью суффиксов: b, w, l, метка может быть одно-, двух-, или четырёхбайтной. Т.о. метка может быть использована для одно-,двух или трехбайтной адресации.

В качестве примера можно привести такой исходник:

Который будет скомпилирован в следующий машкод:

Здесь задаётся две переменные: var1 в нулевой странице ОЗУ, var2 в первой странице OЗУ, и константа const в области флеш-памяти. При обращении к первой переменной используется короткая адресация (shortmem), при обращении к второй переменной и к константе используется длинная адресация (longmem), и инструкция jpf осуществляет переход метке с расширенной адресацией (extmem).

По умолчанию размер метки равен двум байтам (одному слову), но он может быть изменён директивами: BYTES, WORDS, LONGS.

Метка может быть абсолютной и относительной. Относительную метку вычисляет компоновщик на этапе компиляции прошивки.

По умолчанию, все метки локальные. C помощью директив PUBLIC и EXTERN можно обмениваться метками между отдельными модулями программы. Директива PUBLIC делает метку видимой компоновщику. Директива EXTERN делает внешнюю метку видимой данному модулю. В качестве альтернативы директиве PUBLIC, может выступать точка в начале метки.

8. Сегментация

Сегментация позволяет управлять из проекта тем, что в GCC делает скрипт компоновщика, т.е. с помощью директивы сигментации можно указывать компоновщику, где должны располагаться тот или иной код или данные. Один программный модуль (файл) может содержать до 128 сегментов.

Сегмент устанавливается с помощью директивы сегментации. Ее формат представлен ниже:

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

Вначале идет необязательный параметр — имя (name). Он может иметь длину до 12 символов. Сегменты одного класса, но с разными именами группируются компоновщиком друг за другом в порядке определения.

Параметр выравнивания — «align», может принимать следующие значения:

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

Здесь метке counter соответствует адрес 0x80, метке address — 0x81, stack — 0x92. Во втором файле, метка serialtemp имеет адрес 0x93, а serialcou — 0x94. Т.к. оба файла пишут в сегмент «eprom», то второй файл пишет следом за первым.

Следующий параметр директивы segment — указывает компоновщику где следует располагать сегмент. Имеется три варианта для этого параметра:

Вариант at- ДОЛЖЕН использоваться при объявлении класса, и использоваться ТОЛЬКО ОДИН РАЗ .

Также в случае at должен обязательно задан начальный адрес сегмента, и опционально последний адрес сегмента или его размер. Все адреса пишутся в ШЕСТНАДЦАТЕРИЧНОМ виде КАК ЕСТЬ без префиксов.

Вариант common позволяет определить общие области данных. Области этого типа с одинаковым именем класса, будут иметь одинаковый стартовый адрес. Это позволяет использовать такие сегменты для обмена данными.

Рассмотрим такой пример:

Здесь метки lab1 и lab2 будут иметь адрес 0x12, lab3 и lab4 будут иметь адрес 0x1a, а lab5 — 0x1e.

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

Последний параметр директивы segment — cod, позволяет управлять получаемым при линковке выходным файлом, в котором будут размещены данные сегмента. Если параметр cod опущен, то все скомпануется в единый default.cod. Если же в качестве параметра cod задать число от нуля до девяти, то компоновщик все сегменты данного класса разместит в файле prog_x.cod, где x — номер кода. Это может быть полезно, например при формировании разных eeprom для разных устройств. Например:

9. Основные директивы ассемблера

Полный перечень директив с подробным описанием изложен в Руководство пользователя по ассемблеру и компоновщику STM8: ST Assembler-Linker UM0144, приложение A. Я бы хотел упомянуть наиболее востребованные директивы.

EQU — директива подмены или соответствия. Имеется наверно во всех ассемблерных языках. Формат:

При компиляции программы, метка заменяется своим выражением. Пример:

#define — директива похожая на EQU, но имеющая существенное отличие. В EQU метке присваивается число, его размерность: байт, слово, двойное слово — определяется в момент присваивания. В случае использования #define, метке присваивается строка которая уже при компиляции преобразуется в число, и его размерность определяется в самой программе. Формат:

CEQU — директива похожая не EQU но позволяет менять свое содержимое:

Используется в сочетании с директивами REPEAT и UNTIL.

Директивы: DC.B, DC.W, DS.L — позволяют записать константу, или массив. Также возможна запись строки ASCII.

Директивы: DS.B, DS.W, DS.L — позволяют зарезервировать место. Через запятую указывается количество резервируемых ячеек.

Директива STRING позволяет зарезервировать ASCII строку. Является синонимом директивы DC.B. Примеры использования:

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

10. Копирование кода в ОЗУ и выполнение его оттуда

Выполнение программы из ОЗУ может снизить энергопотребление чипа

Выполнение кода из ОЗУ имеет смысл в STM8L — серии, там есть режим энергосбережения WFE который позволяет работать с периферией без прерываний, что позволяет исключить использование флеш-памяти при работе главного цикла. Отказ от использования флеш-памяти позволяет снизить энергопотребление, но при этом не следует забывать, что программа из оперативной памяти выполняется дольше нежели с флеша. Сейчас мы в этом убедимся, но перед тем как запустить программу из ОЗУ, ее нужно туда скопировать. При этом все абсолютные адреса надо будет как-то проиндексировать.

В Руководство пользователя по ассемблеру и компоновщику STM8: ST Assembler-Linker UM0144 описывается механизм написания кода специально предназначенный для копирования в ОЗУ. Для этого декларируется два сегмента, где один будет использоваться для выполнения, а другой для хранения кода. Например:

здесь код начинающийся с метки label1 будет сохранен в ‘code’ сегменте, но все метки будут пересчитаны относительно ‘ram’ сегмента. Кроме того, адресное пространство ‘ram’ сегмента будет зарезервировано под этот код.

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

Здесь сегмент ‘eeprom’ содержит программу размещаемую в оперативной памяти, а сегмент ‘rom’ содержит загрузчик. Программа размещается в первой странице т.е. с адреса 0х100, тогда как нулевая страница отведена на переменные. В программе все относительные метки оставлены как есть, в то время как абсолютные записываются со смещением: адрес-(начальный адрес eeprom)+(адрес начала первой страницы ОЗУ).

После прошивки микроконтроллера будет видно, что светодиод имеет полупериод мигания гораздо больше чем одна секунда. Определяя «на глаз», у меня получилось где-то трёхкратное снижение быстродействия, о чем собственно и говорилось в документации: 3-х уровневый конвейер STM8: перевод глав 3, 4, 5 руководства по программированию микроконтроллеров STM8 (PM0044)

Чтение инструкции из ОЗУ походит на чтение из ПЗУ. Однако, вследствие того, что шина ОЗУ является лишь 8-битной, потребуется 4 последовательных операции чтения, чтобы заполнить одно Fx слово. Вследствие этого код из ОЗУ выполняется медленнее, нежели с флеш-памяти.

В дальнейшем, эту программу я буду использовать для демонстрации процесса отладки в STVD.

11. Интерфейс отладки STVD

В качестве отладчика в STVD используется мощнейший GDB, полные его возможности доступны через вкладку «Консоль» в окне «Output Window». STVD выступает в качестве фронтенда к GDB:

У меня пока не очень большой опыт использования STVD, но из всех возможностей GDB, через консоль я печатал только дампы памяти. Все остальное я делал через графический интерфейс, Работа с ним экономит много времени.

Итак, после после запуска отладки, становится доступна панель управления отладкой:

Она реализует режим трассировки и содержит операции: начать отладку, завершить отладку, команда GDB — Run, команда GDB — Continue, команда GDB — next, команда GDB — nexti, команда GDB — step, команда GDB — stepi и пр. У всех иконок есть всплывающие подсказки, раскрывающие их функции.

Открыть то или иное окно можно через меню->View:

Все окна стекабельны, закрываются крестиком. Настройки окон сохранятся в workspace. Самые полезные на мой взгляд следующие окна:

1. Окно дизассемблера. Наиболее полезно оно наверное при отладке Си программы, позволяет увидеть, в какой код преобразовалась та или иная программная конструкция. Для ассемблера это окно полезно тем, что позволяет увидеть адреса и размерность меток, а также числовой адрес порта ввода-вывода. Через «Display Options» можно настроить отображаемые столбцы. Опция Refresh нужна когда вводишь отладочные команды через консоль gdb. Потом чтобы увидеть изменения приходиться обновлять.

2. Следующее окно «Core Rigisters», показывает содержимое регистров микроконтроллера и флагов состояния. Прямо в окне можно менять значение регистров, снимать и устанавливать нужные флаги.

3. Окно периферийных регистров — это древовидный список сгруппированный по аппаратным модулям. Здесь также можно менять содержимое РВВ:

4. Следующее полезное окошко — окно переменных. Переменную можно ввести по имени или по адресу. Можно менять значение переменной.

5. И последнее оконо — окно с дампом памяти. Лично мне как-то проще вводить в консоли gdb команду: «x/10xb», но данное оконо тоже может быть полезно. Хотя бы тем, что там в интерактивном режиме можно менять содержимое ячеек памяти.

12. Процесс отладки в STVD

Для отладки алгоритма, поставим точку остановки на инструкции jp $100, т.е. после завершения операции копирования из eeprom в ram, и перед передачей управлению скопированному коду. Жмём иконку Run (выглядит как восклицательный знак) и после остановки сверяем оба сегмента: ram1 и eeprom:

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

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

По-моему, ничего сложного.

13. Введение в макроассемблер STVD

Чёрная магия программирования

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

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

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

Директивы макроассемблера и формат задания макроса описан в руководстве: Руководство пользователя по ассемблеру и компоновщику STM8: ST Assembler-Linker UM0144. Я не буду повторять написанное там, вместо этого я приведу несколько примеров использования макросов.

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

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

Тогда использование макроса сведётся к строке:

14. Макрос задержки delay_ms

Теперь пример посложнее, постараемся повторить широко известный макрос _delay_ms который имеется в gcc-avr и используется довольно часто. Для этого, сначала в настройках проекта нужно будет с помощью именованной константы указать частоту F_CPU:

Кроме этого, нам понадобится отдельный файл macros.inc, в который мы будем скидывать макросы. Этот файл потом нужно будет включить через директиву #include:

Макрос у меня получился таким:

Тогда код в модуле main.asm примет следующий вид:

15. Макрос условного оператора сравнения регистра с числом

Как я уже говорил, на макросах можно сделать подобие операторов в ЯВУ. Например, в копировщике кода из eeprom в ram имеется сравнение числа с регистром. Можно написать макрос который сравнивает регистр с числом и осуществляет переход по метке в случае совпадения.

У меня получилось два макроса. Один для 16-битных регистров, другой для 8-битных:

main.c в таком случае принимает вид:

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

16. Задержка длительностью 1 мкс на инструкции условного перехода

Если организовать задержку по таймеру на 1 мс и более не составляет проблем, то с задержкой на 1мкс не все так просто. Обработчики прерываний таймера будут не успевать обрабатывать поступающие прерывания. Единственным выходом будет сделать задержку на инструкциях. Но считать циклы при работающем конвейере, дело неблагодарное. Количество циклов будет зависеть от предыдущих инструкций и от способа адресации. Кроме того, я натыкался на информацию, что на разных тактовых частотах одни и те же инструкции будут выполняться за разное количество циклов. И вроде бы я сталкивался с этим на практике. В качестве способа решения этой проблемы рекомендуют заблокировать работу конвейера. Делается это с помощью инструкций выполняющих сброс очереди конвейера — FLUSH. Такими инструкциями являются инструкции условного перехода. Т.о. задержку можно организовать последовательном выполнении операций условного перехода с нулевым смещением. Одна такая инструкция должна выполняться за 2 цикла ВСЕГДА.

Записать последовательность инструкций помогут директивы REPEAT и UNTIL. К сожалению их нельзя использовать в макросах поэтому их использование выглядит так:

Здесь предполагается, что fCPU = 2MHz и т.к. инструкция условного перехода с случае перехода выполняется за два такта, то на выполнении каждой инструкции JRC уходит 1 мкс. Директивы REPEAT/UNTIL вставляют в текст программы пять таких инструкций, следовательно формируется задержка длительностью 5мкс.

Для задержек 10мкс и больше можно использовать таймеры TIM4/TIM6, а для меньших задержек думаю сгодится такой способ.

Илон Маск рекомендует:  Цвет ссылок
Понравилась статья? Поделиться с друзьями:
Кодинг, CSS и SQL
11.05.2007, 18:20 #4