Я реализую функцию поворота в 64-битной сборке AT&T, и у меня возникают проблемы с получением правильного вывода в моем коде.
Я пытаюсь реализовать функцию
unsigned long rotate(unsigned long val, ul num, ul dir);
Val
- это значение, которое я хотел повернуть, num
- сколько раз и направление влево или вправо, 0 - вправо, 1 - влево.
Мой код сборки:
.global rotate
rotate: #value = %rdi
#num = %rsi
#direction = %rdx
mov %rsi, %r10 #puts num into register
test %rdx, %rdx
jz right #if rdx is 0 jump to rotate right
#else go to loop right below which rotates left
loop:
test %r10, %r10 #if num is 0 I am done
jz done
rol $1, %rdi #rotate left 1 bit
dec %r10 #decrement my count
jmp loop #jump to top of loop
right: #same logic as left
test %r10, %10
jz done
rol $1, %rdi
dec %r10
jmp loop
done:
mov %rdi, %rax
ret
Мой код C:
#include <stdio.h>
extern unsigned long rotate(unsigned long val, unsigned long num, unsigned long direction);
int main()
{
unsigned long v,n,d;
v = 0xDEADBEEFDEADBEEF;
n = 2;
d = 1;
printf("%x\n ", rotate(v,n,d));
}
Когда я компилирую и запускаю, я получаю значение 0x7AB6FBBF
, тогда как я должен получить 0x7AB6FBBF7AB6FBBF
.
Что-то не так с моими инструкциями, не отправляющими unsigned long
или что-то еще?
Вы знаете, что на большинстве платформ long
32-битный, верно?
@Sneftel: x86-64 System V имеет 64-битный long
, и этот код принимает аргументы в RDI и RSI, так что это определенно ABI. (Где документирован ABI для x86-64 System V?)
Я изменил код c на все беззнаковые длинные длинные, и результаты такие же
@pigsploof: x86 имеет rol %cl, %rdi
и ror %cl, %rdi
, которые вы можете использовать вместо цикла. Просто mov
ваш счет смены в %ecx
.
@PeterCordes - хорошее замечание. Хотя даже если тип достаточно широкий, %x
не будет печатать полные 64 бита.
@Sneftel: да, я все еще смотрел на ужасно чрезмерно сложный asm и предполагал, что они правильно поняли C. Вы и @zch заметили ошибку %x
, когда я отправлял ответ об ошибке asm: P
Питер прав - все современные процессоры имеют баррель-шифтеры, которые могут сдвигать и вращать множество битов за одну инструкцию. Не нужно зацикливаться.
@LeeDanielCrocker: баррель-шифтер позволяет одной инструкции иметь задержку в один цикл. Даже оригинальный 8086 имеет ror %cl, %di
, но коды операций сдвига / поворота imm8 были добавлены в 286 (posix.nl/linuxassembly/nasmdochtml/nasmdoca.html). Только позже эти инструкции стали действительно быстрыми, но на тех старых процессорах сдвиги / повороты стоили примерно 1 цикл на счет. Это быстрее, чем может выполняться любой цикл, особенно неэффективный цикл с двумя ветвями, который не учитывает флаги, установленные dec
.
printf("%x", a)
работает с типом unsigned int
. На unsigned long
вам нужно использовать "%lx"
как строку формата.
У вас есть ошибка в вашем asm: ветвь цикла в right
выполняет jmp loop
вместо jmp right
. По крайней мере, это один ваших ошибок, IDK, если их больше. (обновление: @zch обнаружил ошибку в вашем C, которая объясняет усечение, которое вы упоминаете.)
Это было бы легче обнаружить, если бы вы использовали имя лучше, чем loop
. например left
.
Но вы все равно не должны зацикливаться. x86 имеет rol %cl, %rdi
и ror %cl, %rdi
, которые можно использовать вместо цикла. Просто mov
ваш счет смены в %ecx
, например mov %esi, %ecx
.
Причина, по которой я использую цикл, заключается в том, что когда я пытаюсь использовать rol% ecx,% rsi, я получаю ошибку несоответствия типа операнда для rol. Я должен использовать немедленную компиляцию, и я не знаю почему.
Команды сдвига и вращения @pigsploof могут использовать только% cl или немедленный в качестве операнда счетчика.
@pigsploof: в моем ответе есть причина, по которой rol %cl, %rsi
(felixcloutier.com/x86/RCL:RCR:ROL:ROR.html). Но mov %esi, %ecx
может быть немного более эффективным, чем mov %sil, %cl
или movzbl %sil, %ecx
, поэтому я рекомендовал это для mov
. x86 в любом случае маскирует счетчик сдвигов, поэтому не имеет значения, копируете ли вы или обнуляете старшие биты регистра, или объединяете их со старым значением RCX.
Разве это не должно быть длинным длинным без знака?