Доступ к заданным аргументам после вызова процедуры - PullRequest
0 голосов
/ 09 марта 2020

Я изучаю сборку x86, используя FreeDOS и nasm. У меня есть небольшая тестовая программа, все, что она делает, это выводит на экран А и завершает работу.

Она прекрасно работает, если я не использую процедуру записи.

Но то, что, кажется, происходит это когда я кладу sh A в стек, затем вызываю Write, он помещает следующий IP в стек, и когда я вынимаю A в процедуре, я получаю IP, а не значение, которое я нажал.

I Я уверен, что это что-то простое, но я не вижу проблемы.

segment data                    ; start of data segment

segment code                    ; start of code segment
..start:

label1:
top:
        push 'A'
        call Write
        mov ah, 4ch
        mov al, 0
        int 21h

Write:
        pop dx
        mov ah, 02h
        int 21h
        ret
end:
        mov ah, 4ch             ;exit
        mov al, 0               ;exit code 0
        int 21h                 ;call intr

segment stack class=stack       ; start of stack segment
        resb 512

1 Ответ

3 голосов
/ 09 марта 2020

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

В 32-битном коде вы теперь можете просто использовать указатель стека напрямую для доступа к ранее переданному значению (что-то вроде [esp+4] или [esp+2] в 16-битном режиме), но это невозможно при чисто 16-битной сборке только с 16-битными режимами адресации и ограниченным выбором регистров (не включая [sp]).

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

Write:
    push bp            ; Save previous value of bp so it won't get lost
    mov bp, sp         ; Set bp ("base pointer") to current stack pointer position

    mov dx, [bp+4]     ; Get argument from stack
    mov ah, 02h
    int 21h

    mov sp, bp         ; Restore stack pointer
    pop bp             ; Restore value of base pointer

    ret 2              ; Indicate how many bytes should be popped from stack after return

Вместо pop dx мы используем mov dx, [bp+4] здесь. На этом этапе [bp] будет предыдущим значением bp (так как оно было в последний раз перед тем, как bp было присвоено sp), [bp+2] будет адресом возврата, а [bp+4] вашим первым аргументом.

(Помните, что стек растет вниз, поэтому вам нужно +4, а не -4 здесь.)

Кроме того, когда вы возвращаетесь, вы должны быть уверены, что аргумент удаляется из стека. Вы можете либо разрешить вызывающей стороне очистку, либо использовать ret с количеством байтов, которое нужно удалить в качестве аргумента. Это дополнительный sp += n после получения обратного адреса. В вашем случае ret 2 будет реализовывать вызовы вызовов для этой функции.

...