Сегодня я использую g ++ для компиляции файла c ++, но, похоже, проблема с g ++ заключается в том, что когда переменная типа char сравнивается с любым числом типов char с битом 7, установленным в 1, g ++ всегда будет предполагать, что это false, что делает работу неправильно. Более конкретно, код C ++ выглядит так:
// test.cpp
__asm__(".code16gcc \n\t");
int equals0(char i)
{
return i==0x80;
}
int equals1(char i)
{
return i==0x10;
}
int equals2(int i)
{
return i==0x80;
}
Обратите внимание, что есть предложение преамбулы «.code16gcc», которое я использовал для генерации кодов реального режима.
Чтобы быть более конкретным, моя версия g ++ — «g ++ (GCC) 6.4.0» на Cygwin.
Теперь скомпилируйте этот файл в код сборки, используя:
g++ -S -o test.s test.cpp -m32
И полученный файл:
// some trival information is omitted
.code16gcc
.text
...
__Z7equals0c://func equals0
pushl %ebp
movl %esp, %ebp
subl $4, %esp
movl 8(%ebp), %eax
movb %al, -4(%ebp) //no comparison with 0x80
movl $0, %eax //always returns 0
leave
ret
...
__Z7equals1c://func equals1
pushl %ebp
movl %esp, %ebp
subl $4, %esp
movl 8(%ebp), %eax
movb %al, -4(%ebp)
cmpb $16, -4(%ebp) // a comparison with 0x10
sete %al
movzbl %al, %eax // returns value depend on the result of comparison
leave
ret
...
__Z7equals2i://func equals2
pushl %ebp
movl %esp, %ebp
cmpl $128, 8(%ebp) //also a comparison
sete %al
movzbl %al, %eax
popl %ebp
ret
Обратите внимание, что функция equals0 всегда будет возвращать 0 (сохраненный в eax), но функция equals1 будет возвращать правильный результат, основанный на сгенерированном файле сборки.
Также обратите внимание, что единственная разница между функцией equals0 и equals1 — это константа, используемая для сравнения, 0x80 для equals0 и 0x10 для equals1. Исходя из этого, мы можем сказать, что g ++ сгенерировал логически неверный код.
Кто-нибудь знает почему и объяснит это?
0x80
(int
128
) находится вне диапазона подписанного символа ([-128;127]
).
поэтому подписанный символ не может быть равен 0x80
Обратите внимание, что есть предложение преамбулы «.code16gcc», которое я использовал для генерации кодов реального режима.
Неправильно. Ваш __asm__(".code16gcc \n\t");
просто излучающий некоторая директива ассемблера (которая сделала бы ваш ассемблер переключить режим). Но вы хотите изменить поведение вашего компилятора GCC (например, вы ожидаете, что GCC будет испускать 16-битный код ассемблера реального режима).
AFAIK, ты не можешь сделать это легко. Возможно, вы могли бы найти вариант GCC (возможно, некоторый кросс-компилятор) для генерации кода реального режима, а затем вам нужно использовать этот вариант в вашем модуле перевода C ++. Вы мечтаете о некоторых -m16realmode
флаг для GCC, который не существует. И GCC -m16
Режим (очень запутанно названный!) все еще испускает 32-битный код:
-m16
вариант такой же, как-m32
за исключением того, что он выводит.code16gcc
директива ассемблера в начале вывода сборки, чтобы двоичный файл мог работать в 16-битном режиме.
(на самом деле, двоичный не могу работать в 16-битном режиме, если вы не предоставлять и позвонить некоторым дополнительный переключение кода на 32 бита; вам, вероятно, нужно написать этот код режима переключения на ассемблере)
В 2017 году нет причин использовать устаревший 16-битный код реального режима. Единственный случай, когда это может быть полезно, это когда вы кодируете загрузчик. Тогда вы не должны использовать GCC, и что более важно, вы должны использовать некоторые существующий загрузчик (как GRUB) и сосредоточить свои усилия на отдыхе.
Если вы кодируете игрушечную операционную систему (и вы должны кодировать ее как минимум в 32-битном режиме), посмотрите на OSDEV wiki для подсказок.
Теперь скомпилируйте этот файл в код сборки, используя:
g++ -S -o test.s test.cpp -m32
Это все еще использует 32-битный режим (не 16-битное наследие) для всего источника (и ошибочно вставляет неправильно .code16gcc
директива ассемблера внутри него). Кстати, вам лучше попросить некоторые оптимизации. Поэтому я рекомендую компилировать с g++ -m32 -Wall -O -fverbose-asm -S -o test.s test.cpp
,
Кстати, вы смущены подписью char
который фиксированный в пределах -m32
архитектура x86 (так char
это signed char
на x86; на PowerPC это может быть unsigned char
). Я не рекомендую перекомпилировать с -funsigned-char
но если вы делаете это, вы меняете ABI и вам нужно перекомпилировать все, включая вашу стандартную библиотеку C и стандартную библиотеку C ++.