Что такое Word ptr в ассемблере

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

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

.186, .286, .386, .486, .586, .686 — использование инструкций процессоров 80186, 80286, 80386, 80486, Pentium, Pentium Pro

.286P, .386P, .486P, .586P, .686P — использование инструкций процессоров, включая инструкции защищенного режима

.287, .387 — использование инструкций математического сопроцессора

; по-умолчанию в начале ставится режим .8086 .model small .stack 100h .code .486 ; указываем режим 80486, чтобы иметь возможность использовать команду BSWAP start: mov edx, 01020304h bswap edx mov ax, 4C00h int 21h end start

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

Директива SEGMENT

name SEGMENT [align] [combine] [use] [‘class’]

Определяет сегмент с заданным именем name. Если сегмент с таким именем уже был определен ранее, то данный сегмент интерпретируется как продолжение предыдущего.

align — определяет выравнивание начального адреса сегмента на границу, определяемую значением параметра. Возможные значения:

BYTEвыравнивание не выполняется. Сегмент может начинаться с любого адреса памяти
WORDвыравнивание на границу слова (2 байта)
DWORDвыравнивание на границу двойного слова (4 байта)
PARAвыравнивание по границе параграфа (16 байт). Используется по-умолчанию.
PAGEвыравнивание на границу в 256 байт

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

PUBLICзаставляет компоновщик соединить все сегменты с одинаковым именем. Новый объединенный сегмент будет целым и непрерывным. Все адреса (смещения) объектов будут вычисляться относительно начала этого нового сегмента
STACKвыполняется конкатенация всех сегментов с одним и тем же именем для формирования одного непрерывного сегмента, затем регистр SS инициализируется значением начала сегмента, а SP — длиной сегмента. Если не указано ни одного сегмента стека, компоновщик выдаст предупреждение, что стековый сегмент не найден. Если сегмент стека создан, а комбинированный тип STACK не используется, программист должен явно загрузить в регистр SS адрес сегмента (подобно тому, как это делается для регистра DS)
COMMONданный сегмент и все другие сегменты с этим именем помещаются по одному адресу. Все сегменты с данным именем будут перекрываться и совместно использовать память. Размер полученного в результате сегмента будет равен размеру самого большого сегмента
AT addressсегмент помещается по абсолютному адресу параграфа address
PRIVATEданный сегмент не комбинируется с другими сегментами. Используется по-умолчанию

use — определяет разрядность сегмента. Возможные значения:

USE16сегмент с 16-разрядной адресацией. Максимальный размер сегмента 64 Кб
USE32сегмент с 32-разрядной адресацией. Максимальный размер сегмента 4 Гб. В модели памяти FLAT используется по-умолчанию

class — задает строковое значение "класса" сегмента. Компоновщик объединяет вместе в памяти все сегменты с одним и тем же именем класса.

Директива ENDS

ENDS

Определяет конец сегмента.

Директива ASSUME

ASSUME register:segment[,register:segment][, register:segment].

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

.386 stack16 segment stack db 100 dup(0) ends data16 segment var db 0 ends code16 segment use16 'code' assume cs:code16, ds:data16, ss:stack16 start: mov var,00h mov ax,4C00h int 21h ends end start

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

Директива .MODEL

.MODEL memory-model [, language-type] [, stack-option]

Задает модель памяти для упрощенных директив определения сегментов.

memory-model — модель памяти. Возможные значения:

TINYКод, данные и стек объединены в одну группу с именем DGROUP и размером до 64 Кб. Используется для создания программ формата .com. Некоторые языки эту модель не поддерживают. СS=DS=SS=DGROUP
SMALLКод занимает один сегмент, данные и стек объединены в одну группу с именем DGROUP (хотя для описания могут использоваться разные сегменты). Эту модель обычно используют для большинства программ на ассемблере. CS=_text DS=SS=DGROUP
MEDIUMКод занимает несколько сегментов, по одному на каждый объединяемый программный модуль. Все ссылки на передачу управления — типа far (вызов подпрограмм). Данные и стек объединены в одной группе DGROUP; все ссылки на них — типа near (для доступа к данным используется только смещение). CS=_text DS=SS=DGROUP
COMPACTКод находится в одном сегменте, данные и стек в группе DGROUP и могут занимать несколько сегментов, так что для обращения к данным требуется указывать сегмент и смещение (ссылка на данные — типа far). CS=_text DS=SS=DGROUP
LARGEКод может занимать несколько сегментов, по одному на каждый объединяемый программный модуль. Стек и данные находятся в группе DGROUP. Для ссылки на данные используются дальние указатели -far. CS=_text DS=SS=DGROUP
HUGEТо же, что модель LARGE
FLATТо же, что и модель TINY, но для 32-битных сегментов

language-type — тип языка программирования. Возможные значения:

CАргументы передаются через стек, справа налево. Стек очищает вызывающая программа.
PASCAL, BASICАргументы передаются через стек, слева направо. Стек очищает вызываемая подпрограмма.
STDCALLАргументы передаются через стек, справа налево. Стек очищает вызываемая подпрограмма.

stack-option — организация стека. Возможные значения для 16 бит: NEARSTACK, FARSTACK. Для 32 бит не используется. Указание NEARSTACK группирует сегменты стека в один физический сегмент (DGROUP) вместе с данными. Регистр сегмента стека SS назначается на тот же адрес что и регистр сегмента данных DS.

FARSTACK не группирует стек с сегментом данных DGROUP; таким образом регистр SS не будет равен регистру DS.

Директива .CODE или CODESEG

.CODE [имя]

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

Директива .DATA или DATASEG

.DATA

Определяет начало инициализированного сегмента данных.

Директива .DATA?

.DATA?

Определяет в модуле начало неинициализированного сегмента данных.

Директива .CONST

.CONST

Определяет начало сегмента данных-констант.

Сегменты .DATA, .DATA?, .CONST помещаются в одну группу с именем DGROUP

Директива .STACK или STACK

.STACK [размер]

Определяет начало сегмента стека, выделяя количество байт, заданное параметром. Если размер не указывается, выделяется 1024 байт.

.model small,stdcall .stack 100h .data str db 'Hello, world!',0 .code start: mov ax,4C00h int 21h end start

Группа директив для резервирования памяти

DB, DW, DD, DF, DP, DQ, DT — Резервирование памяти с размером соответственно 1 байт (DB), 2 байта (DW), 4 байта (DD), 6 байт (DF, DP), 8 байт (DQ) и 10 байт (DT)

[имя] DB выражение[, выражение][, выражение].

Резервирует область памяти, заданного директивой размера, с указанным именем, и инициализирует значением выражения. Выражение может быть числом, строкой символов, специальным символом "?", а Выражением с использованием директивы DUP.

счетчик DUP (выражение[, выражение]. )

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

val1 dw ? val2 db 10 dup(0) val3 dd 4 dub(0FFFFFFFFh) val4 db 'Test string',0 val5 db 2 dup(0Fh), 4 dup(?)

Директива STRUC (STRUCT)

[имя] STRUC

Определяет структуру данных с заданным именем, содержащую поля. В каждом поле для определения его размера используются обычные директивы выделения данных (DB, DW и т.д.). Поля структуры могут быть именованными или нет.

Директива ENDS

[имя] ENDS

Определяет конец структуры.

str1 struc db 10h field1 dw ? field2 db 5 dup(?) str1 ends

Директива UNION

[имя] UNION

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

str1 union db ? db ? db 2 dup(0) str1 ends str2 union dw ? dw ? str2 ends

Группа директив модификации размера указателей

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

BYTE [PTR] — Приводит адресное выражение к размеру в байт

DWORD [PTR] — Приводит адресное выражение к размеру в двойное слово (4 байта)

FAR [PTR] — Приводит к тому, что адресное выражение будет дальним указателем

FWORD [PTR] — Приводит к тому, что адресное выражение будет иметь размер 32-разрядного дальнего указателя

NEAR [PTR] — Приводит к тому, что адресное выражение будет ближним указателем на код

PWORD [PTR] — Приводит к тому, что адресное выражение будет иметь размер 32-разрядного дальнего указателя

QWORD [PTR] — Приводит к тому, что адресное выражение будет иметь размер четверного слова (8 байт)

SHORT — Приводит к тому, что выражение будет указателем на код короткого типа (в границах -128 до +127 байт от текущего адреса программы)

TBYTE [PTR] — Приводит к тому, что адресное выражение будет иметь размер 10 байт

WORD [PTR] — Приводит адресное выражение к размеру в слово (2 байта)

mov es:[di], dword ptr 0

Директивы определения процедур

Директива PROC

имя PROC [язык] [расстояние] [, аргумент] [, аргумент] .

Определяет начало процедуры с указанным именем.

язык — определяет, из какого языка выполняется вызов для доступа к данной процедуре: C, PASCAL, BASIC, STDCALL или NOLANGUAGE. Этим определяются соглашения по именам идентификаторов, порядок аргументов в стеке и то, останутся ли аргументы в стеке при возврате управления из процедуры (см. директиву MODEL). Если язык не задан, то используется язык заданный в директиве MODEL.

расстояние — это значения NEAR или FAR. Оно определяет тип инструкций RET или RETF, которые будут использоваться в процедуре.

аргумент — параметр процедуры в формате имя[:тип], где имя — имя параметра, тип — тип параметра. В качестве параметра можно задать массив в виде имя[N]:тип. Параметры будут доступны в процедуре через положительные смещения относительно регистра BP. Компилятор автоматически преобразует обращения к параметру по имени в соответствующие смещения относительно BP.

Директива ENDP

[имя] ENDP

Определяет окончание процедуры

Директива USES

USES элемент[, элемент].

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

Директива LOCAL

LOCAL элемент[, элемент]. [=идентификатор]

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

Директивы для макроопределений

Директива MACRO

имя MACRO [параметр][, параметр].

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

При использовании макроопределения в программе, в параметры можно передавать строковые выражения, которые будут подставляться в тело макроопределения. Если передаваемая строка содержит пробелы или какие-то символы, вроде запятых, точек, то параметр можно заключить в угловые скобки <. >.

.386 .model small, stdcall .stack 100h .data var dw 0 .code MAddr macro sreg,reg,addr mov reg, seg addr mov sreg,reg mov reg, offset addr endm start: MAddr ds,di,var mov ax, 4C00h int 21h end start

Директива ENDM

ENDM

Определяет окончание макроопределения

Директива REPT

REPT выражение

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

Директива IRP

IRP параметр, аргумент[. аргумент].

Повторяет блок операторов, заданный между директивой IRP и ENDM со строковой подстановкой. Аргументами может быть любой текст: символы, строки, числа и т.д. Для каждого указанного аргумента ассемблирование блока операторов выполняется только один раз. При каждом ассемблировании блока для каждого вхождения "параметра" в операторах подставляется следующий аргумент в списке.

irp reg,ax,bx push reg endm ; после ассемблирования получится следующий код push ax push bx

Другие директивы

Директива COMMENT

COMMENT [символ ограничитель] . [символ ограничитель]

Позволяет задать многострочный комментарий, ограниченный с начала и с конца заданным символом-ограничителем.

comment * Несколько строк комментария *

Директива EQU

имя EQU выражение

Определяет имя как строку, псевдоним или число, содержащие результат вычисления выражения.

var equ 4000h mov ax, var ; то же самое, что mov ax,4000h

Директива END

END метка

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

Директива EVEN

EVEN

Округляет счетчик адреса до следующего четного адреса

Директива SEG

SEG выражение

Возвращается адрес сегмента выражения со ссылкой на память

Директива OFFSET

OFFSET выражение

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

Директива ORG

ORG выражение

Устанавливает счетчик инструкций в текущем сегменте в соответствии с адресом, задаваемым выражением.

Директива RADIX

RADIX основание

Задает основание системы счисления для целочисленных констант (2, 8, 10 или 16)

Директива SIZE

SIZE имя

Возвращает размер элемента данных, выделенного для переменной

.data var dw 4 dup(0) .code mov ax,size var ;ax = 8

Иллюстрированный самоучитель по Assembler

Команда call передает управление подпрограмме, сохранив перед этим в стеке смещение к точке возврата. Команда ret, которой обычно заканчивается подпрограмма, забирает из стека адрес возврата и возвращает управление на команду, следующую за командой call. Команда не воздействует на флаги процессора.

Команда call имеет четыре модификации:

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

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

  • near ptr – прямой ближний вызов;
  • far ptr – прямой дальний вызов;
  • word ptr – косвенный ближний вызов;
  • dword ptr – косвенный дальний вызов.

Команда call прямого ближнего вызова заносит в стек относительный адрес точки возврата в текущем программном сегменте и модифицирует IP так, чтобы в нем содержатся относительный адрес точки перехода в том же программном сегменте. Необходимая для вычисления этого адреса величина смещения от точки возврата до точки перехода содержится в коде команды, который занимает 3 байт (код операции E8h и смещение к точке перехода).

Команда call прямого дальнего вызова заносит в стек два слова – сначала сегментный адрес текущего программного сегмента, а затем (выше, в слово с меньшим адресом) относительный адрес точки возврата в текущем программном сегменте. Далее модифицируются регистры IP и CS: в IP помещается относительный адрес точки перехода в том сегменте, куда осуществляется переход, а в CS – сегментный адрес этого сегмента. Обе эти величины берутся из кода команды, который занимает 5 байтов (код операции 9А1г, относительный адрес вызываемой подпрограммы и ее сегментный адрес).

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

Примеры прямого ближнего вызова:

call near ptr subl; Вызов подпрограммы subl ;из того же сегмента call subl; To же самое

Косвенные ближние вызовы. Пример 1:

mov BX,offset subl; ВХ=адрес подпрограммы call BX; Вызов подпрограммы

Пример 2:

; В полях данных: addr dw subl; Ячейка с адресом подпрограммы ;В программном сегменте: call DS:addr; Вызов подпрограммы call word ptr addr; To же самое

Пример 3:

;В полях данных: addr dw subl; Ячейка с адресом подпрограммы ;В программном сегменте: mov SI,offset addr; SI=адрес ячейки с адресом ;подпрограммы call [SI]; Вызов подпрограммы

Пример 4:

;В полях данных: tbl dw subl; Ячейка с адресом ;подпрограммы 1 dw sub2; Ячейка с адресом ;подпрограммы 2 dw sub3; Ячейка с адресом ;подпрограммы 3 ;В программном сегменте: mov BX,offset tbl; ВХ=адрес таблицы адресов подпрограмм mov SI, 2; SI=смещение к адресу sub2 call [BX] [SI]; Вызов подпрограммы 2

ASSEMBLER не правильно

Этот опкод пытается поместить DWORD (32-битное) значение в байт (8 битов). Это не может быть сделано mov командой (для этого есть другие команды).

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

mov al, bl mov cl, dl mov cx, dx mov ecx, ebx

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

смещение3435363738393A3B3C3D3E3F404142
данные0D0A50324457257A5E72EF7DFFADC7

(Каждый блок представляет байт)

Значение смещения обозначено здесь как байт, но на самом деле это это — 32-разрядное значение. Возьмем для примера 3A, это также — 32-разрядное значение: 0000003Ah. Только, чтобы с экономить пространство, некоторые используют маленькие смещения.

Посмотрите на смещение 3A в таблице выше. Данные на этом смещении — 25, 7A, 5E, 72, EF, и т.д. Чтобы поместить значение со смещения 3A, например, в регистр, вы также используете команду mov:

mov eax, dword ptr [0000003Ah] Означает: поместить значение с размером DWORD (32-бита) из памяти со смещением 3Ah в регистр eax. После выполнения этой команды, eax будет содержать значение 725E7A25h. Возможно вы заметили, что это — инверсия того что находится в памяти: 25 7A 5E 72. Это потому, что значения сохраняются в памяти, используя формат little endian . Это означает, что самый младший байт сохраняется в наиболее значимом байте: порядок байтов задом на перед. Я думаю, что эти примеры покажут это:

dword (32-бит) значение 10203040 шестнадцатиричное сохраняется в памяти как: 40, 30, 20, 10 word (16-бит) значение 4050 шестнадцатиричное сохраняется в памяти как: 50, 40

Вернемся к примеру выше. Вы также можете это делать и с другими размерами:

mov cl, byte ptr [34h] ; cl получит значение 0Dh mov dx, word ptr [3Eh] ; dx получит значение 7DEFh

Вы, наверное, уже поняли, что префикс ptr обозначает, что надо брать из памяти некоторый размер. А префикс перед ptr обозначает размер данных: Byte — 1 байт Word — 2 байта Dword — 4 байта

Иногда размер можно не указывать:

mov eax, [00403045h] Так как eax — 32-разрядный регистр, ассемблер понимает, что ему также требуется 32-разрядное значение, в данном случае из памяти со смещением 403045h.

Можно также непосредственные значения:

Эта команда просто запишет в регистр edx, значение 5006. Скобки, [ и ], используются, для получения значения из памяти (в скобках находится смещение), без скобок, это просто непосредственное значение.

Можно также использовать регистр как ячейку памяти (он должен быть 32-разрядным в 32-разрядных программах):

mov eax, 403045h ; пишет в eax значение 403045 mov cx, [eax] ; помещает в регистр CX значение (размера word) из памяти ; указанной в EAX (403045) В mov cx, [eax], процессор сначала смотрит, какое значение (= ячейке памяти) содержит eax, затем какое значение находится в той ячейке памяти, и помещает это значение (word, 16 бит, потому что приемник, cx, является 16-разрядным регистром) в CX.

Стековые операции — PUSH, POP . Перед тем, как рассказать вам о стековых операциях, я уже объяснял вам, что такое стек. Стек это область в памяти, на которую указывает регистр стека ESP. Стек это место для хранения адресов возврата и временных значений. Есть две команды, для размещения значения в стеке и извлечения его из стека: PUSH и POP.

Команда PUSH размещает значение в стеке, т.е. помещает значение в ячейку памяти, на которую указывает регистр ESP, после этого значение регистра ESP увеличивается на 4. Команда Pop извлекает значение из стека, т.е. извлекает значение из ячейки памяти, на которую указывает регистр ESP, после этого уменьшает значение регистра ESP на 4. Значение, помещенное в стек последним, извлекается первым. При помещении значения в стек, указатель стека уменьшается, а при извлечении — увеличивается. Рассмотрим пример:

(1) mov ecx, 100 (2) mov eax, 200 (3) push ecx ; сохранение ecx (4) push eax (5) xor ecx, eax (6) add ecx, 400 (7) mov edx, ecx (8) pop ebx (9) pop ecx

Анализ: 1: поместить 100 в ecx 2: поместить 200 в eax 3: разместить значение из ecx (=100) в стеке (размещается первым) 4: разместить значение из eax (=200) в стеке (размещается последним) 5/6/7: выполнение операций над ecx, значение в ecx изменяется 8: извлечение значения из стека в ebx: ebx станет 200 (последнее размещение, первое извлечение) 9: извлечение значения из стека в ecx: ecx снова станет 100 (первое размещение, последнее извлечение)

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

Смещение1203120412051206120712081209120A120B
Значение000000000000000000
ESP

(стек здесь заполнен нулями, но в действительности это не так, как здесь). ESP стоит в том месте, на которое он указывает)

mov ax, 4560h push ax

Смещение1203120412051206120712081209120A120B
Значение000060450000000000
ESP

mov cx, FFFFh push cx

Смещение1203120412051206120712081209120A120B
ЗначениеFFFF60450000000000
ESP
Смещение1203120412051206120712081209120A120B
ЗначениеFFFF60450000000000
ESP

edx теперь 4560FFFFh.

Вызов подпрограмм возврат из них — CALL, RET. Команда call передает управление ближней или дальней процедуре с запоминанием в стеке адреса точки возврата. Команда ret возвращает управление из процедуры вызывающей программе, адрес возврата получает из стека. Пример:

..code.. call 0455659 ..more code.. Код с адреса 455659: add eax, 500 mul eax, edx ret

Когда выполняется команда call, процессор передает управление на код с адреса 455659, и выполняет его до команды ret, а затем возвращает управление команде следующей за call. Код который вызывается командой call называется процедурой. Вы можете поместить код, который вы часто используете в процедуру и каждый раз когда он вам нужен вызывать его командой call.

Подробнее: команда call помещает регистр EIP (указатель на следующюю команду, которая должна быть выполнена) в стек, а команда ret извлекает его и передаёт управление этому адресу. Вы также можете определить аргументы, для вызываемой программы (процедуры). Это можно сделать через стек:

push значение_1 push значение_2 call procedure

Внутри процедуры, аргументы могут быть прочитаны из стека и использованы. Локальные переменные, т.е. данные, которые необходимы только внутри процедуры, также могут быть сохранены в стеке. Я не буду подробно рассказывать об этом, потому, что это может быть легко сделано в ассемблерах MASM и TASM. Просто запомните, что вы можете делать процедуры и что они могут использовать параметры.

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

Оцените статью
InternetDoc.ru
Добавить комментарий