Offset assembler что это
Перейти к содержимому

Offset assembler что это

  • автор:

Оператор OFFSET

Возвращает смещение в соответствующий сегмент выражения.

Синтаксис

См. также

Обратная связь

Были ли сведения на этой странице полезными?

Обратная связь

Coming soon: Throughout 2024 we will be phasing out GitHub Issues as the feedback mechanism for content and replacing it with a new feedback system. For more information see: https://aka.ms/ContentUserFeedback.

Отправить и просмотреть отзыв по

подскажите по ассемблер простыми словами. Что делает команда Offset? и почему символьная константа внутри продолжение.

offset означает что берётся адрес переменной, а не её значение. Например, есть переменная:

Предположим, что она размещена по адресу 20FFFh. Тогда:

mov eax, X ;eax = 7
mov eax, offset X ;eax = 20FFFh

Остальные ответы
dx это параметр для прерывания 21h, в регистре записано смещение начала строки, все просто.

+ Почему метка позже и внутри — у Вас судя по виду .com файл, где сегмент данных и сегмент кода (впрочем как и сегмент стека) — один и тот-же и точка входа у него всегда по адресу со смещением 100h

=> Если данные объявлять до начала кода — нужен будет дополнительный прыжок (jmp) чтобы эти данные обогнуть

Assembler

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

mov АН,40h ;Число 40h загружается в АН
mov AL,’*’ ;Код ASCII символа «*’ загружается в AL
int 21h ;Команда прерывания с аргументом 21h
limit = 528 ;Число 528 получает обозначение limit
mov CX,limit ;Число, обозначенное limit, загружается в СХ

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

; Сегмент данных
mes db ‘Урок 1’ ;Строка символов
;Сегмент команд
mov DX,offset mes ;Адрес строки засылается в DX

В приведенном примере относительный адрес строки mes, т.е. расстояние в байтах первого байта этой строки от начала сегмента, в котором она находится, заносится в регистр DX.

Offset assembler что это

Для обращения к данным по определенному адресу мы можем использовать один из способов адресации:

    Обращение по адресу в базовом регистре.

LDR Xt, [Xn]
LDR Xt, [Xn, ]
LDR Xt, [Xn, ]!

LDR Xt, [Xn],

Рассмотрим сначала адресацию со смещением.

Адресация со смещением

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

    Положительные и отрицательные константы — непосредственные операнды

LDR Xt, [Xn, #imm]
LDR Xt, [Xn, Xm] LDR Xt, [Xn, Wm]
LDR Xt, [Xn, Xm, #imm] LDR Xt, [Xn, Wm, ]

Смещение — непосредственный операнд

Если в качестве смещения применяется непосредственный операнд, то он должен быть размером не более 12 бит. Для инструкций LDR/STR , если первый операнд — 32-разрядный регистр W0-W30, то смещение должно быть кратно 4, а если первый операнд — 64-разрядный регистр Х0-Х30, то смещение должно быть кратно 8. При этом смещение долдно быть в диапазоне 0-4095.

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

.global _start _start: ldr x1, =nums // загружаем в X1 адрес nums ldr x0, [x1, 16] // смещение равно 16 байтам, Х0=13 mov x8, #93 // устанавливаем функцию Linux для выхода из программы svc 0 // Вызываем функцию Linux .data nums: .quad 11, 12, 13, 14, 15, 16, 17, 18

Здесь в регистр Х1 загружен адрес массива nums, который состоит их 8-байтных чисел. Далее обращаемся по адресу на 16 байт вперед относительно адреса в регистре Х1:

ldr x0, [x1, 16] // смещение равно 16 байтам, Х0=13

Регистр Х1 хранит адрес массива nums, точнее самого первого его элемента (число 11). Прибавив к этому адресу 16 байт, получим адрес второго элемента в массиве nums — числа 13. И именно это число будет загружено в регистр Х0.

Смещения могут представлять вычисляемые выражения. Например:

.global _start _start: ldr x1, =nums // загружаем в X1 адрес nums ldr x0, [x1, 3*8] // смещение равно 24 байтам, Х0=14 mov x8, #93 // устанавливаем функцию Linux для выхода из программы svc 0 // Вызываем функцию Linux .data nums: .quad 11, 12, 13, 14, 15, 16, 17, 18

В данном случае обращаемся к 4-му элементу (элементу с индексом 3 или nums[3]). Поскольку каждый элемент занимает 8 байт, то смещение представляет результат выражения 3*8 (то есть 24 байта). Использование таких выражений может быть проще в плане наглядности, так как мы видим и индекс элемента — 3 и размер элемента — 8.

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

.global _start _start: ldr x1, =nums // загружаем в X1 адрес nums ldr x0, [x1, -8] // смещение равно 8 байтам, Х0=125 mov x8, #93 // устанавливаем функцию Linux для выхода из программы svc 0 // Вызываем функцию Linux .data number: .quad 125 nums: .quad 11, 12, 13, 14, 15, 16, 17, 18

Здесь смещение равно -8, то есть относительно адреса переменной nums смещаемся назад на 8 байт. А по этому адресу располагается 8-байтная переменная number, равная 125. Поэтому в регистре Х0 будет загружено число 125.

LDUR

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

Disassembly of section .text: 0000000000000000 : 0: 58000081 ldr x1, 10 4: f85f8020 ldur x0, [x1, #-8] 8: d2800ba8 mov x8, #0x5d // #93 c: d4000001 svc #0x0

Здесь мы видим, что вместо второй инструкции LDR используется инструкция LDUR . Эта инструкция во многоим аналогична LDR , только может принимать смещения, которые не являются кратными 4 или 8. Однако в этом случае смещения должны быть не более 9 бит и находиться в диапазоне от -256 до 255. Существует набор подобных инструкций, которые принимают смещения, не кратные 4 и 8:

  • LDUR : загружает данные размером целевого регистра (аналог LDR )
  • LDURB : загружает 1 байт (аналог LDRB )
  • LDURSB : загружает 1 байт со знаком (аналог LDRS )
  • LDURH : загружает полслова — 2 байта (аналог LDRH )
  • LDURSH : загружает полслова со знаком (аналог LDRSH )
  • LDURW : загружает слово — 4 байта (аналог LDRW )
  • LDURSW : загружает слово со знаком (аналог LDRSW )
  • STUR : сохраняет данные размером целевого регистра (аналог STR )
  • STURB : сохраняет 1 байт (аналог STRB )
  • STURH : сохраняет полслова (аналог STRH )

Все эти инструкции принимают смещение в диапазоне от -256 до 255. И если инструкциям LDR/STR передается или отрицательное смещение или смещение не кратное 4 или 8, то дизассемблер транслирует их в соответствующие инструкции LDUR/STUR

Регистр в качестве смещения

В качестве смещения также может применяться значение регистра:

LDR Wt, [Xn, Xm] LDR Xt, [Xn, Xm]

В таком случае регистр часто выступает в качетсве индекса для обращения к каким-то частям данным. Например:

.global _start _start: mov x2, 2 // Х2-регистр-индекс - обращаемся к nums[2] ldr x1, =nums // загружаем в X1 адрес nums ldrb w0, [x1, x2] // эквивалентно ldr x0, [x1 + x2] mov x8, #93 // устанавливаем функцию Linux для выхода из программы svc 0 // Вызываем функцию Linux .data nums: .byte 11, 12, 13, 14, 15, 16, 17, 18

Здесь определен массив однобайтовых чисел nums. Допустим, мы хотим обратиться к третьему его элемента (nums[2]), то есть к числу 13 в данном случае. Каждое число в массиве nums находится друг от друга на расстоянии 1 байта (так как числа однобайтовые). Соответственно чтобы получить адрес тертьего элемента, надо к адресу начала массива nums (адресу его первого элемента) прибавить 2 байта.

В качестве регистра-индекса выступает регистр X2 — в него загружаем индекс искомого элемента:

mov x2, 2 // Х2-регистр-индекс - обращаемся к nums[2] ldr x1, =nums // загружаем в X1 адрес nums

В регистр Х1 загружаем адрес массива nums, то есть регистр Х1 будет базовым регистром. Затем используем индекс в регистре Х2 и базовый адрес в регистре Х1, с помощью инструкции ldrb (нам нужно получить 1 байт) обращаемся к элементу по нужному адресу:

ldrb w0, [x1, x2] // эквивалентно ldr x0, [x1 + x2]

Регистр-смещение со сдвигом

К регистру-смещению можно применять стандартные операции сдвига — LSL/LSR :

LDR Wt, [Xn, Xm, LSL #2] // адрес = Xn + (Xm*4) LDR Xt, [Xn, Xm, LSL #3] // адрес = Xn + (Xm*8)

Если регистру-смещение представляет 32-разрядный регистр W0-W30, то также к нему можно применять операции расширения знаком и нулем UXTW/SXTW/SXTX :

LDR Wt|Xt, [Xn, Wm, UXTW ] LDR Wt|Xt, [Xn, Wm, SXTW ] LDR Wt|Xt, [Xn, Wm, SXTX ]

В качестве примера рассмотрим следующую программу:

.global _start _start: mov x2, 3 // Х2-регистр-индекс - обращаемся к nums[3] ldr x1, =nums // загружаем в X1 адрес nums ldr x0, [x1, x2, lsl 3] // эквивалентно ldr x0, [x1, x2*8] mov x8, #93 // устанавливаем функцию Linux для выхода из программы svc 0 // Вызываем функцию Linux .data nums: .quad 11, 12, 13, 14, 15, 16, 17, 18

В секции данных определен массив nums, который содержит числа размером 8 байт. То есть каждое последующее число находится от предыдущего на расстоянии 8 байт. Допустим, мы хотим получить четвертое число в этом массиве (в данном случае число 14). Оно будет находится от начала массива на расстоянии 3 * 8 = 24 байта. То есть если к адресу массива nums прибавить 24 байта, то мы получим адрес nums[3], то есть четвертого числа.

Для получения нужного нам числа сначала помещаем в регистр Х2 индекс числа — 3:

mov x2, 3 // Х2-регистр-индекс - обращаемся к nums[3] ldr x1, =nums // загружаем в X1 адрес nums

То есть регистр Х2 будет выполнять роль смещения. Затем загружаем в регистр Х1 адрес массива nums. Таким образом, Х1 будет выполнять роль базового регистра. Далее, используя сдвиг смещения, получаем нужный нам элемент:

ldr x0, [x1, x2, lsl 3]

Сдвиг влево на 3 эквивалентен умножению на 8. То есть финальный адрес будет равен x1 + x2*8 .

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

Ваш адрес email не будет опубликован. Обязательные поля помечены *