2016-03-10 2 views
-1

Я работаю с xv6, который реализует оригинальную UNIX на компьютерах x86. Я написал очень простой ассемблерные в программе C:x86 add и addl операнды добавляются неправильно?

register int ecx asm ("%ecx"); 
printf(1, "%d\n", ecx); 
__asm__("movl 16(%esp), %ecx\t\n"); 
printf(1, "%d\n", ecx); 
__asm__("add $0, %ecx\t\n"); 
printf(1, "%d\n", ecx); 
__asm__("movl %ecx, 16(%esp)\t\n"); 

Я обычно получаю значение, как 434, напечатанной вторым оператором печати. Однако после команды добавления он печатает 2. Если вместо этого я использую команду addl, он также печатает 2. Я использую последнюю стабильную версию xv6. Поэтому я действительно не подозреваю, что это проблема. Есть ли другой способ добавить два числа в встроенную сборку?

По сути мне нужно прирастить 16 (% ЭСП) на 4.


Отредактированный код:

__asm__("addl $8, 16(%esp)\t\n");

+0

Помните, что 'printf' скорее всего изменяет регистр' esp'. Помните, что в C очистка стека выполняется вызывающим. Вы должны пройти через эту программу с помощью отладчика. –

+0

Gdb не очень хорошо работает с xv6, поскольку gdb может только отлаживать ядро. Но это программа уровня пользователя. Таким образом, я не могу отличить от операторов печати отладки. – 1729

+2

@MichaelWalz, который специально означает 'printf' делает ** не ** изменять' esp'. Но это может очень хорошо изменить 'ecx'. – Jester

ответ

1

1) В вашем примере вы не Инкрементирование ECX на 4 , а приразите его к 0.

__asm__("addl $4, %ecx"); 

2) Вы должны иметь возможность подключать мульти iple команды в один ассемблерный вызов

__asm__("movl 16(%esp), %ecx\n\t" 
     "addl $4, %ecx\n\t" 
     "movl %ecx, 16(%esp)"); 

3) Регистр ключевых слов является намек, и компилятор может принять решение поместить переменную, где когда-либо он хочет еще. Также чтение документации на странице GCC предупреждает о том, как некоторые функции могут сжимать различные регистры. Функция printf(), являющаяся функцией C, может очень хорошо использовать регистр ecx без сохранения ее значения. Он мог бы сохранить его, но это может не быть; компилятор может использовать этот регистр для всех видов оптимизации внутри этого вызова. Это регистр общего назначения на 80x86, и они часто используются для различных значений передачи и возврата параметров все время.

Непроверенные поправки:

int reg; // By leaving this out, we give GCC the ability to pick the best available register. 

/* 
* volatile indicates to GCC that this inline assembly might do odd side 
* effects and should disable any optimizations around it. 
*/ 
asm volatile ("movl 16(%esp), %0\n\t" 
       "addl $4, %0\n\t" 
       "movl %0, 16(%esp)" 
       : "r" (reg)); // The "r" indicates we want to use a register 

printf("Result: %d\n", reg); 

GCC manage page имеет больше деталей.

+0

'asm ("% ecx ");' не является подсказкой, gcc гарантированно будет использовать ecx **, если ** вы ссылаетесь на этот операнд в ограничении. См. [Локальные регистровые переменные] (https://gcc.gnu.org/onlinedocs/gcc/Local-Register-Variables.html). Чтобы добавить 4, вы можете просто использовать 'addl $ 4, 16 (% esp)' нет необходимости в дополнительном регистре и перемещать вещи вокруг. – Jester

+0

Эта же страница сильно обескураживает использование ключевого слова register. Даже в контексте использования расширенного вызова ASM он все еще говорит, что оптимизатор может скрыть его или что-то еще его использовать. Поэтому объявление этой переменной как ecx не означает, что компилятор не использует ecx else в функции. (Например, оптимизация передачи параметров для вызова printf()) – Kelmar

+0

Соглашения о вызовах Sys-V говорят, что ECX является регистром нуля. Не прилагается никаких усилий, чтобы сохранить его между вызовами функций в коде OP. –

Смежные вопросы