2015-11-13 9 views
-1

Я пытаюсь перевести следующий код C для сборки:Ассамблея перевод

void write (int bitpos, unsigned short sample) 
{ 
    int pos = bitpos/16; 
    int posA = bitpos - pos * 16; 
    unsigned short write1 = sample >> posA; 
} 

Я получаю сообщение об ошибке в операции сдвига. Я посмотрел несколько примеров в книге, но я не понимаю, что случилось. Я думаю, это может быть потому, что количество, которое я хочу сдвинуть, является переменной. Мне было интересно, какой правильный способ достичь этого?

Вот что я пробовал: говорит

//int pos = bitpos/16; 
mov eax, 0 
mov eax, [bitpos] // eax= bitpos 
cdq 
mov ecx, 16   
idiv ecx  //ecx = pos 

//int posA = bitpos - pos * 16; 
mov ebx, ecx //ebx = pos 
imul ebx, 16 // ebx = pos*16 
sub eax, ebx // eax = posA 

//unsigned short write1 = sample >> posA; 
mov bx, [sample] 
shr bx, eax // This is the part that is not working. 

Ошибка: Неправильный тип операнда. Код ошибки: C2415

+0

Попробуйте использовать 'sar' вместо' shr'. Это сохраняет знак. – drum

+1

Хорошая ссылка на ассемблер должна указывать на то, что единственным регистром, указывающим изменение переменной, является [в CL] (http://x86.renejeschke.de/html/file_module_x86_id_285.html). – usr2564301

+0

Вы можете заменить 'битпот/16' на« битпоты >> 4' и 'pos * 16' с' pos << 4'. –

ответ

2

Функция write() не имеет возвращаемого значения и не имеет побочных эффектов (не записывается ни в одну глобальную переменную, никаких системных вызовов, а устанавливается только некоторые локали, которые отбрасываются при возврате функции). Вы можете и должны оптимизировать его до пустой функции, just like gcc does.

global write 
write: 
    ret 

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

gcc -Og (оптимизация для отладки) делает приятным читаемым asm, который не сохраняет/перезагружается из памяти все время. gcc -m32 -Og -fverbose-asm -masm=intel emits:

# see the godbolt link for colour-coded mapping of source lines to asm lines 
write(int, unsigned short): 
    mov edx, DWORD PTR [esp+4] # bitpos, bitpos 
    lea eax, [edx+15] # tmp98, 
    test edx, edx # bitpos 
    cmovns eax, edx # tmp98,, bitpos, bitpos 
    sar eax, 4 # tmp99, 
    neg eax # tmp101 
    sal eax, 4 # tmp102, 
    mov ecx, eax # tmp102, tmp102 
    add ecx, edx # posA, bitpos 
    movzx eax, WORD PTR [esp+8] # D.2591, sample 
    sar eax, cl # D.2591, posA 
    ret 

Обратите внимание, как он загружает параметры функции из стека, потому что они параметры функции, а не глобал. (Ваши ссылки на код [bitpos], глобальная, а не первая позиция в стеке после обратного адреса, [esp+4].) 64-битная ABI передает аргументы в регистры, поэтому вы получаете более чистый код.

Код условного перемещения существует потому, что семантика C для целочисленного деления отрицательного числа дает разные результаты из арифметического сдвига вправо (они вращаются по-разному). Поскольку idiv очень дорогой по сравнению со сменой, все равно стоит использовать дополнительные инструкции для настройки сдвига. Если bitpos был неподписанным, он мог бы просто использовать shr.

С полной оптимизацией, gcc находит более эффективный способ делать вещи и складывает часть арифметики вместе. (т. е. разделить на 16, а затем умножить на 16, округлить до ближайшего кратного 16, реализован с одним and, чтобы скрыть эти биты.)

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