Offset ассемблер: Оператор OFFSET | Microsoft Learn

Вопрос для тех, кто знает ассемблер

 
Udjin   (2002-05-06 16:36) [0]

Делаю контрольную работу на ассемблере (чайник я в этом деле). Нужно определить номер версии DOS, программа по условию резидентная. Вот что получилось

CSEG segment
assume cs:CSEG, ds:CSEG
org 100h

start: jmp Myload ;Переход на выпонение загрузочной части
key dw 5555h ;Ключ для определения загружен ли резидент
oldvect dd ? ;здесь запоминается адрес правильного

;обработчика прерывания 09h
highDos db ? ;здесь хранится старшая цифра версии Dos
lowDos db ? ;здесь младшая старшая цифра версии Dos
mes db «MsDos $» ;собщение при выводе номера версии

point db «.$» ;разделение старшей и младшей цифры версии

new proc ;начало резидентной части

push ax ;сохраняем в стеке регистры
push es
push bx
push cx
push dx

in ax,60h ;Чтение порта клавиатуры в AX
cmp al,46 ;Проверка нажатия клавиши «С»
jnz exit ;если нет, то возврат управления
mov ax,0 ;Чтение флагов состояния
mov es,ax ;клавиатуры в регистр AL и
mov al,es:[0417h] ;определение нажаты ли клавиши
and al,00001100b ;Ctrl-Alt,если нет, то
cmp al,00001100b ;возврат управления
jnz exit
in al,61h ;Освобождене
or al,80h ;буфера
out 61h,al ;клавиатуры от
and al,7Fh ;скан кода
out 61h,al

mov cx,6 ;вывод слова MsDos
mov bx,offset mes
m1: mov ah,0eh
mov al,cs:[bx]
int 10h
inc bx
loop m1

mov bx,offset highDos ;вывод старшей цифры версии Dos
mov ah,0eh
mov al,cs:[bx]
int 10h

mov bx,offset point ;вывод разделяющей точки
mov ah,0eh
mov al,cs:[bx]
int 10h

mov bx,offset lowDos ;вывод младшей цифры версии Dos
mov ah,0eh
mov al,cs:[bx]
int 10h

exit:
pop dx ;восстановление регистров
pop cx
pop bx
pop es
pop ax
jmp cs:oldvect;передача управления оригинальному обработчику

new endp

Myload:
;начало загрузочной части

cmp word ptr es:[103h],5555h
je inst

mov ah,30h ;получение версии Dos
int 21h
mov highDos,al
mov lowDos,ah
add highDos,30h ;смещение до соответствующих цифр
add lowDos,30h
mov ah,35h ;Чтение вектора 09h

mov al,09h
int 21h

mov word ptr oldvect,bx ;сохранение вектора прерывания
mov word ptr oldvect+2,es
mov ah,25h ;установка вектора 09h на адрес
mov al,09h
mov dx,offset new ;резидентной процедуры old
int 21h
mov dx,offset Myload ;Адрес конца резидентной части
int 27h ;KEEP

inst:

mov ah,9;Если в памяти, то выведем соответствующее сообщение
mov dx,offset mes2 ;
int 21h

mov ah,4ch ;возврат управления
int 21h ;
mes2 db «already loaded $»;сообщение если резидент в памяти

CSEG ends
end start

Возникли следующие вопросы
1. Запретить повторный запуск резидента не удаётся, хотя как это делается в книжке подсмотрел

2. Как вывести номер версии если он окажется, например, 6.22
Моя программа в этом случае не сработает
3. Как на Ассемблере определить скорость процессора (это к другой контрольной)

P.S. Всё это само-собой под Dos


 
copyr25   (2002-05-06 16:53) [1]

Могу выслать .rar . SysInfo107. Где скачал — уже не помню:)) Версию ОС точно, определяет.
И много всего тоже определяет. Работает и под DOS и в окошке под Windows.
C исходниками. Пишите письма:))


 
Udjin   (2002-05-06 16:58) [2]

Адрес мой [email protected]


 
copyr25   (2002-05-06 17:21

) [3]

Ещё в прошлом веке мне однажды пришлось сделать
программку для управления магнитофоном от «мышиного»
порта через дешифратор, вообщем, там всего комбинации
на двух дырочках доступны 00 01 10 11 :))
. 8086
page ,132
;нажатие F12 подает +5В на 7-ю ножку RS-232
;нажатие F11 отключает этот потенциал
;обрабатывается попытка повторной загрузки
;запуск с ненулевым хвостом выгружает резидент из памяти
CSEG segment
assume cs:cseg,ds:cseg,es:cseg
org 100h
f10 proc
jmp init
old09h dd 0 ; буфер для сохранения старого вектора 9h
old2fh dd 0 ; буфер для сохранения старого вектора 2fh
flag db 0 ; буфер флага «выгружать из памяти»
new2fh proc ; ПОП 2fh — связь с резидентными программами

cmp ah,0c8h ; наша функция прерывания?
jne out2fh ; нет
cmp al,0 ; да, попытка повторной загрузки?
je inss ; да, обработаем запрет
cmp al,1 ; требование на выгрузку?
je unins ; да
jmp short out2fh ; ни то, ни другое — не обрабатываем
inss: mov al,0ffh ; покажем, что программа уже загружена
iret ;
out2fh: jmp cs:old2fh ; переход в след. обработчик 2fh

; выгрузим программу из памяти, предварительно восстановив все
; перехваченные ею векторы (9h и 2fh)
unins: push ds es dx ; сохраним исп.регистры
mov ax,2509h ; установить вектор 09h
lds dx,cs:old09h ; адрес старого вектора
int 21h ; восстановили 09h
mov ax,252fh ; установить вектор 2fh

lds dx,cs:old2fh ; адрес старого вектора
int 21h ; восстановили 2fh
mov es,cs:2ch ; получим из PSP адрес собственного
mov ah,49h ; окружения резидента и выгрузим его
int 21h ;
push cs ; выгрузим теперь саму программу
pop es ;
mov ah,49h ;
int 21h ;
pop dx es ds ;
iret ;
new2fh endp
new09h proc ; ПОП 09fh — прерывание от клавиатуры
push ax ;
in al,60h ;
cmp al,58h ; скен-код F12 ?
je yes ; да
cmp al,57h ; F11?
je yes
pop ax ;
jmp cs:old09h ; отдаемся старому вектору
yes:
push bx
mov bl,al ; сохраняем скен-код в bl
push dx
mov al,0 ; задаем параметры передачи COM1
or al,10011011B
mov ah,0
mov dx,0
int 14h
mov dx,3f8h+4 ; адрес регистра данных COM1
mov al,bl ;
sub al,56h ; 1 в al гасит 7-ю ножку, 2 — зажигает
out dx,al ; передаем
pop dx
mov al,bl
pop bx
in al,61h ; восстановим разрешение прерываний
or al,80h ; от клавиатуры
out 61h,al
and al,7fh
out 61h,al
mov al,20h
out 20h,al
pop ax
iret
new09h endp
endres=$ ;
f10 endp ;
init proc ;
mov cl,es:80h ; смотрим, был ли хвост при запуске
cmp cl,0 ; был?
je conti ; нет
inc flag ; установим флаг для выгрузки
conti: mov ah,0c8h ; вызов связи с резидентом с функц.
c8h
mov al,0 ; смотрим, программа установлена?
int 2fh ;
cmp al,0ffh ;
je inst ; да, наша сигнатура
mov ax,352fh ; вектор 2fh
int 21h ;
mov word ptr cs:old2fh,bx ; сохраним
mov word ptr cs:old2fh+2,es ; в буфере
mov ax,252fh ;
mov dx,offset new2fh ; и укажем для него новую ПОП
int 21h ;
mov ax,3509h ; вектор 9h
int 21h ;
mov word ptr cs:old09h,bx ; сохраним
mov word ptr cs:old09h+2,es ; в буфере
mov ax,2509h ;
mov dx,offset new09h ; и укажем для него новую ПОП
int 21h ;
mov ah,9h ; сообщим, что F10 установлен
mov dx,offset mes ;
int 21h ;
mov ax,3100h ; завершим программу
mov dx,(endres-f10+10fh)/16 ; оставив ее резидентной
int 21h ;
inst: ;
cmp flag,1 ; выгружать?
je uin ; да
mov ah,9h ; сообщим, что
mov dx,offset mes1 ; программа УЖЕ загружена
int 21h ;
mov ax,4c01h ; и выйдем с errorlevel=1
int 21h ;
uin: mov ax,0c801h ; посылаем из вновь запущенной
int 2fh ; F10 резиденту в памяти
mov ah,09h ; признак для выгрузки (1)
mov dx,offset mes2 ; сообщаем, что программа
int 21h ; выгружается
mov ax,4c00h ; и выходим из вновь запущенной
int 21h ;
init endp
mes db «Драйвер управления магнитофоном загружен»,10,13
db «Клавиша F12 включает магнитофон»,10,13
db «Клавиша F11 — выключает»,10,13
db «Для выгрузки драйвера — набрать magnit off»,10,13,10,13
db «(C) copyr25@yahoo. com$»
mes1 db «Драйвер управления магнитофоном УЖЕ загружен$»
mes2 db «Драйвер управления магнитофоном выгружен из памяти$»
CSEG ends
end f10
Может, чем и поможет?
:))


 
copyr25   (2002-05-06 17:52) [4]

[email protected]: Послал Вам .rar. Но там крутой asm:))) Имейте ввиду.


 
copyr25   (2002-05-06 18:53) [5]

mov dx,offset Myload ;Адрес конца резидентной части
int 27h ;KEEP
Вот от этого и не работает.
В 1995 г. была такая книжка «Программируем на языке ассемблера IBM PC»
часть 2. Прикладное программирование. Издательство ЭНТРОП, Москва, 1995.
П. И.Рудаков, К.Г.Финогенов.
стр.115 «Резидентный обработчик прерывания от клавиатуры с подключением
после системного обработчика».
Я до сих пор эту книжку люблю. И 1-ю часть, и 3-ю. За работающие коды.
А Финогенов, ваще, ещё тот Мастер — он и в asm»e, и в web»e, и в Pascal»e многих
российских программеров обучил. Найдите книжку! Не пожалеете:))


 
Udjin   (2002-05-07 08:43) [6]

to copyr25
благодарю за письмо. Попробую разобраться с крутым asm-ом :)). С ограничением запуска разобрался. Сначала переменной key присвоил зачение отличное от 5555h, а строки

cmp word ptr es:[103h],5555h
je inst

заменил на

mov ax,0
mov es,ax
cmp word ptr es:[102h],5555h
je inst
mov es:[102h],5555h

to All С последними 2-мя вопросами я пока не смог разобраться.


Дневники чайника

Дневники чайника

Стек.


Восьмой день

Стек — специально выделенная область памяти для передачи или сохранения данных.

Мы всё время наблюдаем такую форму записи (prax03.com):

00000000: B80300     mov  ax,00003
00000003: CD10       int  010
00000005: B402       mov  ah,002
00000007: 8B167501   mov  dx,[0175]
0000000B: CD10       int  010
0000000D: FEC6       inc  dh
0000000F: 80C203     add  dl,003
00000012: 89167501   mov  [0175],dx
00000016: B409       mov  ah,009
00000018: BA5001     mov  dx,00150
0000001B: CD21       int  021
0000001D: 803E760119 cmp  b,[0176],019
00000022: 75E1       jne  000000005
00000024: B410       mov  ah,010
00000026: CD16       int  016
00000028: CD20       int  020

Я имею в виду, что адреса растут вниз. 0 — выше всех (как первая строка в книге), 28 — нижняя строчка.

Так вот в этой системе отображения стек растёт вверх.

Дно стека находится по самому старшему адресу, а вершина — по самому младшему адресу.

На вершину стека указывает регистр-указатель ESP — это его назначение (Stack Pointer — указатель стека).

Видите, prax03 в строке 12h сохраняет значение регистра DX, а в строке 07 при следующем проходе цикла обратно восстанавливает это значение. В данном примере стек использовать неудобно, но если в программе нужно много раз сохранять разные регистры, то лучше делать это через стек. Для записи в стек есть команда PUSH.

Пример:

push EDX    ; толкнуть в стек (положить в стек)
...
pop  EDX    ; извлечь из стека в EDX (вытолкнуть в EDX)

Другой пример:

push EDX    ; положить в стек
...
pop  EAX    ; извлечь из стека в EAX (в EAX будет то, что было в EDX)

Причём значений в стек можно укладывать очень много.

push EAX    ; положить в стек
push EBX    ; положить в стек
push ECX    ; положить в стек
push EDX    ; положить в стек
push 01234h ; положить в стек
. ..
pop  ESI    ; извлечь из стека (значение 01234h было сверху, значит, оно и выйдет)
pop  EDX    ; извлечь из стека в EDX
pop  ECX    ; извлечь из стека в ECX
pop  EBX    ; извлечь из стека в EBX
pop  EAX    ; извлечь из стека в EAX

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

Матрос! Ну-ка иди сюда… Что тут у тебя в корзинке? О, завтрак! Так, сверху яйца, подержи, бутерброд, бутылка… подушка? А зачем тебе подушка? Ты что, на вахте спать собрался! А ну, отдай сюда корзину. Яйца-то сырые, чёрт тебя дери! И бутерброды измазал, и подушку испортил. Какого шлейфа ты их не сварил? Иди отсюда, сам знаю, что яйца сверху кладут.

Память под стек выделяет Windows.

Теперь осмысленно вернёмся к нашей первой программе под форточки:


Адреса   байты         имена операнды                          комментарии
00401000 6A 00         push  0                                ; /Style = MB_OK|MB_APPLMODAL
00401002 68 00304000   push  00403000                         ; |Title = "It's the first your program for Win32"
00401007 68 21304000   push  00403021                         ; |Text = "Assembler language for Windows is a fable!"
0040100C 6A 00         push  0                                ; |hOwner = NULL
0040100E E8 07000000   call &lt jmp. &user32.MessageBoxA&gt        ; \MessageBoxA
00401013 6A 00         push  0                                ; /ExitCode = 0
00401015 E8 06000000   call &lt jmp.&kernel32.ExitProcess&gt      ; \ExitProcess
0040101A FF25 08204000 jmp   dword ptr ds:[&lt user32.MessageBo&gt ;  user32.MessageBoxA
00401020 FF25 00204000 jmp   dword ptr ds:[&lt kernel32.ExitPro&gt ;  kernel32.ExitProcess

Команда Push кладёт в стек указанное в ней значение. Оно может быть два байта или четыре. В 32-битных программах это будет 32-битное значение, то есть dword (4 байта). Так что для нас теперь одно значение в стеке 4 байта.

Push 0        ; отправляет в стек значение 00000000
Push 00403000 ; отправляет в стек значение 00403000
Push 00403021 ; отправляет в стек значение 00403021
Push 0        ; отправляет в стек значение 00000000

Это, естественно, целое и для «удобства» чтения, младший байт уже справа, старший слева.

Матрос, ты запомнил, что на наших чертежах в целой боевой единице Бинарников байты строятся по старшинству слева направо? Каждый младший — самый левый, каждый старший — самый правый (на фиг такие чертёжи :).
У них там в главном штабе есть ещё запутка — стек. Он в чертежах идёт от дна наверх. Целые (word или dword) в нём укладываются именно так: от дна наверх.

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

00000000
00403021
00403000
00000000
........

Каждая строка здесь — одно значение в стеке. Понятие дно очень наглядно объясняет, как устроен стек.

Давайте удалим из исходника эту строку:

	invoke MessageBox, NULL, addr MsgBoxText, addr MsgBoxCaption, MB_OK

И впишем вместо неё вот такие команды:

  push 0AAAAAAAAh
  push 0BBBBBBBBh
  push 0CCCCCCCCh
  push 0DDDDDDDDh

  pop  EAX
  pop  ECX
  pop  EDX
  pop  EBX

Соберите программу ещё раз. Откройте в Olly.

То, что вы делали в отладчике CodeView клавишей F10, называется по-умному:
пошаговая трассировка исполняемого кода без захода в процедуры. В Olly такой «шаг» выполняет клавиша F8.


Адреса   Байты        Имена Операнды
00401000 68 AAAAAAAA  push AAAAAAAA
00401005 68 BBBBBBBB  push BBBBBBBB
0040100A 68 CCCCCCCC  push CCCCCCCC
0040100F 68 DDDDDDDD  push DDDDDDDD
00401014 58           pop  eax
00401015 59           pop  ecx
00401016 5A           pop  edx
00401017 5B           pop  ebx

Когда вы дойдёте до строки 401014h (F8 четыре раза), стек будет выглядеть вот так:

0012FFB4  DDDDDDDD
0012FFB8  CCCCCCCC
0012FFBC  BBBBBBBB
0012FFC0  AAAAAAAA
...        ...

Посмотрите в отладчике, обязательно! Нижняя правая часть.

Вы должны были понять, что каждое новое значение укладывается сверху. Получается, стек растёт вверх. Получается, регистр ESP с каждым новым значением стека уменьшается. И каждый раз, когда из стека извлекают значение, ESP увеличивается на 4 (или на 2 в 16-битных программах).

А ещё стек нужно выравнивать, иначе программа будет работать неправильно. После того, как ваши данные в стеке отработали или просто больше не нужны, возвращайте вершину в положение, которое она занимала раньше — это и есть выравнивание. Если вы так не сделаете, программа вызовет ошибку.

Стек, пожалуй, самое сложное понятие, которое нужно усвоить для написания программ на Ассемблере. Честно-честно, если вы поймёте это, значит, вы точно сможете освоить всё остальное. Конечно, нужна практика, помню, я три дня потратил на понимание стека. Калашников обманул, сказал, что это просто. Так и было, когда я его примеры использовал, а вот когда свои программки стал писать, стек вызвал наибольшие затруднения.

Три дня — думаю, это немного для самого сложного фокуса.

Чтоб положить что-то в стек, пишите:

push что-то
Команда PUSH
ПроисхождениеОт англ. слова push — толкать
Форматpush операнд
ДействиеТолкает значение в стек
ПримечаниеЕсли нужно толкнуть в стек все 8 регистров общего назначения (EAX, EBX, ECX, EDX, EBP, ESP, ESI, EDI), то лучше использовать одну команду PUSHA/PUSHAD

Чтоб достать число с «верхушки стека» куда-то, пишите:

pop куда-то
Команда POP
ПроисхождениеОт англ. слова pop — извлекать, раскошеливаться
ФорматPOP операнд
ДействиеИзвлекает значение из стека
ПримечаниеЕсли нужно достать из стека все РОН, уложенные командой PUSHA, используйте POPA/POPAD

Следующий пример (prax06.com).

Наберите в Hiew’e эти байты:

B8 CD 20 50-EB FD

Теперь посмотрите на асмовый вид:


Адреса    Байты   Имена   Операнды
00000000: B8CD20  mov     ax,020CD 
00000003: 50      push    ax
00000004: EBFD    jmps    000000003

Давайте проанализируем этот пример.


00000000: B8CD20  mov     ax,020CD 

Эта строка помещает в регистр AX значение 20CD.


00000003: 50      push    ax

Эта строка кладёт в стек word (слово) 20CDh из регистра EAX. Причём в памяти слово вы увидите как положено — CD,20h.


00000004: EBFD    jmps    000000003

А вот эта строка зацикливает выполнение строки с адресом 03. То есть после выполнения строки 03 в строке 04 будет совершаться безусловный переход к строке 03.


Так почему же этот пример не вешает комп в ДОС и не виснет сам в WinXP? Запустите и вы увидите, что прога выполнится за доли секунды.

Можете попробовать разобрать хитрость в CV сами, но это займёт некоторое время :).

Честно скажу, что отладчик типа CodeView не очень подходит для разбора этого пустячка.

Дело в том, что простые DOS-отладчики используют стек программы в своих целях, что сбивает значение регистра ESP между шагами. Здесь бы лучше всего запустить пример под SoftIce’ом. И тогда вы могли бы «ровно» увидеть, как программа 32637 раз запишет в стек два байта CD20h. Последний раз байты будут вписаны вместо команды «jmp 03».

CD20h — это машинный код команды int 20h (вызов прерывания завершения программ, как вы уже знаете).

Стек в com-программе начинается на дне её сегмента (FFFE — последний адрес, кратный двум).

Получается, что стек всё растёт и растёт вверх и ничего его не остановит, кроме завершающего или ошибочного кода.

И вот он заполняет всё свободное место в сегменте, а затем начинает затирать данные (в этом примере их нет). Далее затирается код программы.

Такая ситуация называется ошибкой переполнения стека. Для DOS-программ данная ошибка — одна из самых распространённых.

В Win32-программах механизм выделения стека более развит (ОСь сама занимается этим вопросом, используя средства защищённого режима). Однако ошибки переполнения стеков — всё ещё настоящий вызов для программистов, особенно в сфере безопасности. Вы только что могли наблюдать, как из-за такой ошибки стек начинает выполняться.

Ведь изначально программа просто отправляла данные CD20h в стек. Но затем стек вырос (адрес в ESP уменьшился!) до значения, равного адресу команды JMP 0103 (в CodeView этого не видно!). И вот, после следующего выполнения команды push AX, происходит чудесное превращение данных в исполняемый код.

В CodeView тоже можно проследить самый интересный момент этого примера. Измените регистр указателя стека (ESP). Например, установите в нём значение 200h (чтоб долго не трейсить). После этого — F10 много раз, и прога завершится. Посмотрите ещё и ещё раз. Может быть, вы придумаете, как такой фокус можно использовать в своих целях ;).

Предлагаю вам ещё один пример Win32.

Учтите, что русские буквы должны быть в кодировке ANSI (стандарт для форточек).

prax07.asm:

      .386
      .model flat, stdcall
      option casemap :none   ; case sensitive
;#########################################################################
      include \masm32\include\windows. inc
      include \masm32\include\user32.inc
      include \masm32\include\kernel32.inc
      includelib \masm32\lib\user32.lib
      includelib \masm32\lib\kernel32.lib
;#########################################################################
    .data
	MsgBoxCaption  db "It's my first command line for Win32",0
	MsgBoxText     db "Аргументы командной строки отсутствуют",0
;#########################################################################
    .code

start:
	call   GetCommandLine ; API. Возвращает в EAX адрес командной строки
	mov    ECX,512d       
	add    ECX,EAX        

   unquote:	              ; Метка цикла нахождения закрывающей кавычки
	inc    EAX
	cmp    EAX,ECX
	jz     NO
	cmp    byte ptr[EAX],22h
   jnz unquote

   Arg_search:               ; Метка цикла нахождения аргумента командной строки
	inc    EAX              
	cmp    byte ptr[EAX],0  
	jz     NO               
	cmp    byte ptr[EAX],20h
   jz  Arg_search               



  push   0
  push   offset MsgBoxCaption
  push   EAX
  push   0
  call   MessageBox           ; Вызов API-функции вывода сообщения на экран

  push   0                    ; Пустой параметр для функции выхода
  call   ExitProcess          ; Вызов API-функции выхода

NO:
  push   0                    ; Параметр "MB_OK"
  push   offset MsgBoxCaption ; Параметр "адрес заголовка"
  push   offset MsgBoxText    ; Параметр "адрес текста сообщения"
  push   0                    ; Параметр "родительское окно"
  call   MessageBox           ; Вызов API-функции вывода сообщения на экран

  push   0                    ; Пустой параметр для функции выхода
  call   ExitProcess          ; Вызов API-функции выхода

end start

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

Для того, чтоб пример сделал то, что ему положено, запустите его с каким-нибудь ключом типа:
prax07.exe qwerty

Ну что ж, отладчик Olly — ваш лучший друг. При открытии впишите ключ в поле «Arguments». Не торопитесь, подумайте что к чему.

Здесь используются 3 API-функции:

  • GetCommandLine — возвращает в регистр EAX адрес командной строки программы вместе с путём в кавычках. Например:
    «D:\tut\prax07.exe » qwerty
  • MessageBox — вызывает функцию вывода сообщений на экран.
  • ExitProcess — функция завершения.

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

Bitfry


OFFSET Оператор на языке ассемблера для процессоров x86

Задать вопрос

спросил

Изменено 6 лет, 3 месяца назад

Просмотрено 10 тысяч раз

Меня несколько смущает понятие OFFSET Operator. Согласно книге Кипа Р. Ирвина «Язык ассемблера для процессоров x86», он определяет оператор смещения как оператор, возвращающий расстояние переменной от начала окружающего ее сегмента. Он также говорит, что оператор смещения возвращает смещение метки данных и представляет собой расстояние (в байтах) метки от начала сегмента данных. Что такое смещение? Что он имеет в виду под расстоянием метки от начала сегмента данных? И пришел ли он к такому результату:

Он объявляет три разных типа переменных:

 .data
bVal БАЙТ ?
wVal СЛОВО ?
dVal DWORD ?
dVal2 двойное слово?
 

Если бы значение bVal располагалось по смещению 00404000 (шестнадцатеричное), оператор OFFSET вернул бы следующие значения:

 mov esi, OFFSET bVal ;ESI = 00404000h
mov esi, OFFSET wVal ;ESI = 00404001h
mov esi, OFFSET dVal ;ESI = 00404003h
mov esi, OFFSET dVal2 ;ESI = 00404007h
 

Откуда он взял эти значения? Пожалуйста помоги. Большое спасибо!

  • в сборе
  • x86
  • nasm
  • masm
  • irvine32

1

За исключением 16-битного кода, в обычных ОС виртуальная память является плоской, и все сегменты имеют base=0.

Так что это просто сложный способ сказать, что OFFSET var дает вам адрес var как непосредственный, вместо загрузки с него.

 мов esi, bVal ; загрузить из [bVal], в синтаксисе MASM
 
 mov esi, OFFSET bVal ; esi= адрес bVal
мов эси, [эси] ; нагрузка от [bVal]
 

См. также раздел «Разница сборки между [var] и var», чтобы узнать о различиях между синтаксисом MASM и NASM.

Теория смещения означает: «Смещение — это количество адресов в базовом адресе для перехода к определенному абсолютному адресу». Таким образом, это выглядит как индекс (элемент данных или поле) массива. (элемент данных, блок или кадр). Смещение указывает расстояние между элементом данных и элементом данных. Все элементы элемента имеют одинаковый размер (обычно задается в байтах или словах).

Итак, в вашем случае «данные» — это сегмент памяти, блок или элемент, а переменные в этом сегменте — элементы данных или смещения. Эти смещения — виртуальный адрес пространства несмежных областей физической памяти. Итак, эти числа представляют виртуальный адрес пространства несмежных областей физической памяти.

Зарегистрируйтесь или войдите

Зарегистрироваться через Google

Зарегистрироваться через Facebook

Зарегистрируйтесь, используя адрес электронной почты и пароль

Опубликовать как гость

Электронная почта

Требуется, но не отображается

Опубликовать как гость

Электронная почта

Требуется, но не отображается

arm — Что такое смещения в сборке и как их использовать?

Задать вопрос

спросил

Изменено 4 года, 4 месяца назад

Просмотрено 6к раз

В приведенном ниже коде человек указывает в регистре адрес, я понял. Почему позже он просто не загрузил R3 в R1, почему нужно было сместить его на 0x14?

Как R1 переместил несколько строк кода и что заставило его это сделать? Это действительно сбивает меня с толку, и часы поиска не дали четкого ответа.

Код украден отсюда.

http://mbed.org/forum/mbed/topic/3205/

 my_asm
    ; установить указатель на базовый адрес порта 1
    LDR R1, = 0x2009c020 ; указатель на базу port1
    LDR R3, = 0x00040000 ; установите контакт порта LED 1 на выход
    STR R3,[R1,#0x00] ; установить биты направления (base+0x00)
петля
    LDR R3, = 0x00000000 ; выключить светодиод 1 (все биты равны нулю)
    СТР R3,[R1,#0x14] ; установить выходы напрямую (база + 0x14)
    LDR R3, = 0x00040000 ; включить светодиод 1 (бит 18, также известный как P1.18, равен «единице»)
    СТР R3,[R1,#0x14] ; установить выходы напрямую (база + 0x14)
    петля В; переход к циклу (бесконечный цикл!)
    ВЫРОВНЯТЬ
    КОНЕЦ
 
  • сборка
  • рука

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

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

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

  • 4 байта для этого
  • .
  • 2 байта для этого
  • 4 байта для чего-то другого
  • и т. д.

Современные процессоры используют подобные карты памяти для ввода и вывода. Каждое устройство «сопоставляется» с определенным адресом. Это базовый адрес в вашем коде.

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

2

Причина в том, что это кратчайший (наименьшее количество инструкций) способ добиться желаемого эффекта. Делаются две вещи: (1) настройка порта для вывода и (2) переключение вывода. Поскольку ячейки памяти, в которые необходимо выполнить запись для выполнения этих операций, находятся рядом, для адресации обоих необходимо использовать возможности процессора по базовому/смещению.

Тот же эффект может быть достигнут с помощью следующего кода, но без пользы. Он больше (меньше вероятность того, что он поместится в фиксированной памяти), и цикл не быстрее (процессор все равно должен добавить ноль к новому значению в R1 при выполнении инструкций STR).

 мой_асм
    ; установить указатель на базовый адрес порта 1
    LDR R1, = 0x2009c020 ; указатель на базу port1
    LDR R3, = 0x00040000 ; установите контакт порта LED 1 на выход
    STR R3,[R1,#0x00] ; установить биты направления (base+0x00)
    LDR R1, = 0x2009с034 ; указатель на port1 выводит <-- изменить R1
петля
    LDR R3, = 0x00000000 ; выключить светодиод 1 (все биты равны нулю)
    STR R3,[R1,#0x00] ; установить выходы <-- больше нет смещения
    LDR R3, = 0x00040000 ; включить светодиод 1 (бит 18, также известный как P1.18, равен «единице»)
    STR R3,[R1,#0x00] ; установить выходы <-- больше нет смещения
    петля В; переход к циклу (бесконечный цикл!)
    ВЫРОВНЯТЬ
    КОНЕЦ
 

Регистр 1 загружается с базовым адресом для Порт 1 и третьей инструкцией. ..

 STR R3, [R1, #0x00] ; установить биты направления (база + 0x00)
 

...предполагает, что второй байт данных, адресованных содержимым R1, является битами направления для светодиода на порту 1.

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

 STR R3, [R1, #0x14] ; установить выходы напрямую (база + 0x14)
 

Что вы можете узнать из приведенного выше кода, так это то, что указатели поворота имеют смещение 1, а выходные данные находятся в байте 15 (, я думаю, ) в данных, относящихся к порту 1. Кроме этого, у нас нет никакой информации. .

Однако, если вам нужно получить доступ к каким-либо другим данным, относящимся к порту 1, позже в процессе, и вы обновили базовый регистр, указав на смещение 0x14 после начала записи для порта 1, вам придется загрузить адрес для этого или перезагрузите базу и получите смещение, особенно если оно было где-то между базой данных для порта 1 и смещением 14.

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

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