Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
ASMhelp_Kiselev.doc
Скачиваний:
2
Добавлен:
01.07.2025
Размер:
68 Кб
Скачать
☆

2.2 Вызовы ядра

eax

ebx

ecx

edx

Выход из программы

1

0

-

-

Ввод символов с клавиатуры

3

0

метка

(.bss)

количество памяти

Вывод символов на экран

4

1

метка

(.data или .bss)

количество памяти

После помещения соответствующих значений в регистры вызывается ядро:

int 0x80

Нельзя конечно точно утверждать, но скорее всего, размер одного элемента массива для выделенной памяти должен быть равен одному байту. Т.е. Надо использовать только db для секции .data, или только resb для секции .bss

Здесь и далее я буду называть вызов ядра по значению числа помещаемого в eax. То есть «первый вызов ядра», это завершение программы:

mov eax,1

mov ebx,0

int 0x80

«Третий» и «Четвёртый» вызовы это соответственно ввод с клавиатуры и вывод на экран.

2.3 Организация подпрограмм

Первое, что необходимо прояснить: главная программа начинается с метки _start и заканчивается первым вызовом ядра. Как-то так:

section .text

_start:

…

;код главной программы

…

mov eax,1

mov ebx,0

int 0x80

Любая подпрограмма же всегда начинается после первого вызова ядра, и вызывается из главной программы.

Затем — вызов подпрограммы производиться с помощью команды call <имя подпрограммы>.

Чтобы вернутся из подпрограммы в конце её кода пишется

команда ret

Скомпоновав это будет как-то так:

section .text

_start:

…

;код главной программы

…

call subprog1

…

;ещё код…

…

mov eax,1

mov ebx,0

int 0x80

subprog1:

…

;код подпрограммы subprog1

…

ret

2.4 Метки в подпрограммах

Любая метка в подпрограмме должна иметь имя начинающееся с точки! Чтобы «извне» никакая другая подпрограмма не могла перейти на середину этой и застать её «выходящей из душа».

2.5 Сохранение регистров

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

1) Каждый по отдельности.

Если в программе используется всего-то два или три регистра глупо сохранять сразу все, можно в начале, в определенном порядке сохранить эти регистры, а после, в обратном порядке вытащить их. Если мы используем, например, eax, ecx, esi, это будет выглядеть вот так:

push eax

push ecx

push esi

…

;некий код

…

pop esi

pop ecx

pop eax

2) Все сразу.

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

pushad

…

;некий код

…

popad

2.6 Передача аргументов подпрограмме

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

1) Через регистры

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

Пример:

Скажем для подпрограммы sp1 в eax и ebx надо положить два числа 23 и 42

section .text

_start:

…

;код главной программы

…

mov eax, 23

mov ebx, 42

call sp1

…

;ещё код…

…

mov eax,1

mov ebx,0

int 0x80

sp1:

…

;код подпрограммы sp1

…

ret

Здесь жирным выделены куски кода на которые стоит обратить внимание.

2) Через стек

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

Как работает стек?

В секции .stack находиться память на которую указывает регистр esp, при операции push <операнд1> в четыре байта кладётся этот операнд1 и значение регистра esp уменьшается на четыре. При выполнении pop <операнд1> в операнд1 загружается значение из памяти на которую указывает esp и он же увеличивается на 4.

Как работает call?

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

В главной программе:

Аргументы помещаются в стек в обратном порядке и вызывается подпрограмма, после вызова, к регистру esp прибавляется 4*количество_аргументов.

Теперь о подпрограмме:

call помещает в стек eip отдаляя нас от аргументов на 4 байта, затем мы сами сохраняем в стеке некий регистр (обычно ebp), в который потом заносим значение esp => мы отдалились от аргументов на 8 байт. Также каждый аргумент в стеке весит 4 байта => формула расчёта смещения F(n) = (n-1)*4+8. Как пользоваться? Например, нам нужно добраться до 5-ого аргумента в массиве аргументов 4, 8, 15, 16, 23, 42, которые были помещены в стек «правильно»:

push 42

push 23

push 16

push 15

push 8

push 4

Тогда смещение F(5)=(5-1)*4+8=24, то есть

dword [ebp+24] = 23. Почему мы кладём esp именно в ebp, и затем используем только его? Потому что здесь так заведе... Не знаю. Просто так.

Пример кода:

Скажем для подпрограммы sp2 надо положить два числа 23 и 42

section .text

_start:

…

;код главной программы

…

push 42

push 23

call sp2

add esp, 8

…

;ещё код…

…

mov eax,1

mov ebx,0

int 0x80

sp2:

push ebp

mov ebp, esp

…

;код подпрограммы sp2

…

pop ebp

ret

Опять же выделен жирным лишь тот код, который достоин внимания.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]