Ассемблер db: Данные в ассемблере

Содержание

Типы данных в ассемблере


 

Данные – числа и закодированные символы, используемые в качестве операндов команд.
Основные типы данных в ассемблере

Тип Директива Количество байт
Байт DB 1
Слово DW 2
Двойное слово DD 4
8 байт DQ 8
10 байт DT 10

Данные, обрабатываемые вычислительной машиной, можно разделить на 4 группы:

  • целочисленные;
  • вещественные.
  • символьные;
  • логические;
Целочисленные данные

Целые числа в ассемблере могут быть представлены в 1-байтной, 2-байтной, 4-байтной или 8-байтной форме. Целочисленные данные могут представляться в знаковой и беззнаковой форме.

Беззнаковые целые числа представляются в виде последовательности битов в диапазоне от 0 до 2n-1, где n- количество занимаемых битов.

Беззнаковые целые числа
Знаковые целые числа представляются в диапазоне -2

n-1 … +2n-1-1. При этом старший бит данного отводится под знак числа (0 соответствует положительному числу, 1 – отрицательному).
Знаковые целые числа

Вещественные данные

Вещественные данные могут быть 4, 8 или 10-байтными и обрабатываются математическим сопроцессором.

Логические данные

Логические данные представляют собой бит информации и могут записываться в виде последовательности битов. Каждый бит может принимать значение 0 (ЛОЖЬ) или 1 (ИСТИНА). Логические данные могут начинаться с любой позиции в байте.

Символьные данные

Символьные данные задаются в кодах и имеют длину, как правило, 1 байт (для кодировки ASCII) или 2 байта (для кодировки Unicode)  .

Числа в двоично-десятичном формате

В двоично-десятичном коде представляются беззнаковые целые числа, кодирующие цифры от 0 до 9. Числа в двоично-десятичном формате могут использоваться в одном из двух видов:

  • упакованном;
  • неупакованном.

В неупакованном виде в каждом байте хранится одна цифра, размещенная в младшей половине байта (биты 3…0).

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

Числовые константы

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

Десятичный формат – допускает использование десятичных цифр от 0 до 9 и обозначается последней буквой d, которую можно не указывать, например, 125 или 125d. Ассемблер сам преобразует значения в десятичном формате в объектный шестнадцатеричный код и записывает байты в обратной последовательности для реализации прямой адресации.

a DB 12

Шестнадцатеричный формат – допускает использование шестнадцатеричных цифр от 0 до F и обозначается последней буквой h, например 7Dh. Так как ассемблер полагает, что с буквы начинаются идентификаторы, то первым символом шестнадцатеричной константы должна быть цифра от 0 до 9. Например, 0Eh.

a DB 0Ch

Двоичный формат – допускает использование цифр 0 и 1 и обозначается последней буквой b. Двоичный формат обычно используется для более четкого представления битовых значений в логических командах (AND, OR, XOR).

a DB 00001100b

Восьмеричный формат – допускает использование цифр от 0 до 7 и обозначается последней буквой q или o, например, 253q.

a DB 14q

Массивы и цепочки

Массивом называется последовательный набор однотипных данных, именованный одним идентификатором.

Цепочка — массив, имеющий фиксированный набор начальных значений.

Примеры инициализации цепочек

M1 DD 0,1,2,3,4,5,6,7,8,9
M2 DD 0,1,2,3

   DD 4,5,6,7

   DD 8,9

Каждая из записей выделяет десять последовательных 4-байтных ячеек памяти и записывает в них значения 0, 1, 2, 3, 4, 5, 6, 7, 8, 9.

Идентификатор M1 определяет смещение начала этой области в сегменте данных .DATA.

Для инициализации всех элементов массива одинаковыми значениями используется оператор DUP:

Идентификатор Тип Размер DUP (Значение)

Идентификатор — имя массива;
Тип — определяет количество байт, занимаемое одним элементом;
Размер — константа, характеризующая количество элементов в массиве
Значение — начальное значение элементов.

Например

a DD 20 DUP (0)

описывает массив a из 20 элементов, начальные значения которых равны 0.

Если необходимо выделить память, но не инициализировать ее, в качестве поля Значение используется знак ?. Например,

b DD 20 DUP(?)

Символьные строки

Символьные строки представляют собой набор символов для вывода на экран. Содержимое строки отмечается

  • одиночными кавычками », например, ‘строка’
  • двойными кавычками «», например «строка»

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

Символьная строка, предназначенная для корректного вывода, должна заканчиваться нуль-символом ‘\0’ с кодом, равным 0.

Str DB ‘Привет всем!’, 0

Для перевода строки могут использоваться символы

  • возврат каретки с кодом 13 (0Dh)
  • перевод строки с кодом 10 (0Ah).

Stroka DB «Привет», 13, 10, 0

Назад


Назад: Язык ассемблера

3.2 Директивы ассемблера

3.2 Директивы ассемблера

3.2 Директивы ассемблера

Компилятор поддерживает ряд директив. Директивы не транслируются непосредственно в код. Вместо этого они используются для указания положения в программной памяти, определения макросов, инициализации памяти и т.д. Список директив приведён в таблице 3.5

Таблица 3.5 Список директив ассемблера
Директива Описание
BYTE Зарезервировать байты в ОЗУ
CSEG Программный сегмент
DB Определить байты во флэш или EEPROM
DEF Назначить регистру символическое имя
DEVICE Определить устройство для которого компилируется программа
DSEG Сегмент данных
DW Определить слова во флэш или EEPROM
ENDM Конец макроса
EQU Установить постоянное выражение
ESEG Сегмент EEPROM
EXIT Выйти из файла
INCLUDE Вложить другой файл
LIST Включить генерацию листинга
LISTMAC Включить разворачивание макросов в листинге
MACRO Начало макроса
NOLIST Выключить генерацию листинга
ORG Установить положение в сегменте
SET Установить переменный символический эквивалент выражения

Все директивы предваряются точкой.

BYTE - Зарезервировать байты в ОЗУ

Директива BYTE резервирует байты в ОЗУ. Если Вы хотите иметь возможность ссылаться на выделенную область памяти, то директива BYTE должна быть предварена меткой. Директива принимает один обязательный параметр, который указывает количество выделяемых байт. Эта директива может использоваться только в сегменте данных(смотреть директивы CSEG и DSEG). Выделенные байты не инициализируются.

Синтаксис:

МЕТКА: .BYTE выражение

Пример:


.DSEG 
var1:    .BYTE 1            ; резервирует 1 байт для var1 
table:   .BYTE tab_size     ; резервирует tab_size байт 
.CSEG 
ldi r30,low(var1)  ; Загружает младший байт регистра Z 
ldi r31,high(var1) ; Загружает старший байт регистра Z 
ld r1,Z            ; Загружает VAR1 в регистр 1 

CSEG - Программный сегмент

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

Синтаксис:

.CSEG

Пример:


.DSEG               ; Начало сегмента данных 
vartab: .BYTE 4     ; Резервирует 4 байта в ОЗУ 
.CSEG               ; Начало кодового сегмента
const:  .DW 2       ; Разместить константу 0x0002 в памяти программ 
mov r1,r0           ; Выполнить действия

DB - Определить байты во флэш или EEPROM

Директива DB резервирует необходимое количество байт в памяти программ или в EEPROM. Если Вы хотите иметь возможность ссылаться на выделенную область памяти, то директива DB должна быть предварена меткой. Директива DB должна иметь хотя бы один параметр. Данная директива может быть размещена только в сегменте программ (CSEG) или в сегменте EEPROM (ESEG).

Параметры, передаваемые директиве - это последовательность выражений разделённых запятыми. Каждое выражение должно быть или числом в диапазоне (-128..255), или в результате вычисления должно давать результат в этом же диапазоне, в противном случае число усекается до байта, причём БЕЗ выдачи предупреждений.

Если директива получает более одного параметра и текущим является программный сегмент, то параметры упаковываются в слова (первый параметр - младший байт), и если число параметров нечётно, то последнее выражение будет усечено до байта и записано как слово со старшим байтом равным нулю, даже если далее идет ещё одна директива DB.

Синтаксис:

МЕТКА: .DB список_выражений

Пример:


.CSEG 
consts: .DB 0, 255, 0b01010101, -128, 0xaa
.ESEG 
const2: .DB 1,2,3 

DEF - Назначить регистру символическое имя

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

Синтаксис:

.DEF Символическое_имя = Регистр

Пример:


.DEF temp=R16 
.DEF ior=R0 
.CSEG 
 ldi temp,0xf0  ; Загрузить 0xf0 в регистр temp (R16)
 in ior,0x3f    ; Прочитать SREG в регистр ior (R0) 
 eor temp,ior   ; Регистры temp и ior складываются по исключающему или 

DEVICE - Определить устройство для которого компилируется программа

Директива DEVICE позволяет указать для какого устройства компилируется программа. При использовании данной директивы компилятор выдаст предупреждение, если будет найдена инструкция, которую не поддерживает данный микроконтроллер. Также будет выдано предупреждение, если программный сегмент, либо сегмент EEPROM превысят размер допускаемый устройством. Если же директива не используется то все инструкции считаются допустимыми, и отсутствуют ограничения на размер сегментов.

Синтаксис:

.DEVICE AT90S1200 |AT90S2313 | AT90S2323 | AT90S2333 | AT90S2343 | AT90S4414 | AT90S4433 | AT90S4434 | AT90S8515 | AT90S8534 | AT90S8535 | ATtiny11 | ATtiny12 | ATtiny22 | ATmega603 | ATmega103

Пример:


.DEVICE AT90S1200  ; Используется AT90S1200 
.CSEG 
        push r30   ; Эта инструкция вызовет предупреждение 
                   ; поскольку AT90S1200 её не имеет

DSEG - Сегмент данных

Директива DSEG определяет начало сегмента данных. Исходный файл может состоять из нескольких сегментов данных, которые объединяются в один сегмент при компиляции. Сегмент данных обычно состоит только из директив BYTE и меток. Сегменты данных имеют свои собственные побайтные счётчики положения. Директива ORG может быть использована для размещения переменных в необходимом месте ОЗУ. Директива не имеет параметров.

Синтаксис:

.DSEG

Пример:


.DSEG                        ; Начало сегмента данных 
var1:  .BYTE 1               ; зарезервировать 1 байт для var1 
table:  .BYTE tab_size       ; зарезервировать tab_size байт. 
.CSEG 
        ldi r30,low(var1)    ; Загрузить младший байт регистра Z 
        ldi r31,high(var1)   ; Загрузить старший байт регистра Z
        ld r1,Z              ; Загрузить var1 в регистр r1 

DW - Определить слова во флэш или EEPROM

Директива DW резервирует необходимое количество слов в памяти программ или в EEPROM. Если Вы хотите иметь возможность ссылаться на выделенную область памяти, то директива DW должна быть предварена меткой. Директива DW должна иметь хотя бы один параметр. Данная директива может быть размещена только в сегменте программ (CSEG) или в сегменте EEPROM (ESEG). Параметры передаваемые директиве - это последовательность выражений разделённых запятыми. Каждое выражение должно быть или числом в диапазоне (-32768..65535), или в результате вычисления должно давать результат в этом же диапазоне, в противном случае число усекается до слова, причем БЕЗ выдачи предупреждений.

Синтаксис:

МЕТКА: .DW expressionlist

Пример:


.CSEG 
varlist:  .DW 0, 0xffff, 0b1001110001010101, -32768, 65535 
.ESEG 
eevarlst: .DW 0,0xffff,10 

ENDMACRO - Конец макроса

Директива определяет конец макроопределения, и не принимает никаких параметров. Для информации по определению макросов смотрите директиву MACRO.

Синтаксис:

.ENDMACRO

Пример:


.MACRO SUBI16               ; Начало определения макроса
        subi r16,low(@0)    ; Вычесть младший байт первого параметра 
        sbci r17,high(@0)   ; Вычесть старший байт первого параметра 
.ENDMACRO 

EQU - Установить постоянное выражение

Директива EQU присваивает метке значение. Эта метка может позднее использоваться в выражениях. Метка которой присвоено значение данной директивой не может быть переназначена и её значение не может быть изменено.

Синтаксис:

.EQU метка = выражение

Пример:


.EQU io_offset = 0x23 
.EQU porta     = io_offset + 2 
.CSEG                 ; Начало сегмента данных
        clr r2        ; Очистить регистр r2 
        out porta,r2  ; Записать в порт A 

ESEG - Сегмент EEPROM

Директива ESEG определяет начало сегмента EEPROM. Исходный файл может состоять из нескольких сегментов EEPROM, которые объединяются в один сегмент при компиляции. Сегмент EEPROM обычно состоит только из директив DB, DW и меток. Сегменты EEPROM имеют свои собственные побайтные счётчики положения. Директива ORG может быть использована для размещения переменных в необходимом месте EEPROM. Директива не имеет параметров.

Синтаксис:

.ESEG

Пример:


.DSEG                    ; Начало сегмента данных
var1:   .BYTE 1          ; зарезервировать 1 байт для var1 
table:  .BYTE tab_size   ; зарезервировать tab_size байт. 
.ESEG 
eevar1: .DW 0xffff       ; проинициализировать 1 слово в EEPROM 

EXIT - Выйти из файла

Встретив директиву EXIT компилятор прекращает компиляцию данного файла. Если директива использована во вложенном файле (см. директиву INCLUDE), то компиляция продолжается со строки следующей после директивы INCLUDE. Если же файл не является вложенным, то компиляция прекращается.

Синтаксис:

.EXIT

Пример:


.EXIT  ; Выйти из данного файла

INCLUDE - Вложить другой файл

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

Синтаксис:

.INCLUDE "имя_файла"

Пример:


                       ; файл iodefs.asm: 
.EQU sreg   = 0x3f     ; Регистр статуса 
.EQU sphigh = 0x3e     ; Старший байт указателя стека 
.EQU splow  = 0x3d     ; Младший байт указателя стека 
                       ; файл incdemo.asm 
.INCLUDE iodefs.asm    ; Вложить определения портов 
        in r0,sreg     ; Прочитать регистр статуса 

LIST - Включить генерацию листинга

Директива LIST указывает компилятору на необходимость создания листинга. Листинг представляет из себя комбинацию ассемблерного кода, адресов и кодов операций. По умолчанию генерация листинга включена, однако данная директива используется совместно с директивой NOLIST для получения листингов отдельных частей исходных файлов.

Синтаксис:

.LIST

Пример:


.NOLIST                ; Отключить генерацию листинга
.INCLUDE "macro.inc"   ; Вложенные файлы не будут 
.INCLUDE "const.def"   ; отображены в листинге 
.LIST                  ; Включить генерацию листинга 

LISTMAC - Включить разворачивание макросов в листинге

После директивы LISTMAC компилятор будет показывать в листинге содержимое макроса. По умолчанию в листинге показывается только вызов макроса и передаваемые параметры.

Синтаксис:

.LISTMAC

Пример:


.MACRO MACX         ; Определение макроса
        add  r0,@0  ; Тело макроса
        eor  r1,@1 
.ENDMACRO           ; Конец макроопределения
.LISTMAC            ; Включить разворачивание макросов 
        MACX r2,r1  ; Вызов макроса (в листинге будет показано тело макроса)

MACRO - Начало макроса

С директивы MACRO начинается определение макроса. В качестве параметра директиве передаётся имя макроса. При встрече имени макроса позднее в тексте программы, компилятор заменяет это имя на тело макроса. Макрос может иметь до 10 параметров, к которым в его теле обращаются через @[email protected] При вызове параметры перечисляются через запятые. Определение макроса заканчивается директивой ENDMACRO.

По умолчанию в листинг включается только вызов макроса, для разворачивания макроса необходимо использовать директиву LISTMAC. Макрос в листинге показывается знаком +.

Синтаксис:

.MACRO макроимя

Пример:


.MACRO SUBI16                   ; Начало макроопределения
        subi @1,low(@0)         ; Вычесть младший байт параметра 0 из параметра 1
        sbci @2,high(@0)        ; Вычесть старший байт параметра 0 из параметра 2
.ENDMACRO                       ; Конец макроопределения
.CSEG                           ; Начало программного сегмента
        SUBI16 0x1234,r16,r17   ; Вычесть 0x1234 из r17:r16 

NOLIST - Выключить генерацию листинга

Директива NOLIST указывает компилятору на необходимость прекращения генерации листинга. Листинг представляет из себя комбинацию ассемблерного кода, адресов и кодов операций. По умолчанию генерация листинга включена, однако может быть отключена данной директивой. Кроме того данная директива может быть использована совместно с директивой LIST для получения листингов отдельных частей исходных файлов

Синтаксис:

.NOLIST

Пример:


.NOLIST                ; Отключить генерацию листинга
.INCLUDE "macro.inc"   ; Вложенные файлы не будут 
.INCLUDE "const.def"   ; отображены в листинге 
.LIST                  ; Включить генерацию листинга 

ORG - Установить положение в сегменте

Директива ORG устанавливает счётчик положения равным заданной величине, которая передаётся как параметр. Для сегмента данных она устанавливает счётчик положения в SRAM (ОЗУ), для сегмента программ это программный счётчик, а для сегмента EEPROM это положение в EEPROM. Если директиве предшествует метка (в той же строке) то метка размещается по адресу указанному в параметре директивы. Перед началом компиляции программный счётчик и счётчик EEPROM равны нулю, а счётчик ОЗУ равен 32 (поскольку адреса 0-31 заняты регистрами). Обратите внимание что для ОЗУ и EEPROM используются побайтные счётчики а для программного сегмента - пословный.

Синтаксис:

.ORG выражение

Пример:


.DSEG                ; Начало сегмента данных
.ORG 0x37            ; Установить адрес SRAM равным 0x37
variable: .BYTE 1    ; Зарезервировать байт по адресу 0x37H
.CSEG
.ORG 0x10  ; Установить программный счётчик равным 0x10
mov r0,r1  ; Данная команда будет размещена по адресу 0x10

SET - Установить переменный символический эквивалент выражения

Директива SET присваивает имени некоторое значение. Это имя позднее может быть использовано в выражениях. Причем в отличии от директивы EQU значение имени может быть изменено другой директивой SET.

Синтаксис:

.SET имя = выражение

Пример:


.SET io_offset = 0x23 
.SET porta     = io_offset + 2 
.CSEG         ; Начало кодового сегмента
clr r2        ; Очистить регистр 2 
out porta,r2  ; Записать в порт A 
Выражения

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

Операнды

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

  • Метки определённые пользователем (дают значение своего положения).
  • Переменные, определённые директивой SET
  • Константы, определённые директивой EQU
  • Числа, заданные в формате:

  • Десятичном (принят по умолчанию): 10, 255
    Шестнадцатеричном (два варианта записи): 0x0a, $0a, 0xff, $ff
    Двоичном: 0b00001010, 0b11111111
    Восьмеричном (начинаются с нуля): 010, 077
  • PC - текущее значение программного счётчика (Program Counter)


Ассемблер. Переменные и Константы | Уроки Ассемблера

  Обновл. 12 Окт 2019  | 

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

Хранения инициализированных данных

Синтаксис стейтмента выделения памяти для инициализированных данных следующий:

[имя_переменной] директива_определения начальное_значение [,начальное_значение]...

[имя_переменной]    директива_определения    начальное_значение   [,начальное_значение]...

Где имя_переменной является идентификатором для каждого пространства для хранения. Ассемблер связывает значение смещения для каждого имени переменной, определённого в сегменте данных.

Есть 5 основных форм директивы определения:

Директива Цель Пространство для хранения
DB Определяет Byte Выделяет 1 байт
DW Определяет Word Выделяет 2 байта
DD Определяет Doubleword Выделяет 4 байта
DQ Определяет Quadword Выделяет 8 байт
DT Определяет 10 Byte-ов Выделяет 10 байт

Ниже приведены примеры использования директив определения:

choice DB 'y' number DW 12345 neg_number DW -12345 big_number DQ 123456789 real_number1 DD 1.234 real_number2 DQ 123.456

choice     DB    'y'

number     DW    12345

neg_number   DW    -12345

big_number   DQ    123456789

real_number1 DD    1.234

real_number2 DQ    123.456

Обратите внимание, что:

   каждый байт символа хранится как его ASCII-значение в шестнадцатеричном формате;

   каждое десятичное значение автоматически конвертируется в 16-битный двоичный эквивалент и сохраняется в виде шестнадцатеричного числа;

   процессор использует прямой порядок байтов;

   отрицательные числа конвертируются в форму «two’s complement»;

   короткие и длинные числа типа с плавающей запятой представлены с использованием 32 или 64 бит, соответственно.

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

section .text global _start ; должно быть объявлено для линкера (gcc) _start: ; сообщаем линкеру входную точку mov edx,1 ; длина сообщения mov ecx,choice ; сообщение для вывода mov ebx,1 ; файловый дескриптор (stdout) mov eax,4 ; номер системного вызова (sys_write) int 0x80 ; вызов ядра mov eax,1 ; номер системного вызова (sys_exit) int 0x80 ; вызов ядра section .data choice DB 'y'

section .text

   global _start          ; должно быть объявлено для линкера (gcc)

_start:                   ; сообщаем линкеру входную точку

   mov edx,1       ; длина сообщения

   mov ecx,choice        ; сообщение для вывода

   mov ebx,1       ; файловый дескриптор (stdout)

   mov eax,4       ; номер системного вызова (sys_write)

   int 0x80       ; вызов ядра

 

   mov eax,1       ; номер системного вызова (sys_exit)

   int 0x80       ; вызов ядра

 

section .data

choice DB 'y'

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

y

Хранение неинициализированных данных

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

Есть 5 основных форм директив резервирования:

Директива Цель
RESB Резервирует Byte
RESW Резервирует Word
RESD Резервирует Doubleword
RESQ Резервирует Quadword
REST Резервирует 10 Byte-ов

Несколько определений

Вы можете иметь несколько стейтментов определения данных в программе. Например:

choice DB 'Y' ; ASCII-значение для y = 79H number1 DW 12345 ; 12345D = 3039H number2 DD 12345679 ; 123456789D = 75BCD15H

choice    DB 'Y'    ; ASCII-значение для y = 79H

number1    DW 12345    ; 12345D = 3039H

number2    DD   12345679   ; 123456789D = 75BCD15H

Ассемблер выделяет смежную память для нескольких определений переменных.

Несколько инициализаций

Директива TIMES позволяет выполнить несколько инициализаций одного значения. Например, массив с именем marks длиной 9 может быть определён и инициализирован нулём следующим образом:

marks TIMES 9 DW 0

marks  TIMES  9  DW  0

Директива TIMES полезна при определении массивов и таблиц. Следующая программа выводит на экран 9 звездочек:

section .text global _start ; должно быть объявлено для линкера (ld) _start: ; сообщаем линкеру входную точку mov edx,9 ; длина сообщения mov ecx, stars ; сообщение для написания mov ebx,1 ; файловый дескриптор (stdout) mov eax,4 ; номер системного вызова (sys_write) int 0x80 ; вызов ядра mov eax,1 ; номер системного вызова (sys_exit) int 0x80 ; вызов ядра section .data stars times 9 db '*'

section .text

   global _start        ; должно быть объявлено для линкера (ld)

_start:                 ; сообщаем линкеру входную точку

   mov edx,9     ; длина сообщения

   mov ecx, stars     ; сообщение для написания

   mov ebx,1     ; файловый дескриптор (stdout)

   mov eax,4     ; номер системного вызова (sys_write)

   int 0x80     ; вызов ядра

 

   mov eax,1     ; номер системного вызова (sys_exit)

   int 0x80     ; вызов ядра

 

section .data

stars   times 9 db '*'

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

*********

Определение констант

NASM предоставляет несколько директив, определяющих константы:

   EQU

   %assign

   %define

Директива EQU

Директива EQU используется для определения констант. Её синтаксис следующий:

ИМЯ_КОНСТАНТЫ EQU-выражение

Например:

Затем вы можете использовать эту константу в программе:

mov ecx, TOTAL_STUDENTS cmp eax, TOTAL_STUDENTS

mov  ecx,  TOTAL_STUDENTS

cmp  eax,  TOTAL_STUDENTS

Операндом стейтмента EQU может быть выражение:

LENGTH equ 20 WIDTH equ 10 AREA equ length * width

LENGTH equ 20

WIDTH  equ 10

AREA   equ length * width

Фрагмент кода выше определит AREA как 200.

Ещё один пример:

SYS_EXIT equ 1 SYS_WRITE equ 4 STDIN equ 0 STDOUT equ 1 section .text global _start ; должно быть объявлено для линкера (gcc) _start: ; сообщаем линкеру входную точку mov eax, SYS_WRITE mov ebx, STDOUT mov ecx, msg1 mov edx, len1 int 0x80 mov eax, SYS_WRITE mov ebx, STDOUT mov ecx, msg2 mov edx, len2 int 0x80 mov eax, SYS_WRITE mov ebx, STDOUT mov ecx, msg3 mov edx, len3 int 0x80 mov eax,SYS_EXIT ; номер системного вызова (sys_exit) int 0x80 ; вызов ядра section .data msg1 db 'Hello, programmers!',0xA,0xD len1 equ $ - msg1 msg2 db 'Welcome to the world of,', 0xA,0xD len2 equ $ - msg2 msg3 db 'Linux assembly programming! ' len3 equ $- msg3

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

SYS_EXIT  equ 1

SYS_WRITE equ 4

STDIN     equ 0

STDOUT    equ 1

section .text

   global _start          ; должно быть объявлено для линкера (gcc)

_start:                   ; сообщаем линкеру входную точку

   mov eax, SYS_WRITE        

   mov ebx, STDOUT        

   mov ecx, msg1        

   mov edx, len1

   int 0x80                

   mov eax, SYS_WRITE        

   mov ebx, STDOUT        

   mov ecx, msg2        

   mov edx, len2

   int 0x80

   mov eax, SYS_WRITE        

   mov ebx, STDOUT        

   mov ecx, msg3        

   mov edx, len3

   int 0x80

  

   mov eax,SYS_EXIT       ; номер системного вызова (sys_exit)

   int 0x80               ; вызов ядра

 

section .data

msg1 db 'Hello, programmers!',0xA,0xD

len1 equ $ - msg1

 

msg2 db 'Welcome to the world of,', 0xA,0xD

len2 equ $ - msg2

 

msg3 db 'Linux assembly programming! '

len3 equ $- msg3

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

Hello, programmers!
Welcome to the world of,
Linux assembly programming!

Директива %assign

Директива %assign может быть использована для определения числовых констант. Эта директива допускает переопределение. Например, вы можете определить константу TOTAL следующим образом:

Затем в коде вы можете переопределить её:

Эта директива является чувствительной к регистру.

Директива %define

Директива %define позволяет определять как числовые, так и строковые константы. Эта директива похожа на директиву #define в языке С.  Например, вы можете определить константу PRT следующим образом:

Код выше заменяет PTR на [EBP+4].

Эта директива также допускает переопределение и является чувствительной к регистру.

Оценить статью:

Загрузка...

Поделиться в социальных сетях:

Ассемблер. Сегменты памяти и регистры | Уроки Ассемблера

  Обновл. 29 Сен 2019  | 

Мы уже рассматривали 3 секции из которых состоят программы на ассемблере. Эти секции также представляют различные сегменты памяти. Что интересно, если вы замените ключевое слово section на segment, то получите тот же результат. Например:

segment .text ; сегмент когда global_start ; должно быть объявлено для линкера _start: ; сообщаем линкеру точку входа mov edx,len ; длина сообщения mov ecx,msg ; сообщение для написания mov ebx,1 ; файловый дескриптор (stdout) mov eax,4 ; номер системного вызова (sys_write) int 0x80 ; вызов ядра mov eax,1 ; номер системного вызова (sys_exit) int 0x80 ; вызов ядра segment .data ; сегмент данных msg db 'Hello, world!',0xa ; наша строка len equ $ - msg ; длина нашей строки

segment .text    ; сегмент когда

   global_start    ; должно быть объявлено для линкера

_start:            ; сообщаем линкеру точку входа

   mov edx,len    ; длина сообщения

   mov ecx,msg     ; сообщение для написания

   mov ebx,1    ; файловый дескриптор (stdout)

   mov eax,4    ; номер системного вызова (sys_write)

   int 0x80        ; вызов ядра

 

   mov eax,1       ; номер системного вызова (sys_exit)

   int 0x80        ; вызов ядра

 

segment .data      ; сегмент данных

msg db 'Hello, world!',0xa   ; наша строка

len equ $ - msg              ; длина нашей строки

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

Hello, world!

Сегменты памяти

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

Сегменты памяти:

   Сегмент данных (data segment) — представлен секциями .data и .bss. Секция .data используется для объявления области памяти, где хранятся элементы данных для программы. Эта секция не может быть расширена после объявления элементов данных, и она остаётся статической во всей программе. Секция .bss также является секцией статической памяти, которая содержит буферы для данных, которые будут объявлены в программе позже. Эта буферная память заполнена нулями.

   Сегмент кода (code segment) — представлен секцией .text. Он определяет область в памяти, в которой хранятся коды инструкций. Это также фиксированная область.

   Стек (stack) — это сегмент, который содержит значения данных, передаваемые в функции и процедуры в программе.

Регистры

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

Чтобы ускорить свою работу, процессор подключает определённые внутренние места хранения памяти, которые называются регистрами. Регистры хранят элементы данных для обработки без необходимости получать доступ к памяти. Ограниченное количество регистров встроено в чип процессора.

Регистры процессора

В архитектуре IA-32 есть десять 32-битных и шесть 16-битных процессорных регистра. Регистры делятся на три категории:

   Общие регистры (General Registers)

   Регистры управления (Control Registers)

   Сегментные регистры (Segment Registers)

В свою очередь, общие регистры делятся на следующие:

   Регистры данных (Data Registers)

   Регистры-указатели (Pointer Registers)

   Индексные регистры (Index Registers)

Регистры данных

Регистры данных — это четыре 32-битных регистра, которые используются для арифметических, логических и других операций. Эти 32-битные регистры могут быть использованы следующими тремя способами:

   Как полные 32-битные регистры данных: EAX, EBX, ECX, EDX.

   Нижние половины 32-битных регистров могут использоваться как четыре 16-битных регистра данных: AX, BX, CX и DX.

   Нижняя и верхняя половины вышеупомянутых четырёх 16-битных регистров могут использоваться как восемь 8-битных регистров данных: AH, AL, BH, BL, CH, CL, DH и DL.

Некоторые из этих регистров данных имеют специфическое применение в арифметических операциях.

   AX (primary accumulator) используется для ввода/вывода и в большинстве арифметических операций. Например, в операции умножения один операнд сохраняется в регистре EAX или AX или AL в соответствии с размером операнда.

   BX (base register) используется при индексированной адресации.

   CX (count register) хранит количество циклов в повторяющихся операциях (также, как и регистры ECX и CX).

   DX (data register) используется в операциях ввода/вывода, а также с регистрами AX и DX для выполнения операций умножения и деления, связанных с большими значениями.

Регистры-указатели

Регистрами-указателями являются 32-битные регистры EIP, ESP и EBP и соответствующие им 16-битные регистры IP, SP и BP. Есть три категории регистров-указателей:

   Указатель на инструкцию или команду (Instruction Pointer или IP) — 16-битный регистр IP хранит смещение адреса следующей команды, которая должна быть выполнена. IP в сочетании с регистром CS (как CS:IP) предоставляет полный адрес текущей инструкции в сегменте кода.

   Указатель на стек (Stack Pointer или SP) — 16-битный регистр SP обеспечивает значение смещения в программном стеке. SP в сочетании с регистром SS (SS:SP) относится к текущей позиции данных или адреса в программном стеке.

   Базовый указатель (Base Pointer или BP) — 16-битный регистр BP используется в основном при передаче параметров в подпрограммы. Адрес в регистре SS объединяется со смещением в BP, чтобы получить местоположение параметра. BP также можно комбинировать с DI и SI в качестве базового регистра для специальной адресации.


Индексные регистры

32-битные индексные регистры ESI и EDI и их 16-битные версии: SI и DI, которые используются в индексированной адресации, и, иногда, в операциях сложения/вычитания. Есть два типа индексных указателей:

   Исходный индекс (Source Index или SI) — используется в качестве исходного индекса в строковых операциях.

   Индекс назначения (Destination Index или DI) — используется в качестве индекса назначения в строковых операциях.


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

Регистром управления является объединённый 32-битный регистр инструкций и 32-битный регистр флагов (регистр процессора, отражающий его текущее состояние). Многие инструкции включают в себя операции сравнения и математические вычисления, которые способны изменить состояние флагов, а некоторые другие условные инструкции проверяют значения флагов состояния, чтобы перенести поток управления в другое место.

Распространённые битовые флаги:

   Флаг переполнения (Overflow Flag или OF) — указывает на переполнение старшего бита данных (крайнего левого бита) после signed арифметической операции.

   Флаг направления (Direction Flag или DF) — определяет направление влево или вправо для перемещения или сравнения строковых данных. Если DF = 0, то строковая операция принимает направление слева направо, а когда DF = 1, то строковая операция принимает направление справа налево.

   Флаг прерывания (Interrupt Flag или IF) — определяет, будут ли игнорироваться или обрабатываться внешние прерывания (например, ввод с клавиатуры и т.д.). Он отключает внешнее прерывание, когда значение равно 0, и разрешает прерывание, когда установлено значение 1.

   Флаг ловушка (Trap Flag или TF) — позволяет настроить работу процессора в одношаговом режиме.

   Флаг знака (Sign Flag или SF) — показывает знак результата арифметической операции. Этот флаг устанавливается в соответствии со знаком элемента данных после выполнения арифметической операции. Знак определяется по старшему левому биту. Положительный результат сбрасывает значение SF до 0, а отрицательный результат устанавливает его равным 1.

   Нулевой флаг (Zero Flag или ZF) — указывает результат арифметической операции или операции сравнения. Ненулевой результат сбрасывает нулевой флаг до 0, а нулевой результат устанавливает его равным 1.

   Вспомогательный флаг переноса (Auxiliary Carry Flag или AF) — после выполнения арифметической операции содержит перенос с бита 3 на бит 4. Используется для специализированной арифметики. AF устанавливается, когда 1-байтовая арифметическая операция вызывает перенос из бита 3 в бит 4.

   Флаг равенства (Parity Flag или PF) — указывает общее количество 1-бит в результате, полученном после выполнения арифметической операции. Чётное число 1-бит сбрасывает PF до 0, а нечётное число 1-бит устанавливает PF равным 1.

   Флаг переноса (Carry Flag или CF) — после выполнения арифметической операции содержит перенос 0 или 1 из старшего бита (крайнего слева). Кроме того, хранит содержимое последнего бита операции сдвига или поворота.

В таблице ниже указано положение битовых флагов в 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 0

Сегментные регистры

Сегменты — это специфические части программы, которые содержат данные, код и стек. Есть три основных сегмента:

   Сегмент кода (Code Segment или CS) — содержит все команды и инструкции, которые должны быть выполнены. 16-битный регистр сегмента кода или регистр CS хранит начальный адрес сегмента кода.

   Сегмент данных (Data Segment или DS) — содержит данные, константы и рабочие области. 16-битный регистр сегмента данных или регистр DS хранит начальный адрес сегмента данных.

   Сегмент стека (Stack Segment или SS) — содержит данные и возвращаемые адреса процедур или подпрограмм. Он представлен в виде структуры данных «Стек». Регистр сегмента стека или регистр SS хранит начальный адрес стека.

Кроме регистров CS, DS и SS существуют и другие регистры дополнительных сегментов (Extra Segment или ES), FS и GS, которые предоставляют дополнительные сегменты для хранения данных.

При написании программ на ассемблере, программе необходим доступ к ячейкам памяти. Все области памяти в сегменте относятся к начальному адресу сегмента. Сегмент начинается с адреса, равномерно делимого на десятичное 16 или на шестнадцатеричное 10. Таким образом, крайняя правая шестнадцатеричная цифра во всех таких адресах памяти равна 0, что обычно не сохраняется в сегментных регистрах.

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

Пример на практике

Посмотрите на следующую простую программу, чтобы понять, как используются регистры в программировании на ассемблере. Эта программа выводит 9 звездочек с простым сообщением:

section .text global _start ; должно быть объявлено для линкера (gcc) _start: ; сообщаем линкеру точку входу mov edx,len ; длина сообщения mov ecx,msg ; сообщение для написания mov ebx,1 ; файловый дескриптор (stdout) mov eax,4 ; номер системного вызова (sys_write) int 0x80 ; вызов ядра mov edx,9 ; длина сообщения mov ecx,s2 ; сообщение для написания mov ebx,1 ; файловый дескриптор (stdout) mov eax,4 ; номер системного вызова (sys_write) int 0x80 ; вызов ядра mov eax,1 ; номер системного вызова (sys_exit) int 0x80 ; вызов ядра section .data msg db 'Displaying 9 stars',0xa ; наше сообщение len equ $ - msg ; длина нашего сообщения s2 times 9 db '*'

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

section .text

   global _start ; должно быть объявлено для линкера (gcc)

_start:          ; сообщаем линкеру точку входу

   mov edx,len  ; длина сообщения

   mov ecx,msg  ; сообщение для написания

   mov ebx,1    ; файловый дескриптор (stdout)

   mov eax,4    ; номер системного вызова (sys_write)

   int 0x80     ; вызов ядра

   mov edx,9    ; длина сообщения

   mov ecx,s2   ; сообщение для написания

   mov ebx,1    ; файловый дескриптор (stdout)

   mov eax,4    ; номер системного вызова (sys_write)

   int 0x80     ; вызов ядра

   mov eax,1    ; номер системного вызова (sys_exit)

   int 0x80     ; вызов ядра

section .data

msg db 'Displaying 9 stars',0xa ; наше сообщение

len equ $ - msg  ; длина нашего сообщения

s2 times 9 db '*'

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

Displaying 9 stars
*********

Оценить статью:

Загрузка...

Поделиться в социальных сетях:

Урок 2. Переменные в ассемблере и работа с ними

  • Подробности
  • Категория: Уроки

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

Перемеренные 

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

Так вот объявление переменных осуществляется с помощью псевдокоманд. Переменная это по сути область памяти с которой мы работаем. Для того чтоб объявить ее используют директивы (псевдокоманды) DB, DW DD,DQ и DT . Вернее эти директивы просто говорят ассемблеру что нужно заполнить память указанным количеством байт, а также чем именно заполнить.

  • DB  - говорит что нужно выделить 1 байт.
  • DW - что два байта (define word - определить слово)
  • DD - определить двойное слово (четыре байта)
  • DQ -определить восемь байт или четыре слова
  • DT -определить десть байт

К примеру чтоб объявить перемененную на ассемблере в 1 байт которая будет равна 10:
имя_переменной DD 10 

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

mov ax,100   ;
mov temp,ax ;теперь наша переменная равна 100(прошу обратить внимание что в данном случае переменная должна равняться два байта )

Я думаю Вы уже за метели что в тексте программы ассемблера часто встречаться точка с запятой, так вот это комментарии, вернее все то что после точки запятой к примеру: команда ; комментарий 

Теперь давайте рассмотрим ситуацию когда нам нужно объявить строку, для это нужно всего навсего написать такой код:

tempstr DB "hello world$"

Код выше что мы объявили строку  в 12 байт! Прошу Вас заметить что при работе со строками в DOS символ $ является концом строки.

Для вывода строк  используется  прерывание 21h и его 9я функция.  То есть  регистр AH должен содержать номер функции а регистр DX должен хранить адрес переменной которую хотим вывести на экран. Для того чтобы поучить адрес переменно используется директива OFFSET. К примеру чтоб внести в DX адрес переменной tempstr нужно выполнить следующее:

MOV DX, OFFEST tempstr 

Давайте теперь рассмотрим пример программы которая выводит строку на экран:

.model small
.data ;начало сегмента даты
tempstr     DB "Hello World! $"
.code ;начало сегмента кода
main: ;начало нашей программы

mov ax, @data ; настраивается сегментный регистр, вносим адрес сегмента даты в AX
mov ds, ax ; а затем с АХ помещаем адрес в DS чтоб можно было работать с переменными которые в ;сегменте даты

mov ah, 09
mov dx, offset tempstr ; вывод сообщения
int 21h

mov ax,4c00h ; выход
int 21h

end main


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

Ввод строки

Для ввода строк предусмотрено множество функций. Мы для примера возьмем все тоже прерывание DOS 21h в котором есть функция под номером 0ah. Данная функция принимает строку первый элемент которой число которое указывает за максимальное количество вводимых символов, а на выходе второй элемент будет указывать на количество введенных пользователем символов. 

|5|0|0|0|0|0|0|0|$|  наша строка на начале ввода, и затем мы ввели HELLO

|5|5|H|E|L|L|O|#|$| такая строка у нас получается на выходе.

И это все нужно учитывать! Для вывода  нам нужно к адресу строки прибавить 2 чтоб мы вывели суму введенную строку.  Вот пример программы которая принимает строку а затем выводит ее на экран: 

.model small
.data

 
EnterS DB "Enter String:$"
endl     DB 10,13,'$'
buf db  20 dup('$')

.code
main:

mov ax, @data ; настраивается сегментный регистр
mov ds, ax

mov ah, 9
mov dx, offset EnterS 
int 21h

mov ah, 0ah
mov dx, offset buf 
int 21h

mov ah, 9
mov dx, offset endl
int 21h

mov ah, 09
mov dx, offset buf 
add dx,2
int 21h

mov ah, 9
mov dx, offset endl 
int 21h

mov ax,4c00h ; выход
int 21h

end main

Результат работы программы:


В программе есть для вас новая директива DUP. К примеру если Вам нужно ввести 100 одинаковых символов или вам просто требуется выделить память для того чтоб ее потом использовать вот для этого и служит эта директива. В данном примере мы выделили 20 байт которые заполнили символом $ . Эта переменная из 20 байт служила для того чтоб в нее вводилась строка.  И обратите внимание когда мы выводим нашу введенную строку , сначала мы получаем адрес ее а потом прибавляем  2 с помощь команды AAD, про которую мы поговорим в следующем уроке, для того чтоб пропустить 2 первых элемента которые нужны для ее ввода.

 На этом второй урок из цикла уроки ассемблера окончен. Желаю успехов Вам! 

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

Ассемблер. Числа | Уроки Ассемблера

  Обновл. 26 Ноя 2019  | 

Обычно числовые данные в Ассемблере представлены в двоичной системе. Арифметические инструкции работают с двоичными данными. Когда числа отображаются на экране или вводятся с клавиатуры, они имеют ASCII-форму.

Представление чисел в Ассемблере

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

Например:

section .text global _start ; должно быть объявлено для использования gcc _start: ; сообщаем линкеру входную точку mov eax,'3' sub eax, '0' mov ebx, '4' sub ebx, '0' add eax, ebx add eax, '0' mov [sum], eax mov ecx,msg mov edx, len mov ebx,1 ; файловый дескриптор (stdout) mov eax,4 ; номер системного вызова (sys_write) int 0x80 ; вызов ядра mov ecx,sum mov edx, 1 mov ebx,1 ; файловый дескриптор (stdout) mov eax,4 ; номер системного вызова (sys_write) int 0x80 ; вызов ядра mov eax,1 ; номер системного вызова (sys_exit) int 0x80 ; вызов ядра section .data msg db "The sum is:", 0xA,0xD len equ $ - msg segment .bss sum resb 1

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

section .text

   global _start        ; должно быть объявлено для использования gcc

_start:                 ; сообщаем линкеру входную точку

   mov eax,'3'

   sub     eax, '0'

   mov ebx, '4'

   sub     ebx, '0'

   add eax, ebx

   add eax, '0'

   mov [sum], eax

   mov ecx,msg

   mov edx, len

   mov ebx,1          ; файловый дескриптор (stdout)

   mov eax,4          ; номер системного вызова (sys_write)

   int 0x80          ; вызов ядра

   mov ecx,sum

   mov edx, 1

   mov ebx,1          ; файловый дескриптор (stdout)

   mov eax,4          ; номер системного вызова (sys_write)

   int 0x80          ; вызов ядра

   mov eax,1          ; номер системного вызова (sys_exit)

   int 0x80          ; вызов ядра

section .data

msg db "The sum is:", 0xA,0xD

len equ $ - msg  

segment .bss

sum resb 1

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

The sum is:
7

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

   ASCII-форма;

   BCD-форма (или ещё «Двоично-десятичный код»).

Представление ASCII

В представлении ASCII десятичные числа хранятся в виде строки ASCII-символов. Есть четыре инструкции для обработки чисел в представлении ASCII:

   AAA (англ. «Adjust After Addition») — настроить после добавления;

   AAS (англ. «Adjust After Subtraction») — настроить после вычитания;

   AAM (англ. «Adjust After Multiplication») — настроить после умножения;

   AAD (англ. «Adjust After Division») — настроить после деления.

Эти инструкции не принимают никаких операндов и предполагают, что требуемый операнд находится в регистре AL.

В следующем примере мы будем использовать инструкцию AAS:

section .text global _start ; должно быть объявлено для использования gcc _start: ; сообщаем линкеру входную точку sub ah, ah mov al, '9' sub al, '3' aas or al, 30h mov [res], ax mov edx,len ; длина сообщения mov ecx,msg ; сообщение для написания mov ebx,1 ; файловый дескриптор (stdout) mov eax,4 ; номер системного вызова (sys_write) int 0x80 ; вызов ядра mov edx,1 ; длина сообщения mov ecx,res ; сообщение для написания mov ebx,1 ; файловый дескриптор (stdout) mov eax,4 ; номер системного вызова (sys_write) int 0x80 ; вызов ядра mov eax,1 ; номер системного вызова (sys_exit) int 0x80 ; вызов ядра section .data msg db 'The Result is:',0xa len equ $ - msg section .bss res resb 1

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

section .text

   global _start        ; должно быть объявлено для использования gcc

_start:                 ; сообщаем линкеру входную точку

   sub     ah, ah

   mov     al, '9'

   sub     al, '3'

   aas

   or      al, 30h

   mov     [res], ax

   mov edx,len         ; длина сообщения

   mov ecx,msg         ; сообщение для написания

   mov ebx,1         ; файловый дескриптор (stdout)

   mov eax,4         ; номер системного вызова (sys_write)

   int 0x80         ; вызов ядра

   mov edx,1         ; длина сообщения

   mov ecx,res         ; сообщение для написания

   mov ebx,1         ; файловый дескриптор (stdout)

   mov eax,4         ; номер системного вызова (sys_write)

   int 0x80         ; вызов ядра

   mov eax,1         ; номер системного вызова (sys_exit)

   int 0x80         ; вызов ядра

section .data

msg db 'The Result is:',0xa

len equ $ - msg

section .bss

res resb 1

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

The Result is:
6

Представление BCD

Существует два типа представления BCD:

   распакованное представление BCD;

   упакованное представление BCD.

В распакованном представлении BCD каждый байт хранит двоичный эквивалент каждой десятичной цифры числа. Четыре инструкции настройки ASCII: AAA, AAS, AAM и AAD также могут использоваться с распакованным представлением BCD.

В упакованном представлении BCD каждая цифра сохраняется с использованием четырёх бит. Две десятичные цифры упаковываются в 1 байт. Есть две инструкции для обработки этих чисел:

   DAA (англ. «Decimal Adjust After Addition») — десятичная настройка после добавления;

   DAS (англ. «Decimal Adjust After Subtraction») — десятичная настройка после вычитания.

Обратите внимание, что в упакованном представлении BCD отсутствует поддержка операций умножения и деления.

В следующей программе мы выполним операцию сложения двух 5-значных десятичных чисел и выведем на экран их сумму:

section .text global _start ; должно быть объявлено для использования gcc _start: ; сообщаем линкеру входную точку mov esi, 4 ; указываем на крайнюю правую цифру mov ecx, 5 ; количество цифр clc add_loop: mov al, [num1 + esi] adc al, [num2 + esi] aaa pushf or al, 30h popf mov [sum + esi], al dec esi loop add_loop mov edx,len ; длина сообщения mov ecx,msg ; сообщение для написания mov ebx,1 ; файловый дескриптор (stdout) mov eax,4 ; номер системного вызова (sys_write) int 0x80 ; вызов ядра mov edx,5 ; длина сообщения mov ecx,sum ; сообщение для написания mov ebx,1 ; файловый дескриптор (stdout) mov eax,4 ; номер системного вызова (sys_write) int 0x80 ; вызов ядра mov eax,1 ; номер системного вызова (sys_exit) int 0x80 ; вызов ядра section .data msg db 'The Sum is:',0xa len equ $ - msg num1 db '12345' num2 db '23456' sum db ' '

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

section .text

   global _start        ; должно быть объявлено для использования gcc

 

_start:                 ; сообщаем линкеру входную точку

 

   mov     esi, 4       ; указываем на крайнюю правую цифру

   mov     ecx, 5       ; количество цифр

   clc

add_loop:  

   mov al, [num1 + esi]

   adc al, [num2 + esi]

   aaa

   pushf

   or al, 30h

   popf

   mov [sum + esi], al

   dec esi

   loop add_loop

   mov edx,len         ; длина сообщения

   mov ecx,msg         ; сообщение для написания

   mov ebx,1         ; файловый дескриптор (stdout)

   mov eax,4         ; номер системного вызова (sys_write)

   int 0x80         ; вызов ядра

   mov edx,5         ; длина сообщения

   mov ecx,sum         ; сообщение для написания

   mov ebx,1         ; файловый дескриптор (stdout)

   mov eax,4         ; номер системного вызова (sys_write)

   int 0x80         ; вызов ядра

   mov eax,1         ; номер системного вызова (sys_exit)

   int 0x80         ; вызов ядра

 

section .data

msg db 'The Sum is:',0xa

len equ $ - msg

num1 db '12345'

num2 db '23456'

sum db '     '

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

The Sum is:
35801


Оценить статью:

Загрузка...

Поделиться в социальных сетях:

Ассемблер. Арифметические инструкции | Уроки Ассемблера

  Обновл. 20 Окт 2019  | 

В этом уроке мы будем разбираться с арифметическими инструкциями в ассемблере на примере INC, DEC, ADD, SUB и пр.

Инструкция INC

Инструкция INC (от англ. «INCREMENT») используется для увеличения операнда на единицу. Она работает с одним операндом, который может находиться либо в регистре, либо в памяти.

Синтаксис инструкции INC:

INC место_назначения

Операндом место_назначения может быть 8-битный, 16-битный или 32-битный операнд.

Пример:

INC EBX ; выполняем инкремент 32-битного регистра INC DL ; выполняем инкремент 8-битного регистра INC [count] ; выполняем инкремент переменной count

INC EBX      ; выполняем инкремент 32-битного регистра

INC DL       ; выполняем инкремент 8-битного регистра

INC [count]  ; выполняем инкремент переменной count

Инструкция DEC

Инструкция DEC (от англ. «DECREMENT») используется для уменьшения операнда на единицу. Она работает с одним операндом, который может находиться либо в регистре, либо в памяти.

Синтаксис инструкции DEC:

DEC место_назначения

Операндом место_назначения может быть 8-битный, 16-битный или 32-битный операнд.

Пример:

segment .data count dw 0 value db 15 segment .text inc [count] dec [value] mov ebx, count inc word [ebx] mov esi, value dec byte [esi]

segment .data

   count dw  0

   value db  15

segment .text

   inc [count]

   dec [value]

   mov ebx, count

   inc word [ebx]

   mov esi, value

   dec byte [esi]

Инструкции ADD и SUB

Инструкции ADD и SUB используются для выполнения простого сложения/вычитания двоичных данных размером в byte, word и doubleword, то есть для сложения или вычитания 8-битных, 16-битных или 32-битных операндов, соответственно.

Синтаксис инструкций ADD и SUB:

ADD/SUB      место_назначения, источник

Инструкции ADD/SUB могут выполняться между:

   регистром и регистром;

   памятью и регистром;

   регистром и памятью;

   регистром и константами;

   памятью и константами.

Однако, как и другие инструкции, операции типа память-в-память невозможны с использованием инструкций ADD/SUB. Операции ADD или SUB устанавливают или сбрасывают флаги переполнения и переноса.

В следующем примере мы спрашиваем у пользователя два числа, сохраняем их в регистрах EAX и EBX, затем выполняем операцию сложения, сохраняем результат в ячейке памяти res и выводим его на экран:

SYS_EXIT equ 1 SYS_READ equ 3 SYS_WRITE equ 4 STDIN equ 0 STDOUT equ 1 segment .data msg1 db "Enter a digit ", 0xA,0xD len1 equ $- msg1 msg2 db "Please enter a second digit", 0xA,0xD len2 equ $- msg2 msg3 db "The sum is: " len3 equ $- msg3 segment .bss num1 resb 2 num2 resb 2 res resb 1 section .text global _start ; должно быть объявлено для использования gcc _start: ; сообщаем линкеру входную точку mov eax, SYS_WRITE mov ebx, STDOUT mov ecx, msg1 mov edx, len1 int 0x80 mov eax, SYS_READ mov ebx, STDIN mov ecx, num1 mov edx, 2 int 0x80 mov eax, SYS_WRITE mov ebx, STDOUT mov ecx, msg2 mov edx, len2 int 0x80 mov eax, SYS_READ mov ebx, STDIN mov ecx, num2 mov edx, 2 int 0x80 mov eax, SYS_WRITE mov ebx, STDOUT mov ecx, msg3 mov edx, len3 int 0x80 ; перемещаем первое число в регистр eax, а второе число - в регистр ebx ; и вычитаем ASCII '0' для конвертации в десятичное число mov eax, [num1] sub eax, '0' mov ebx, [num2] sub ebx, '0' ; добавляем eax и ebx add eax, ebx ; добавляем '0' для конвертации суммы из десятичной системы в ASCII add eax, '0' ; сохраняем сумму в ячейке памяти res mov [res], eax ; выводим сумму mov eax, SYS_WRITE mov ebx, STDOUT mov ecx, res mov edx, 1 int 0x80 exit: mov eax, SYS_EXIT xor ebx, ebx int 0x80

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

SYS_EXIT  equ 1

SYS_READ  equ 3

SYS_WRITE equ 4

STDIN     equ 0

STDOUT    equ 1

 

segment .data

 

   msg1 db "Enter a digit ", 0xA,0xD

   len1 equ $- msg1

 

   msg2 db "Please enter a second digit", 0xA,0xD

   len2 equ $- msg2

 

   msg3 db "The sum is: "

   len3 equ $- msg3

 

segment .bss

 

   num1 resb 2

   num2 resb 2

   res resb 1    

 

section .text

   global _start    ; должно быть объявлено для использования gcc

_start:             ; сообщаем линкеру входную точку

   mov eax, SYS_WRITE        

   mov ebx, STDOUT        

   mov ecx, msg1        

   mov edx, len1

   int 0x80                

 

   mov eax, SYS_READ

   mov ebx, STDIN  

   mov ecx, num1

   mov edx, 2

   int 0x80            

 

   mov eax, SYS_WRITE        

   mov ebx, STDOUT        

   mov ecx, msg2          

   mov edx, len2        

   int 0x80

 

   mov eax, SYS_READ  

   mov ebx, STDIN  

   mov ecx, num2

   mov edx, 2

   int 0x80        

 

   mov eax, SYS_WRITE        

   mov ebx, STDOUT        

   mov ecx, msg3          

   mov edx, len3        

   int 0x80

 

   ; перемещаем первое число в регистр eax, а второе число - в регистр ebx

   ; и вычитаем ASCII '0' для конвертации в десятичное число

   mov eax, [num1]

   sub eax, '0'

   mov ebx, [num2]

   sub ebx, '0'

 

   ; добавляем eax и ebx

   add eax, ebx

   ; добавляем '0' для конвертации суммы из десятичной системы в ASCII

   add eax, '0'

 

   ; сохраняем сумму в ячейке памяти res

   mov [res], eax

 

   ; выводим сумму

   mov eax, SYS_WRITE        

   mov ebx, STDOUT

   mov ecx, res        

   mov edx, 1        

   int 0x80

 

exit:    

  

   mov eax, SYS_EXIT  

   xor ebx, ebx

   int 0x80

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

Enter a digit:
3
Please enter a second digit:
4
The sum is:
7

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

section .text global _start ; должно быть объявлено для использования gcc _start: ; сообщаем линкеру входную точку mov eax,'3' sub eax, '0' mov ebx, '4' sub ebx, '0' add eax, ebx add eax, '0' mov [sum], eax mov ecx,msg mov edx, len mov ebx,1 ; файловый дескриптор (stdout) mov eax,4 ; номер системного вызова (sys_write) int 0x80 ; вызов ядра mov ecx,sum mov edx, 1 mov ebx,1 ; файловый дескриптор (stdout) mov eax,4 ; номер системного вызова (sys_write) int 0x80 ; вызов ядра mov eax,1 ; номер системного вызова (sys_exit) int 0x80 ; вызов ядра section .data msg db "The sum is:", 0xA,0xD len equ $ - msg segment .bss sum resb 1

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

section .text

   global _start    ; должно быть объявлено для использования gcc

_start:             ; сообщаем линкеру входную точку

   mov eax,'3'

   sub     eax, '0'

   mov ebx, '4'

   sub     ebx, '0'

   add eax, ebx

   add eax, '0'

   mov [sum], eax

   mov ecx,msg

   mov edx, len

   mov ebx,1 ; файловый дескриптор (stdout)

   mov eax,4 ; номер системного вызова (sys_write)

   int 0x80 ; вызов ядра

   mov ecx,sum

   mov edx, 1

   mov ebx,1 ; файловый дескриптор (stdout)

   mov eax,4 ; номер системного вызова (sys_write)

   int 0x80 ; вызов ядра

   mov eax,1 ; номер системного вызова (sys_exit)

   int 0x80 ; вызов ядра

section .data

   msg db "The sum is:", 0xA,0xD

   len equ $ - msg  

   segment .bss

   sum resb 1

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

The sum is:
7

Инструкции MUL и IMUL

Есть две инструкции для умножения двоичных данных:

   инструкция MUL (от англ. «MULTIPLY») обрабатывает данные unsigned;

   инструкция IMUL (от англ. «INTEGER MULTIPLY») обрабатывает данные signed.

Обе инструкции влияют на флаги переноса и переполнения.

Синтаксис инструкций MUL/IMUL:

MUL/IMUL множитель

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

Рассмотрим 3 разных сценария:

Сценарий №1: Когда перемножаются 2 значения типа byte — множимое находится в регистре AL, а множителем является значение типа byte в памяти или в другом регистре. Результат произведения находится в AX. Старшие 8 бит произведения хранятся в AH, а младшие 8 бит — хранятся в AL:

Сценарий №2: Когда перемножаются 2 значения типа word множимое должно быть в регистре AX, а множителем является значение типа word в памяти или в другом регистре. Например, для такой инструкции, как MUL DX, вы должны сохранить множитель в DX, а множимое — в AX. В результате получится значение типа doubleword для которого понадобятся два регистра. Часть высшего порядка (крайняя слева) сохраняется в DX, а часть нижнего порядка (крайняя справа) — сохраняется в AX:

Сценарий №3: Когда перемножаются 2 значения типа doubleword — множимое должно находится в EAX, а множителем является значение типа doubleword, хранящееся в памяти или в другом регистре. Результат умножения сохраняется в регистрах EDX и EAX. Биты старшего порядка сохраняются в регистре EDX, а биты младшего порядка — сохраняются в регистре EAX:

Пример:

MOV AL, 10 MOV DL, 25 MUL DL ... MOV DL, 0FFH ; DL= -1 MOV AL, 0BEH ; AL = -66 IMUL DL

MOV AL, 10

MOV DL, 25

MUL DL

...

MOV DL, 0FFH ; DL= -1

MOV AL, 0BEH ; AL = -66

IMUL DL

А в следующем примере мы 3 умножаем на 2 и выводим результат:

section .text global _start ; должно быть объявлено для использования gcc _start: ; сообщаем линкеру входную точку mov al,'3' sub al, '0' mov bl, '2' sub bl, '0' mul bl add al, '0' mov [res], al mov ecx,msg mov edx, len mov ebx,1 ; файловый дескриптор (stdout) mov eax,4 ; номер системного вызова (sys_write) int 0x80 ; вызов ядра mov ecx,res mov edx, 1 mov ebx,1 ; файловый дескриптор (stdout) mov eax,4 ; номер системного вызова (sys_write) int 0x80 ; вызов ядра mov eax,1 ; номер системного вызова (sys_exit) int 0x80 ; вызов ядра section .data msg db "The result is:", 0xA,0xD len equ $- msg segment .bss res resb 1

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

section .text

   global _start    ; должно быть объявлено для использования gcc

_start:             ; сообщаем линкеру входную точку

 

   mov al,'3'

   sub     al, '0'

   mov bl, '2'

   sub     bl, '0'

   mul bl

   add al, '0'

   mov [res], al

   mov ecx,msg

   mov edx, len

   mov ebx,1 ; файловый дескриптор (stdout)

   mov eax,4 ; номер системного вызова (sys_write)

   int 0x80 ; вызов ядра

   mov ecx,res

   mov edx, 1

   mov ebx,1 ; файловый дескриптор (stdout)

   mov eax,4 ; номер системного вызова (sys_write)

   int 0x80 ; вызов ядра

   mov eax,1 ; номер системного вызова (sys_exit)

   int 0x80 ; вызов ядра

 

section .data

msg db "The result is:", 0xA,0xD

len equ $- msg  

segment .bss

res resb 1

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

The result is:
6

Инструкции DIV и IDIV

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

Инструкция DIV (от англ. «DIVIDE») используется с данными unsigned, а инструкция IDIV (от англ. «INTEGER DIVIDE») используется с данными signed.

Синтаксис инструкций DIV/IDIV:

DIV/IDIV     делитель

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

Рассмотрим следующие 3 сценария:

Сценарий №1: Когда делителем является значение типа byte — предполагается, что делимое находится в регистре AX (16 бит). После деления частное переходит в регистр AL, а остаток переходит в регистр AH:

Сценарий №2: Когда делителем является значение типа word — предполагается, что делимое имеет длину 32 бита и находится в регистрах DX и AX. Старшие 16 битов находятся в DX, а младшие 16 битов — в AX. После деления 16-битное частное попадает в регистр AX, а 16-битный остаток — в регистр DX:

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

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

section .text global _start ; должно быть объявлено для использования gcc _start: ; сообщаем линкеру входную точку mov ax,'8' sub ax, '0' mov bl, '2' sub bl, '0' div bl add ax, '0' mov [res], ax mov ecx,msg mov edx, len mov ebx,1 ; файловый дескриптор (stdout) mov eax,4 ; номер системного вызова (sys_write) int 0x80 ; вызов ядра mov ecx,res mov edx, 1 mov ebx,1 ; файловый дескриптор (stdout) mov eax,4 ; номер системного вызова (sys_write) int 0x80 ; вызов ядра mov eax,1 ; номер системного вызова (sys_exit) int 0x80 ; вызов ядра section .data msg db "The result is:", 0xA,0xD len equ $- msg segment .bss res resb 1

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

section .text

   global _start    ; должно быть объявлено для использования gcc

_start:             ; сообщаем линкеру входную точку

   mov ax,'8'

   sub     ax, '0'

   mov bl, '2'

   sub     bl, '0'

   div bl

   add ax, '0'

   mov [res], ax

   mov ecx,msg

   mov edx, len

   mov ebx,1 ; файловый дескриптор (stdout)

   mov eax,4 ; номер системного вызова (sys_write)

   int 0x80 ; вызов ядра

   mov ecx,res

   mov edx, 1

   mov ebx,1 ; файловый дескриптор (stdout)

   mov eax,4 ; номер системного вызова (sys_write)

   int 0x80 ; вызов ядра

   mov eax,1 ; номер системного вызова (sys_exit)

   int 0x80 ; вызов ядра

section .data

msg db "The result is:", 0xA,0xD

len equ $- msg  

segment .bss

res resb 1

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

The result is:
4

На этом всё!


Оценить статью:

Загрузка...

Поделиться в социальных сетях:

Руководство по сборке x86

Руководство по сборке x86
Университет Вирджинии, компьютерные науки
CS216: Программа и представление данных, весна 2006 г.

19 ноября 2018

Содержание: регистров | Память и Обращение | Инструкции | Соглашение о вызовах

В этом руководстве описаны основы 32-разрядного языка ассемблера x86. программирование, охватывающее небольшое, но полезное подмножество доступных инструкции и директивы ассемблера.Есть несколько разных языки ассемблера для генерации машинного кода x86. Тот, который мы будем использовать в CS216 - ассемблер Microsoft Macro Assembler (MASM). MASM использует стандартный синтаксис Intel для написания ассемблерного кода x86.

Полный набор инструкций x86 большой и сложный (Intel x86 инструкции по эксплуатации содержат более 2900 страниц), и мы не покрываем все это в этом руководстве. Например, есть 16-битное подмножество x86 набор инструкций.Использование 16-битной модели программирования может быть довольно сложный. Имеет сегментированную модель памяти, больше ограничений на регистр использование и так далее. В этом руководстве мы ограничим наше внимание другими современные аспекты программирования x86 и углубиться в набор инструкций только достаточно подробно, чтобы получить базовое представление о программировании x86.

Ресурсы

Регистры

Современные (например, 386 и выше) процессоры x86 имеют восемь 32-битных общих регистры назначения, как показано на рисунке 1.Имена регистров в основном исторический. Например, EAX раньше назывался аккумулятор, так как он использовался для ряда арифметических операций, и ECX был известен как счетчик, поскольку он использовался для удержания цикла индекс. В то время как большинство регистров утратили свое специальное назначение в в современном наборе команд по соглашению две зарезервированы для специальных цели - указатель стека (ESP) и базовый указатель (EBP).

Для EAX, EBX, ECX и Регистры EDX, можно использовать подразделы.Например, наименее 2 значимых байта EAX можно рассматривать как 16-битный регистр называется AX. Наименее значимый байт AX может быть используется как один 8-битный регистр, называемый AL, в то время как большинство значащий байт AX может использоваться как один 8-битный регистр называется AH. Эти имена относятся к одному и тому же физическому регистр. Когда двухбайтовая величина помещается в DX, обновление влияет на значение DH, DL и EDX. Эти субрегистры в основном являются пережитками старых, 16-битные версии набора команд.Однако иногда они удобно при работе с данными размером меньше 32 бит (например, 1-байтовые символы ASCII).

При обращении к регистрам в сборке язык, имена не чувствительны к регистру. Например, имена EAX и eax относятся к одному регистру.


Рисунок 1. Регистры x86

Память и режимы адресации

Объявление областей статических данных
Вы можете объявить области статических данных (аналогично глобальным переменным) в x86 с использованием для этого специальных директив ассемблера.Данные объявлениям должен предшествовать .DATA директива. Следуя этой директиве, директивы DB, DW и DD могут использоваться для объявления одно-, двух- и четырехбайтовых местоположения данных соответственно. Заявленные местоположения могут быть помечены имена для дальнейшего использования - это похоже на объявление переменных имя, но соблюдает некоторые правила более низкого уровня. Например, локации заявленные последовательно будут располагаться в памяти рядом друг с другом.

Примеры объявлений:

.ДАННЫЕ
var DB 64
var2 ДБ?
ДБ 10
х DW?
Y DD 30000

В отличие от языков высокого уровня, где массивы могут иметь много измерений и доступны по индексам, массивы на ассемблере x86 - это просто количество ячеек, расположенных в памяти подряд.Можно объявить массив просто перечислив значения, как в первом примере ниже. Два других распространенными методами, используемыми для объявления массивов данных, являются директива DUP и использование строковых литералов. В Директива DUP предписывает ассемблеру дублировать выражение заданное количество раз. Например, 4 DUP (2) эквивалентно 2, 2, 2, 2.

Некоторые примеры:

Z DD 1, 2, 3
байт DB 10 DUP (?)
около ДД 100 ДУП (0)
ул. БД «привет», 0
Адресация памяти
Современные x86-совместимые процессоры способны адресовать до 2 32 байта памяти: адреса памяти имеют размер 32 бита.В приведенные выше примеры, где мы использовали метки для обозначения областей памяти, эти метки фактически заменены ассемблером на 32-битный количества, указывающие адреса в памяти. Дополнительно.

Простой 8-битный ассемблер - Справка по набору команд

Введение

Этот симулятор обеспечивает упрощенный синтаксис ассемблера (на основе NASM) и имитирует x86-подобный процессор. Подробную документацию и введение в ассемблер можно найти на следующих сайтах:

Симулятор состоит из 8-битного процессора и 256 байт памяти. Все инструкции (код) и переменные (данные) должны уместиться в памяти. Для простоты каждая инструкция (и операнд) имеет размер 1 байт.Следовательно, инструкция MOV будет использовать 3 байта памяти. Симулятор предоставляет консольный вывод, который отображает память от 0xE8 до 0xFF. Отображение памяти означает, что каждое значение, записанное в этот блок памяти, отображается на консоли.

Синтаксис

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

 метка: операнды инструкции; Комментарий 

Допустимые форматы чисел для констант:

Десятичный: 200
Десятичный: 200d
Шестнадцатеричный: 0xA4
Восьмеричный: 0o48
Двоичный: 101b
 

Можно определить число, используя символ или несколько чисел (см. Инструкцию DB ), используя строку.

Характер: «А»
Строка: «Привет, мир!»
 

Операнды могут быть одним из четырех регистров общего назначения, регистром указателя стека, адресом памяти или константой. Регистр указателя стека может использоваться только как операнд в инструкциях MOV, ADD, SUB, CMP, INC и DEC. Вместо определения адреса как константы или использования регистра вы можете использовать метки. Затем ассемблер заменит метку соответствующей константой.

Регистр общего назначения (GP): A, B, C, D
Регистр указателя стека: SP
Адрес с использованием регистра GP: [A]
Адрес с использованием регистра GP и смещения: [D-3]
Адрес с использованием регистра SP и смещения: [SP + 2]
Адрес с использованием константы: [100]
Адрес с помощью метки: метка
Константа: любое число от 0..255 (8 бит без знака)
Смещение для косвенной адресации: целое число от -16 до +15 (знак обязателен)
 
MOV - скопировать значение

Копирует значение из src в dest . Инструкция MOV - единственная, которая может напрямую изменять память. SP может использоваться как операнд с MOV.

MOV reg, reg
МОВ рег, адрес
MOV reg, постоянная
Адрес МОВ, рег
Адрес MOV, постоянный
 
DB - переменная

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

Константа БД
 
Математические операции
Сложение и вычитание

Складывает два числа или вычитает одно число из другого. Эти операции изменят флаг переноса и нуля. SP может использоваться как операнд с ADD и SUB.

ДОБАВИТЬ рег, рег
ДОБАВИТЬ рег, адрес
ADD reg, постоянная
SUB reg, reg
SUB reg, адрес
SUB reg, постоянная
 
Увеличение и уменьшение

Увеличивает или уменьшает регистр на единицу.Эти операции изменят флаг переноса и нуля. SP может использоваться как операнд с INC и DEC.

INC reg
Рег.
 
Умножение и деление

Умножает или делит регистр A на заданное значение. Эти операции изменят флаг переноса и нуля.

MUL reg
Адрес MUL
Константа MUL
DIV reg
DIV адрес
Константа DIV
 
Логические инструкции

Поддерживаются следующие логические инструкции: AND, OR, XOR, NOT.Эти операции изменят флаг переноса и нуля.

И рег, рег
И рег, адрес
И рег, постоянная
ИЛИ рег, рег
ИЛИ рег, адрес
ИЛИ рег, константа
XOR reg, reg
XOR reg, адрес
XOR reg, константа
НЕ рег
 
Инструкции по переключению

Поддерживаются следующие инструкции смены: SHL / SAL и SHR / SAR. Поскольку этот симулятор поддерживает только беззнаковые числа, SHR и SAR дают одинаковый результат. Эти операции изменят флаг переноса и нуля.

ШЛ рег, рег
SHL reg, адрес
SHL reg, постоянная
SHR рег, рег
SHR reg, адрес
Рег. SHR, постоянная
 
CMP - Сравнить

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

CMP reg, reg
CMP reg, адрес
Рег. CMP, постоянная
 
Прыжки
JMP - Безусловный переход

Разрешить указателю инструкции выполнить безусловный переход к заданному адресу.

Адрес JMP
 
Условные прыжки

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

Инструкция Описание Состояние Альтернативы
JC Перейти, если нести Перенести = ИСТИНА JB, JNAE
JNC Перейти, если нет переноски Carry = FALSE JNB, JAE
JZ Перейти, если ноль Ноль = ИСТИНА JB, JE
JNZ Перейти, если нет нуля Ноль = ЛОЖЬ JNE
JA > Carry = FALSE && Zero = FALSE JNBE
JNBE не <= Carry = FALSE && Zero = FALSE JA
JAE > = Carry = FALSE JNC, JNB
JNB не < Carry = FALSE JNC, JAE
JB < Перенести = ИСТИНА JC, JNAE
JNAE не> = Перенести = ИСТИНА JC, JB
JBE <= C = ИСТИНА или Z = ИСТИНА JNA
JNA не> C = ИСТИНА или Z = ИСТИНА JBE
JE = Z = ИСТИНА JZ
JNE ! = Z = ЛОЖЬ JNZ
CALL - вызов функции

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

ЗВОНИТЕ адрес
 
RET - Выход из подпрограммы

Выходит из подпрограммы, выталкивая адрес возврата, ранее введенный инструкцией CALL. Перед вызовом RET убедитесь, что SP сбалансирован, иначе указатель инструкции будет иметь неоднозначное значение.

RET
 
Инструкции стека
PUSH - Вставить в стек

Помещает значение в стек.Стек растет вниз, и текущая позиция доступна в регистре указателя стека (SP). Эта инструкция уменьшит SP.

PUSH reg
PUSH адрес
PUSH константа
 
POP - Поп из стека

Извлекает значение из стека в регистр. Эта инструкция увеличит SP.

POP reg
 
Прочие инструкции
HLT - Останавливает процессор.

Останавливает работу процессора.Нажмите кнопку «Сброс», чтобы сбросить IP перед перезапуском.

HLT
 

Марко Швайгхаузер (2015) | Лицензия MIT | Блог

.

Отправить ответ

avatar
  Подписаться  
Уведомление о