Асемблерная вставка в С++, почему положительное число вместо отрицательного
Помогите пожалуйста, не могу понять в чём проблема
#include <iostream>
#include <clocale>
short CalcExp(short a, short b, short p)
{
short Ans; // Переменная для присвоения результата
_asm // Начало ассемблеровской вставки
{
mov dx, a
mov bx, b
mov cx, p
xor ax, ax// очищаем регистр ax
//Начало цикла
start : cmp dx, bx
jg m1 // если а>b, то перейти на метку m1
cmp dx, 0
jl m2 // если x<0 то перейти на метку m2
cmp dx, 0
jg m3 // если x>0 то перейти на метку m3 иначе x=0
add ax, 54
jmp start // возвращает в начало цикла
m2: mov bx, dx
mov dx, dx
mov cx, 5
mul cx
add dx, 1
add ax, dx
mov dx,bx
mov bx, b
mov cx, p
add dx, cx
jmp start // возвращает в начало цикла
m3 : mov bx, dx
mov dx, dx
mov cx, 3
mul cx
add dx, a
add ax, dx
mov dx, bx
mov bx, b
mov cx, p
add dx, cx
jmp start // конец цыкла
m1 : mov Ans, ax
}
return Ans; // Возвращение результата
}
int main()
{
setlocale(LC_CTYPE, "rus"); // вызов функции настройки локали
short a; short b; short p; //Объявление переменных
std::cout << "Введите а, b и p \n";
std::cin >> a; // присвоение значений с клавиатуры
std::cin >> b;
std::cin >> p;
std::cout << "Ответ:";
std::cout << CalcExp(a, b, p);// Вывод на экран результата работы метода.
}
Прога работает, если передать положительные значения, а вот если например передать в а = -2, то почему то в регистр dx передаётся что то типо 65000
Ответы (1 шт):
Потому что вы используете беззнаковые операции для знаковых чисел. Ассемблерные команды деляться на знаковые беззнаковые и универсальные. mul - беззнаковая. imul - знаковая. Для
mulчисло -2 будет числом 65534, и число 65534 будет числом 65534 (условно, справедливо для отдельных случаев, для общего случая верно), потому что оно игнорирует знаковый бит. Дляimulчисло 65534 будет числом -2, и -2 будет числом -2 потому что знаковый бит оно считает знаком числа, а не старшим разрядом.short это 16-битное знаковое представление -2 в нём будет -2, а 65534 в нём будет тоже -2. Смотреть двоичное представление знакового числа. Разряд 15 (слагаемое 32768) это знак. 65536 = 65534 - (-2) это связь для 16-битного числа. 65536 - константа для 16-битного числа. unsigned short -2 в нём будет 65534, а 65534 будет тоже 65534. Для -3 эквивалент будет 65536-3 = 65533, и так далее. (Знаковые числа будут в диапазоне -32768 до 32767).
Знаковые числа сделедует передавать в мнемоники поддерживающие знак (если есть такие), например imul idiv. Тогда результат будет верно посчитан. Если перепутаете - ответ будет странным, посчитаным как для положительного числа. Беззнаковые числа следумет применять к беззнаковым (если такие есть), например mul, div. Иначе при числах где установлен последний бит - результат будет посчитан как для отрицательных чисел, но при переводе получатся "странные" числа.
Знаковость в си/с++ отличают модификаторы signed и unsigned (например unsigned short). Обычно все типы по умолчанию signed, это зависит о т опций компилятора. Но если нужен явно signed - то что бы опции не влияли - добавляют модификатор signed. Если нужно беззнаковое число - применяют unsigned. Если кастить unsigned в signed одинакового размера - биты не теряются. Поэтому после каста "большое" (65534) число станет "отрицательным" (-2), а назад "отрицательне" (-2) - "большим" (65534). Если кастятся типы данных разной размерности -может применяться "расширение знака", там чуть сложнее. signed конвертируются (каст) "с сохранением знака", а unsigned - конвертируются без изменений.
Для 16 битного числа -2 и 65534 - это одновременно "эквивалентные" значения, выбор между -2 и 65534 зависит а)от "отображения" (signed или unsigned) б) от того использована будет signed или unsigned операция. Т.е. конвертация может происходить неявно. Если в одном из случаев будет перепутано signed или unsigned, мы увидим "не то число", или "не тот результат" операции. Поэтому эту особенность можно использовать для запутывания кода, при работе с "сырыми байтами" или с ассемблером - это нужно учитывать что бы не получить UB.
Программе не важно знаковое число или беззнаковое, это важно вам, важно не потерять знак, что бы правильно посчитало. Выводите число в правильном представлении, тогда будет понятно что происходит. При операциях которые зависят от знака - выбирайте правильную - знаковую или беззнаковую соответственно. Если где-то не отображается знак (например часто в регистрах не отображается) - переводите число в знаковое с помощью остнастки, если остнастка это не поддерживает - то переводите "вручную", что бы получить правильный результат. Манипулация с знаками в отдельных случаях может быть использована для запутывания кода. Borland c++ (начиная с 3.0) - поддерживает отображение регистров в watch, даже с преобразованием (кастом, например _EAX или _AX или (short)_AX) . VS - можно (short)ax. Не все среды могут показывать регистры.