Assembler процедура, возврат результата.

Вопросы программирования на Free Pascal, использования компилятора и утилит.

Модератор: Модераторы

Ответить
Azazaz
новенький
Сообщения: 41
Зарегистрирован: 21.04.2015 19:00:03

Assembler процедура, возврат результата.

Сообщение Azazaz »

Есть вот такой код:

Код: Выделить всё

{$mode objfpc}{$H+}
{$inline on}
{$ASMMODE intel}

procedure DM(W: Word; dwr: byte; var D, M: byte); assembler;
asm
 mov ax, W;
 div dwr;
 
 mov byte [M], ah;
 mov byte [D], al;
end;

var
 {div, mod}
 D, M: byte;
begin
 DM(20, 3, d, m);
 Writeln('D = ', D, ' M = ', M);
 readln;
end.

где процедура должна вернуть результат(div и mod операций) через 2 переменные, но возвращает только одну переменную.
В чем проблема? Каким образом FPC возвращает результаты assembler процедур?
Аватара пользователя
Дож
энтузиаст
Сообщения: 900
Зарегистрирован: 12.10.2008 16:14:47

Сообщение Дож »

Проблема в том, что M (т.е. указатель на переменную) передаётся через стек, а не в регистре:

Код: Выделить всё

.section .text.n_p$program_$$_dm$word$byte$byte$byte,"x"
   .balign 16,0x90
.globl   P$PROGRAM_$$_DM$WORD$BYTE$BYTE$BYTE
P$PROGRAM_$$_DM$WORD$BYTE$BYTE$BYTE:
# [da.pas]
# [6] asm
   pushl   %ebp
   movl   %esp,%ebp
# Var W located in register ax
# Var dwr located in register dl
# Var D located in register ecx
# Var M located at ebp+8, size=OS_32
# [7] mov ax, W;
   movw   %ax,%ax
# [8] div dwr;
   divb   %dl
# [12] mov byte [M], ah;
   movb   %ah,8(%ebp)
# [13] mov byte [D], al;
   movb   %al,(%ecx)
# [14] end;
   leave
   ret   $4

Одним mov'ом не обойтись, нужно два, вот так работает:

Код: Выделить всё

procedure DM(W: Word; dwr: byte; var D, M: byte); assembler;
asm
mov ax, W;
div dwr;

mov ebx, [M]
mov byte [ebx], ah;
mov byte [D], al;
end;

Код: Выделить всё

C:\data\temp>fpc -al da.pas && da
Free Pascal Compiler version 3.0.0rc1 [2015/08/10] for i386
Copyright (c) 1993-2015 by Florian Klaempfl and others
Note: Switching assembler to default source writing assembler
Target OS: Win32 for i386
Compiling da.pas
Assembling program
Linking da.exe
21 lines compiled, 0.1 sec, 25504 bytes code, 1236 bytes data
1 note(s) issued
D = 6 M = 2


Для успешного написания ассемблерных функций следует либо изучить подробно как действует дефолтная конвенция вызова в паскале, либо использовать какую-то более-менее фиксированную (stdcall, например).
Azazaz
новенький
Сообщения: 41
Зарегистрирован: 21.04.2015 19:00:03

Сообщение Azazaz »

У меня код с 2 mov работает прежним образом. По умолчанию компилятор использует регистры для передачи аргументов, по видимому M переменная попадает в неизвестный регистр из-за чего не возвращается результат. Вопрос в том в какие регистры распихиваются переменные?
Аватара пользователя
Дож
энтузиаст
Сообщения: 900
Зарегистрирован: 12.10.2008 16:14:47

Сообщение Дож »

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

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

по видимому M переменная попадает в неизвестный регистр из-за чего не возвращается результат.

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

Добавлено спустя 3 минуты 14 секунд:
У меня код с 2 mov работает прежним образом.

Значит, нужно смотреть дамп ассемблерного кода (ключик -al), где именно были размещены переменные.
Azazaz
новенький
Сообщения: 41
Зарегистрирован: 21.04.2015 19:00:03

Сообщение Azazaz »

Все, заработало. Интересно что будет если скомпилировать вот так:

Код: Выделить всё

procedure DM(W: Word; dwr: byte; var D, M: byte); assembler; register; nostackframe;
asm
 div dwr;
 mov ebx, M;
 mov byte [ebx], ah;
 mov byte [D], al;
end;

M не будет передано?

Добавлено спустя 7 минут 15 секунд:
Скомпилировал, M по прежнему в ebp+8 даже с ключом nostackframe.
Аватара пользователя
Дож
энтузиаст
Сообщения: 900
Зарегистрирован: 12.10.2008 16:14:47

Сообщение Дож »

Так а модификатор nostackframe и не отключает стек, он отключает обвязку с сохранением топа в стек
http://www.freepascal.org/docs-html/ref/refsu79.html

Добавлено спустя 1 минуту 45 секунд:
По всей видимости, asm..end блок не учитывает nostackframe и алиас M неправильный, поэтому обращаться к переменной нужно ручками

Код: Выделить всё

procedure DM(W: Word; dwr: byte; var D, M: byte); assembler; register; nostackframe;
asm
div dwr;
mov ebx, [esp+4];
mov byte [ebx], ah;
mov byte [D], al;
end;
Ответить