Начиная с процессора 80286 компания Intel поддерживала полную совместимость «снизу-вверх» в системе команд. То есть если какая-то из команд процессора дает такой-то результат на 8086, то и на более поздних процессорах результат будет точно таким же (сейчас не будем рассматривать ошибки типа неправильного деления в Pentium I).
Но так ли это? Что за вопрос! Ведь если бы совместимость не сохранялась, то старые программы не могли бы выполняться, а ведь до сих пор на любом компьютере можно поностальгировать запустив Norton Commander или Tetris . Однако не все так просто… Начиная с 8080 в процессорах Intel есть регистр флагов, состояние которого определяется результатом последней команды вычисления данных. Все флаги в нем давно описаны и поведение их строго зафиксировано. Кроме двух исключений.
AF – бит вспомогательного переноса. Он определял произошел ли перенос из младшей тетрады в старшую. Для 8-разрядного 8080 это было логично и для всех его команд состояние однозначно определялось. При переходе на 16-разрядные процессоры x86 появилась двойственность – в каком байте нужно учитывать вспомогательный перенос ? На что Intel однозначно дала ответ – флаг AF меняет свое состояние только по результатам переноса внутри младшего байта. Но процессоре 8080 не было команд умножения и деления. И при переходе на 16-разрядную архитектуру поведение AF при командах умножения и деления так и осталось не определенным!
Дальше еще интереснее, оказалось что по-разному ведет себя еще и флаг четности! На разных поколениях процессоров и у разных производителей в результате операций умножения и деления флаги AF и PF разные!
Чтобы убедиться в этом можно выполнить коротенькую программу:
#include <iostream>
#include <iomanip>
int main()
{
using namespace std;
const int min_i = -20, max_i = 50,min_j=-10, max_j = 50;
short int i, j, k, pf;
cout << "Name;i;j;Result;AF;PF;Calculated parityn";
for (i = min_i; i < max_i; i++) {
for (j = min_j; j < max_j; j++) {
__asm {
mov ax, i
mov dx, j
imul dx
pushf
pop ax
mov k, ax
}
pf = i * j; pf = ((pf >> 7) ^ (pf >> 6) ^ (pf >> 5) ^ (pf >> 4) ^ (pf >> 3) ^ (pf >> 2) ^ (pf >> 1) ^ pf ^ 1) & 1;
cout << "Mul ;" << setw(4) << i << "; " << setw(4) << j << "; " << setw(4) << i * j << "; " << ((k & 0x10) ? "1" : "0") << "; " << ((k & 0x04) ? "1" : "0") << "; " << pf <<"n";
}
}
for (i = min_i; i < max_i; i++) {
for (j = min_j; j < max_j; j++) {
if (j == 0) continue;
__asm {
mov ax, i
mov cx, j
cwd
idiv cx
pushf
pop ax
mov k, ax
}
pf = i / j; pf = ((pf >> 7) ^ (pf >> 6) ^ (pf >> 5) ^ (pf >> 4) ^ (pf >> 3) ^ (pf >> 2) ^ (pf >> 1) ^ pf ^ 1) & 1;
std::cout << "Div ;" << setw(4) << i << "; " << setw(4) << j << "; " << setw(4) << i / j << "; " << ((k & 0x10) ? "1" : "0") << "; " << ((k & 0x04) ? "1" : "0") << "; " << pf << "n";
}
}
}
Эта программа выводит на экран результат проверки флагов AF и PF после операций умножения и деления. А так же (для удобства) рассчитанный результат четности последнего байта результата операции.
Я протестировал этой программой 16 разных процессоров. Вкратце результат такой:
Wolfdale |
Sandy Bridge |
Coffee Lake |
AMD |
||||||||
Pentium Dual-Core E6600 |
Core i5-2300 |
Xeon E-2278G |
AMD Ryzen 7 2700 |
||||||||
i |
j |
Result |
Calс parity |
AF |
PF |
AF |
PF |
AF |
PF |
AF |
PF |
-20 |
-10 |
200 |
0 |
0 |
1 |
0 |
0 |
0 |
0 |
0 |
1 |
-20 |
-9 |
180 |
1 |
0 |
1 |
0 |
1 |
0 |
1 |
0 |
1 |
-20 |
-8 |
160 |
1 |
0 |
1 |
0 |
1 |
0 |
1 |
0 |
1 |
-20 |
-7 |
140 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
-20 |
-6 |
120 |
1 |
0 |
0 |
0 |
1 |
0 |
1 |
0 |
0 |
-20 |
-5 |
100 |
0 |
0 |
1 |
0 |
0 |
0 |
0 |
0 |
1 |
-20 |
-4 |
80 |
1 |
0 |
1 |
0 |
1 |
0 |
1 |
0 |
1 |
-20 |
-3 |
60 |
1 |
0 |
0 |
0 |
1 |
0 |
1 |
0 |
0 |
-20 |
-2 |
40 |
1 |
0 |
1 |
0 |
1 |
0 |
1 |
0 |
1 |
-20 |
-1 |
20 |
1 |
0 |
0 |
0 |
1 |
0 |
1 |
0 |
0 |
-20 |
0 |
0 |
1 |
1 |
0 |
0 |
1 |
0 |
1 |
1 |
0 |
-20 |
1 |
-20 |
0 |
1 |
1 |
0 |
0 |
0 |
0 |
1 |
1 |
-20 |
2 |
-40 |
1 |
0 |
0 |
0 |
1 |
0 |
1 |
0 |
0 |
-20 |
3 |
-60 |
0 |
0 |
1 |
0 |
0 |
0 |
0 |
0 |
1 |
-20 |
4 |
-80 |
0 |
0 |
1 |
0 |
0 |
0 |
0 |
0 |
1 |
-20 |
5 |
-100 |
1 |
0 |
0 |
0 |
1 |
0 |
1 |
0 |
0 |
-20 |
6 |
-120 |
1 |
0 |
1 |
0 |
1 |
0 |
1 |
0 |
1 |
-20 |
7 |
-140 |
1 |
0 |
0 |
0 |
1 |
0 |
1 |
0 |
0 |
-20 |
8 |
-160 |
1 |
0 |
0 |
0 |
1 |
0 |
1 |
0 |
0 |
-20 |
9 |
-180 |
0 |
0 |
1 |
0 |
0 |
0 |
0 |
0 |
1 |
-20 |
10 |
-200 |
0 |
0 |
1 |
0 |
0 |
0 |
0 |
0 |
1 |
-20 |
11 |
-220 |
1 |
0 |
0 |
0 |
1 |
0 |
1 |
0 |
0 |
-20 |
12 |
-240 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
-20 |
13 |
-260 |
1 |
0 |
1 |
0 |
1 |
0 |
1 |
0 |
1 |
-20 |
14 |
-280 |
1 |
0 |
0 |
0 |
1 |
0 |
1 |
0 |
0 |
-20 |
15 |
-300 |
1 |
0 |
1 |
0 |
1 |
0 |
1 |
0 |
1 |
-20 |
16 |
-320 |
1 |
1 |
1 |
0 |
1 |
0 |
1 |
1 |
1 |
-20 |
17 |
-340 |
1 |
1 |
0 |
0 |
1 |
0 |
1 |
1 |
0 |
-20 |
18 |
-360 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
-20 |
19 |
-380 |
1 |
0 |
1 |
0 |
1 |
0 |
1 |
0 |
1 |
-20 |
20 |
-400 |
0 |
0 |
1 |
0 |
0 |
0 |
0 |
0 |
1 |
-20 |
21 |
-420 |
1 |
0 |
0 |
0 |
1 |
0 |
1 |
0 |
0 |
-20 |
22 |
-440 |
1 |
0 |
1 |
0 |
1 |
0 |
1 |
0 |
1 |
-20 |
23 |
-460 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
-20 |
24 |
-480 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
-20 |
25 |
-500 |
1 |
0 |
1 |
0 |
1 |
0 |
1 |
0 |
1 |
-20 |
26 |
-520 |
0 |
0 |
1 |
0 |
0 |
0 |
0 |
0 |
1 |
-20 |
27 |
-540 |
1 |
0 |
0 |
0 |
1 |
0 |
1 |
0 |
0 |
-20 |
28 |
-560 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
-20 |
29 |
-580 |
0 |
0 |
1 |
0 |
0 |
0 |
0 |
0 |
1 |
-20 |
30 |
-600 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
-20 |
31 |
-620 |
0 |
0 |
1 |
0 |
0 |
0 |
0 |
0 |
1 |
-20 |
32 |
-640 |
0 |
1 |
1 |
0 |
0 |
0 |
0 |
1 |
1 |
-20 |
33 |
-660 |
1 |
1 |
0 |
0 |
1 |
0 |
1 |
1 |
0 |
-20 |
34 |
-680 |
0 |
0 |
1 |
0 |
0 |
0 |
0 |
0 |
1 |
-20 |
35 |
-700 |
1 |
0 |
0 |
0 |
1 |
0 |
1 |
0 |
0 |
-20 |
36 |
-720 |
1 |
0 |
0 |
0 |
1 |
0 |
1 |
0 |
0 |
-20 |
37 |
-740 |
0 |
0 |
1 |
0 |
0 |
0 |
0 |
0 |
1 |
-20 |
38 |
-760 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
-20 |
39 |
-780 |
0 |
0 |
1 |
0 |
0 |
0 |
0 |
0 |
1 |
-20 |
40 |
-800 |
0 |
0 |
1 |
0 |
0 |
0 |
0 |
0 |
1 |
-20 |
41 |
-820 |
1 |
0 |
0 |
0 |
1 |
0 |
1 |
0 |
0 |
-20 |
42 |
-840 |
1 |
0 |
0 |
0 |
1 |
0 |
1 |
0 |
0 |
-20 |
43 |
-860 |
0 |
0 |
1 |
0 |
0 |
0 |
0 |
0 |
1 |
-20 |
44 |
-880 |
1 |
0 |
1 |
0 |
1 |
0 |
1 |
0 |
1 |
-20 |
45 |
-900 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
-20 |
46 |
-920 |
0 |
0 |
1 |
0 |
0 |
0 |
0 |
0 |
1 |
-20 |
47 |
-940 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
-20 |
48 |
-960 |
0 |
1 |
0 |
0 |
0 |
0 |
0 |
1 |
0 |
-20 |
49 |
-980 |
0 |
1 |
1 |
0 |
0 |
0 |
0 |
1 |
1 |
Результат команды умножения |
Yonah |
Conroe |
Coffee Lake |
AMD |
||||||||
Core Duo T2450 |
Core 2 Duo E6750 |
Xeon_E-2278G |
AMD Ryzen 7 2700 |
||||||||
i |
j |
Result |
Calc parity |
AF |
PF |
AF |
PF |
AF |
PF |
AF |
PF |
-20 |
-10 |
2 |
0 |
0 |
1 |
0 |
1 |
0 |
1 |
1 |
0 |
-20 |
-9 |
2 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
0 |
-20 |
-8 |
2 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
0 |
-20 |
-7 |
2 |
0 |
0 |
1 |
0 |
1 |
0 |
1 |
1 |
0 |
-20 |
-6 |
3 |
1 |
0 |
1 |
0 |
1 |
0 |
1 |
1 |
0 |
-20 |
-5 |
4 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
0 |
-20 |
-4 |
5 |
1 |
0 |
1 |
0 |
1 |
0 |
1 |
1 |
0 |
-20 |
-3 |
6 |
1 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
0 |
-20 |
-2 |
10 |
1 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
0 |
-20 |
-1 |
20 |
1 |
0 |
1 |
0 |
1 |
0 |
1 |
1 |
0 |
-20 |
1 |
-20 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
0 |
-20 |
2 |
-10 |
1 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
0 |
-20 |
3 |
-6 |
1 |
0 |
1 |
0 |
1 |
0 |
1 |
1 |
0 |
-20 |
4 |
-5 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
0 |
-20 |
5 |
-4 |
1 |
0 |
1 |
0 |
1 |
0 |
1 |
1 |
0 |
-20 |
6 |
-3 |
0 |
0 |
1 |
0 |
1 |
0 |
1 |
1 |
0 |
-20 |
7 |
-2 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
0 |
-20 |
8 |
-2 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
0 |
-20 |
9 |
-2 |
0 |
0 |
1 |
0 |
1 |
0 |
1 |
1 |
0 |
-20 |
10 |
-2 |
0 |
0 |
1 |
0 |
1 |
0 |
1 |
1 |
0 |
-20 |
11 |
-1 |
1 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
0 |
-20 |
12 |
-1 |
1 |
0 |
1 |
0 |
1 |
0 |
1 |
1 |
0 |
-20 |
13 |
-1 |
1 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
0 |
-20 |
14 |
-1 |
1 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
0 |
-20 |
15 |
-1 |
1 |
0 |
1 |
0 |
1 |
0 |
1 |
1 |
0 |
-20 |
16 |
-1 |
1 |
1 |
0 |
0 |
0 |
0 |
0 |
1 |
0 |
-20 |
17 |
-1 |
1 |
0 |
1 |
0 |
1 |
0 |
1 |
1 |
0 |
-20 |
18 |
-1 |
1 |
0 |
1 |
0 |
1 |
0 |
1 |
1 |
0 |
-20 |
19 |
-1 |
1 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
0 |
-20 |
20 |
-1 |
1 |
0 |
1 |
0 |
1 |
0 |
1 |
1 |
0 |
-20 |
21 |
0 |
1 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
0 |
-20 |
22 |
0 |
1 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
0 |
-20 |
23 |
0 |
1 |
0 |
1 |
0 |
1 |
0 |
1 |
1 |
0 |
-20 |
24 |
0 |
1 |
0 |
1 |
0 |
1 |
0 |
1 |
1 |
0 |
-20 |
25 |
0 |
1 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
0 |
-20 |
26 |
0 |
1 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
0 |
-20 |
27 |
0 |
1 |
0 |
1 |
0 |
1 |
0 |
1 |
1 |
0 |
-20 |
28 |
0 |
1 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
0 |
-20 |
29 |
0 |
1 |
0 |
1 |
0 |
1 |
0 |
1 |
1 |
0 |
-20 |
30 |
0 |
1 |
0 |
1 |
0 |
1 |
0 |
1 |
1 |
0 |
-20 |
31 |
0 |
1 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
0 |
-20 |
32 |
0 |
1 |
1 |
0 |
0 |
0 |
0 |
0 |
1 |
0 |
-20 |
33 |
0 |
1 |
0 |
1 |
0 |
1 |
0 |
1 |
1 |
0 |
-20 |
34 |
0 |
1 |
0 |
1 |
0 |
1 |
0 |
1 |
1 |
0 |
-20 |
35 |
0 |
1 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
0 |
-20 |
36 |
0 |
1 |
0 |
1 |
0 |
1 |
0 |
1 |
1 |
0 |
-20 |
37 |
0 |
1 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
0 |
-20 |
38 |
0 |
1 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
0 |
-20 |
39 |
0 |
1 |
0 |
1 |
0 |
1 |
0 |
1 |
1 |
0 |
-20 |
40 |
0 |
1 |
0 |
1 |
0 |
1 |
0 |
1 |
1 |
0 |
-20 |
41 |
0 |
1 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
0 |
-20 |
42 |
0 |
1 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
0 |
-20 |
43 |
0 |
1 |
0 |
1 |
0 |
1 |
0 |
1 |
1 |
0 |
-20 |
44 |
0 |
1 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
0 |
-20 |
45 |
0 |
1 |
0 |
1 |
0 |
1 |
0 |
1 |
1 |
0 |
-20 |
46 |
0 |
1 |
0 |
1 |
0 |
1 |
0 |
1 |
1 |
0 |
-20 |
47 |
0 |
1 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
0 |
-20 |
48 |
0 |
1 |
1 |
1 |
0 |
1 |
0 |
1 |
1 |
0 |
-20 |
49 |
0 |
1 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
0 |
Результат команды деления |
Даже по такой небольшой выборке, можно сделать следующие выводы:
-
Операции умножения различаются на современных и более старых процессорах Intel (до Wolfdale включительно – можно отнести к «старому» поколению, начиная с Sandy Bridge – «новое» (Nehalem мне не попадался)). На «новых» процессорах бит AF всегда устанавливается после умножения в 0, а PF – в соответствии с младшим байтом результата (как и следовало бы делать раньше). Логика же установки битов в «старых» процессорах мне не понятна.
-
Процессоры AMD выполняют умножение с установкой битов, в точности как «старое» поколение от Intel.
-
На операциях деления все процессоры Intel устанавливают бит четности одинаково (но он не совпадает с четностью битов последнего байтов результатов деления и остатка по модулю).
-
Бит AF после деления в процессорах Intel устанавливается в 0, за исключением семейств Yonah и более ранних.
-
AMD после деления устанавливает AF=1 и PF=0.
Полную таблицу тестов можно скачать
Если у кого-то есть доступ к невошедшим в тестирование процессорам – прошу принять участие.
Автор: SlFed