Я пишу виртуальную машину для своего собственного языка ассемблера, я хочу иметь возможность устанавливать флаги переноса, четности, нуля, знака и переполнения, как они установлены в архитектуре x86-64, когда я выполняю такие операции, как сложение.
Примечания:
- Я использую Microsoft Visual C++ 2015 и компилятор Intel C++ 16.0.
- Я компилирую как приложение Win64.
- Моя виртуальная машина (в настоящее время) выполняет арифметические операции только с 8-битными целыми числами.
- Меня (в настоящее время) не интересуют никакие другие флаги (например, AF)
В моем текущем решении используется следующая функция:
void update_flags(uint16_t input)
{
Registers::flags.carry = (input > UINT8_MAX);
Registers::flags.zero = (input == 0);
Registers::flags.sign = (input < 0);
Registers::flags.overflow = (int16_t(input) > INT8_MAX || int16_t(input) < INT8_MIN);
// I am assuming that overflow is handled by trunctation
uint8_t input8 = uint8_t(input);
// The parity flag
int ones = 0;
for (int i = 0; i < 8; ++i)
if (input8 & (1 << i) != 0) ++ones;
Registers::flags.parity = (ones % 2 == 0);
}
Что для добавления я бы использовал следующим образом:
uint8_t a, b;
update_flags(uint16_t(a) + uint16_t(b));
uint8_t c = a + b;
РЕДАКТИРОВАТЬ: Чтобы уточнить, я хочу знать, есть ли более эффективный/аккуратный способ сделать это (например, путем прямого доступа к RFLAGS). Также мой код может не работать для других операций (например, умножение)
EDIT 2 Теперь я обновил свой код следующим образом:
void update_flags(uint32_t result)
{
Registers::flags.carry = (result > UINT8_MAX);
Registers::flags.zero = (result == 0);
Registers::flags.sign = (int32_t(result) < 0);
Registers::flags.overflow = (int32_t(result) > INT8_MAX || int32_t(result) < INT8_MIN);
Registers::flags.parity = (_mm_popcnt_u32(uint8_t(result)) % 2 == 0);
}
Еще один вопрос, будет ли правильно работать мой код для флага переноса? Я также хочу, чтобы он был правильно установлен для "заимствований", которые происходят во время вычитания.
Примечание. Язык ассемблера, который я виртуализирую, разработан мной, должен быть простым и основан на реализации Intel x86-64 (т.е. Intel64), и поэтому я хотел бы, чтобы эти флаги вели себя в основном одинаково.
input < 0
никогда не будет истинным, потому чтоinput
не имеет знака, а OF будет зависеть от операндов, а не только от результата. Например, 8-битная операция0x7f + 0x02 = 0x81
приведет кOF = 1
, а0x82 + 0xff = 0x81
приведет кOF = 0
. Этот код неверен, поэтому некоторый код, который правильно устанавливает флаги, более аккуратен, чем этот способ. - person MikeCAT   schedule 26.03.2016