Почему флаги V нельзя объединять по OR?
Вид для печати
Лень проверять, но интуитивно мне так кажется)
---------- Post added at 00:07 ---------- Previous post was at 00:06 ----------
Интерпретивная - это обычная эмуляция. В данном случае, она бы торомозила раз... в 4 точно.
Рекомпилируемая - это сначала в полу-автоматическом режиме программа перекомпилируется под PDP11, а потом уже этот нативный код запускается.
Но это только идея.
Полагаю, что это ошибочное ощущение.
Дело в том, что и C, и V - это "циклические" флаги, поэтому механизмы их "наследования" в цепочных операциях не могут быть разными.
Т.е. если "суммарный" признак C двух операций можно и нужно определять по OR - значит и признак V - тоже.
Признак Z двух операций нужно определять по AND, а признак N - по результату последней (вроде так)..
Ну тогда вот вам пример.
В итоге, в обоих двух последних каскадах получили флаги V, хотя финально он должен быть сброшен.Код:Складываем два числа 0x7FFF.FFFF и 0x8001.0001
0x8001.0001 - это -0x7FFEFFFF
Понятно, что в сумме они нам дадут 0x0001.0000
Где флаг V = 0, т.к. арифметического переполнения не было.
Теперь вычисляем каскадно.
ADD L1,L2 L2 = 0xFFFF + 0x0001 = 0x0000
ADC H2 H2 = 0x7FFF + C = 0x8000 (V установлен)
ADD H1,H2 H2 = 0x8000 + 0x8001 = 0x0001 (V установлен)
При эмуляции команды ADD X,Y признак V определяется так:
Код:if( ((X < 0 && Y < 0) || (X >= 0 && Y >= 0)) &&
((Res < 0 && Y >= 0) || (Res >= 0 && Y < 0)) )
{
PSW |= V;
}
---------- Post added at 00:31 ---------- Previous post was at 00:25 ----------
Значит, флаг V цикличен с периодом на один бит меньше, чем флаг C, поэтому за один 16-битный цикл прибавлений единицы - флаг C установится один раз, а флаг V - два раза.
Я правильно понял?
Нет, он цикличен с таким же периодом, но со смещением на пол-периода.
Например, для 8-битных чисел, точка перехода для C - это 0xFF, а точка перехода для V - это 0x7F.
---------- Post added at 00:38 ---------- Previous post was at 00:34 ----------
Кстати, смею предположить, что корректно будет брать флаги V с двух последних каскадов сложенные по xor.
В таком случае программа точной имитации 32-разрядного сложения может выглядеть так:
MOV #<NOP>,SETPSW
CLR Cflag
CLR Vflag
CLR Zflag
ADD L1,L2
BNE 1$
BIS #4,Zflag
1$:
ADC H2
BVC 2$
BIS #2,Vflag
2$:
ADC Cflag
ADD H1,H2
BMI 3$
BNE 4$
BVC 5$
BIS #2,SETPSW
5$:
BR DONE
3$:
BVC 6$
BIS #2,SETPSW
6$:
BIS #10,SETPSW
BR 7$
4$:
BVC 7$
BIS #2,SETPSW
7$:
BIC #4,Zflag
DONE:
ADC SETPSW
BIS Cflag,SETPSW
ADD Vflag,SETPSW
BIC #4,SETPSW
BIS Zflag,SETPSW
CCC
SETPSW:
NOP
Для проверки правильности работы программы имитации 32-разрядного сложения - добавил в эмулятор команду Add32, выполняющую прибавление 32-разрядного числа с адресом в R0 к 32-разрядному числу с адресом в R1:
В приложении - программа тестирования.Код:if( nWord == 040 )
{ // Add_32_32 -- Add32 (R0),(R1)
word addr00 = R0 &(~1);
word addr02 = addr00 + 2;
dword src = WORD(addr02);
src <<= 16;
src |= WORD(addr00);
word addr10 = R1 &(~1);
word addr12 = addr10 + 2;
dword dst = WORD(addr12);
dst <<= 16;
dst |= WORD(addr10);
word flags;
__asm {
mov EAX, dst
add EAX, src
pushf
mov dst, EAX
pop AX
mov flags,AX
}
WORD(addr10) = (word)dst;
WORD(addr12) = (word)(dst>>16);
PSW &= ~(N|Z|V|C);
if( flags & BIT_0 ) { PSW |= C; }
if( flags & BIT_11 ) { PSW |= V; }
if( flags & BIT_6 ) { PSW |= Z; }
if( flags & BIT_7 ) { PSW |= N; }
continue;
}
Результат запуска:
Вроде, всё правильно.Код:.RU ADD32
Add32 Test v1.0
******************************
SRC: 0x0000'0000 (0)
DST: 0x0000'0000 (0)
Add32 : 0x0000'0000 (0) ; PSW: 004 : N[0] Z[1] V[0] C[0]
$Add32 : 0x0000'0000 (0) ; PSW: 004 : N[0] Z[1] V[0] C[0]
******************************
SRC: 0x0000'0001 (1)
DST: 0x0000'0000 (0)
Add32 : 0x0000'0001 (1) ; PSW: 000 : N[0] Z[0] V[0] C[0]
$Add32 : 0x0000'0001 (1) ; PSW: 000 : N[0] Z[0] V[0] C[0]
******************************
SRC: 0x0000'0001 (1)
DST: 0x0000'0001 (1)
Add32 : 0x0000'0002 (2) ; PSW: 000 : N[0] Z[0] V[0] C[0]
$Add32 : 0x0000'0002 (2) ; PSW: 000 : N[0] Z[0] V[0] C[0]
******************************
SRC: 0x0000'0001 (1)
DST: 0xFFFF'FFFF (-1)
Add32 : 0x0000'0000 (0) ; PSW: 005 : N[0] Z[1] V[0] C[1]
$Add32 : 0x0000'0000 (0) ; PSW: 005 : N[0] Z[1] V[0] C[1]
******************************
SRC: 0x0000'0002 (2)
DST: 0xFFFF'FFFF (-1)
Add32 : 0x0000'0001 (1) ; PSW: 001 : N[0] Z[0] V[0] C[1]
$Add32 : 0x0000'0001 (1) ; PSW: 001 : N[0] Z[0] V[0] C[1]
******************************
SRC: 0xFFFF'FFFF (-1)
DST: 0x0000'0001 (1)
Add32 : 0x0000'0000 (0) ; PSW: 005 : N[0] Z[1] V[0] C[1]
$Add32 : 0x0000'0000 (0) ; PSW: 005 : N[0] Z[1] V[0] C[1]
******************************
SRC: 0xFFFF'FFFF (-1)
DST: 0xFFFF'FFFF (-1)
Add32 : 0xFFFF'FFFE (-2) ; PSW: 011 : N[1] Z[0] V[0] C[1]
$Add32 : 0xFFFF'FFFE (-2) ; PSW: 011 : N[1] Z[0] V[0] C[1]
******************************
SRC: 0x7FFF'FFFF (2'147'483'647)
DST: 0x8001'0001 (-2'147'418'111)
Add32 : 0x0001'0000 (65'536) ; PSW: 001 : N[0] Z[0] V[0] C[1]
$Add32 : 0x0001'0000 (65'536) ; PSW: 001 : N[0] Z[0] V[0] C[1]
******************************
SRC: 0x8001'0001 (-2'147'418'111)
DST: 0x7FFF'FFFF (2'147'483'647)
Add32 : 0x0001'0000 (65'536) ; PSW: 001 : N[0] Z[0] V[0] C[1]
$Add32 : 0x0001'0000 (65'536) ; PSW: 001 : N[0] Z[0] V[0] C[1]
******************************
SRC: 0x0000'0001 (1)
DST: 0x7FFF'FFFE (2'147'483'646)
Add32 : 0x7FFF'FFFF (2'147'483'647) ; PSW: 000 : N[0] Z[0] V[0] C[0]
$Add32 : 0x7FFF'FFFF (2'147'483'647) ; PSW: 000 : N[0] Z[0] V[0] C[0]
******************************
SRC: 0x0000'0001 (1)
DST: 0x7FFF'FFFF (2'147'483'647)
Add32 : 0x8000'0000 (-2'147'483'648) ; PSW: 012 : N[1] Z[0] V[1] C[0]
$Add32 : 0x8000'0000 (-2'147'483'648) ; PSW: 012 : N[1] Z[0] V[1] C[0]
******************************
SRC: 0xFFFF'FFFF (-1)
DST: 0x8000'0001 (-2'147'483'647)
Add32 : 0x8000'0000 (-2'147'483'648) ; PSW: 011 : N[1] Z[0] V[0] C[1]
$Add32 : 0x8000'0000 (-2'147'483'648) ; PSW: 011 : N[1] Z[0] V[0] C[1]
******************************
SRC: 0xFFFF'FFFF (-1)
DST: 0x8000'0000 (-2'147'483'648)
Add32 : 0x7FFF'FFFF (2'147'483'647) ; PSW: 003 : N[0] Z[0] V[1] C[1]
$Add32 : 0x7FFF'FFFF (2'147'483'647) ; PSW: 003 : N[0] Z[0] V[1] C[1]
******************************
Есть ещё какие-то граничные случаи, которые нужно протестировать дополнительно ?
Проверил ещё немного входных данных:
Ошибок нет:Код:.W3232 0000 8000 7fff 8000
.W3232 0002 0000 7ffe 0000
.W3232 0001 0000 7ffe 0000
.W3232 0001 8000 7ffe 8000
.W3232 7fff 0000 7fff 0000
.W3232 8000 0000 7fff 0000
.W3232 8001 0001 7fff ffff
.W3232 7fff ffff 8001 0001
.W3232 8000 0000 8000 0000
.W3232 ffff 0000 8000 0000
.W3232 ffff 8000 8000 8000
Код:.RU ADD32
Add32 Test v1.1
******************************
SRC: 0x0000'8000 (32'768)
DST: 0x7FFF'8000 (2'147'450'880)
Add32 : 0x8000'0000 (-2'147'483'648) ; PSW: 012 : N[1] Z[0] V[1] C[0]
$Add32 : 0x8000'0000 (-2'147'483'648) ; PSW: 012 : N[1] Z[0] V[1] C[0]
******************************
SRC: 0x0002'0000 (131'072)
DST: 0x7FFE'0000 (2'147'352'576)
Add32 : 0x8000'0000 (-2'147'483'648) ; PSW: 012 : N[1] Z[0] V[1] C[0]
$Add32 : 0x8000'0000 (-2'147'483'648) ; PSW: 012 : N[1] Z[0] V[1] C[0]
******************************
SRC: 0x0001'0000 (65'536)
DST: 0x7FFE'0000 (2'147'352'576)
Add32 : 0x7FFF'0000 (2'147'418'112) ; PSW: 000 : N[0] Z[0] V[0] C[0]
$Add32 : 0x7FFF'0000 (2'147'418'112) ; PSW: 000 : N[0] Z[0] V[0] C[0]
******************************
SRC: 0x0001'8000 (98'304)
DST: 0x7FFE'8000 (2'147'385'344)
Add32 : 0x8000'0000 (-2'147'483'648) ; PSW: 012 : N[1] Z[0] V[1] C[0]
$Add32 : 0x8000'0000 (-2'147'483'648) ; PSW: 012 : N[1] Z[0] V[1] C[0]
******************************
SRC: 0x7FFF'0000 (2'147'418'112)
DST: 0x7FFF'0000 (2'147'418'112)
Add32 : 0xFFFE'0000 (-131'072) ; PSW: 012 : N[1] Z[0] V[1] C[0]
$Add32 : 0xFFFE'0000 (-131'072) ; PSW: 012 : N[1] Z[0] V[1] C[0]
******************************
SRC: 0x8000'0000 (-2'147'483'648)
DST: 0x7FFF'0000 (2'147'418'112)
Add32 : 0xFFFF'0000 (-65'536) ; PSW: 010 : N[1] Z[0] V[0] C[0]
$Add32 : 0xFFFF'0000 (-65'536) ; PSW: 010 : N[1] Z[0] V[0] C[0]
******************************
SRC: 0x8001'0001 (-2'147'418'111)
DST: 0x7FFF'FFFF (2'147'483'647)
Add32 : 0x0001'0000 (65'536) ; PSW: 001 : N[0] Z[0] V[0] C[1]
$Add32 : 0x0001'0000 (65'536) ; PSW: 001 : N[0] Z[0] V[0] C[1]
******************************
SRC: 0x7FFF'FFFF (2'147'483'647)
DST: 0x8001'0001 (-2'147'418'111)
Add32 : 0x0001'0000 (65'536) ; PSW: 001 : N[0] Z[0] V[0] C[1]
$Add32 : 0x0001'0000 (65'536) ; PSW: 001 : N[0] Z[0] V[0] C[1]
******************************
SRC: 0x8000'0000 (-2'147'483'648)
DST: 0x8000'0000 (-2'147'483'648)
Add32 : 0x0000'0000 (0) ; PSW: 007 : N[0] Z[1] V[1] C[1]
$Add32 : 0x0000'0000 (0) ; PSW: 007 : N[0] Z[1] V[1] C[1]
******************************
SRC: 0xFFFF'0000 (-65'536)
DST: 0x8000'0000 (-2'147'483'648)
Add32 : 0x7FFF'0000 (2'147'418'112) ; PSW: 003 : N[0] Z[0] V[1] C[1]
$Add32 : 0x7FFF'0000 (2'147'418'112) ; PSW: 003 : N[0] Z[0] V[1] C[1]
******************************
SRC: 0xFFFF'8000 (-32'768)
DST: 0x8000'8000 (-2'147'450'880)
Add32 : 0x8000'0000 (-2'147'483'648) ; PSW: 011 : N[1] Z[0] V[0] C[1]
$Add32 : 0x8000'0000 (-2'147'483'648) ; PSW: 011 : N[1] Z[0] V[0] C[1]
******************************
Чтобы точно убедиться в правильности алгоритма, недостаточно умозрительных предположений, относительно поведений флагов. Нужно или же четко обосновать их, либо же сделать тест, который переберет все варианты, либо же все варианты в зоне влияния флагов, что опять же должно быть обосновано.
1. Думаю, что относительно поведения флага N умозрительных предположений вполне достаточно.
Флаг N копирует старший бит результата.
2. Относительно флага Z тоже всё ясно - если хотя бы один бит результата отличен от 0 - флаг сбрасывается, иначе - устанавливается.
3. С флагом C больших проблем также не видно. Если был перенос из старшего разряда - флаг C устанавливается, иначе - сбрасывается.
4. Единственный не вполне очевидный флаг - флаг V.
Но все возможные комбинации влияющих на него операндов я уже протестировал.
Если не все - то какие ещё комбинации нужно протестировать ?
Теоретическое обоснование (на мой взгляд) несложно.
Флаг V устанавливается, только когда результат меняет знак. Поэтому, если результат меняет знак дважды (четырежды и т.д.) - флаг V сбрасывается.
Отсюда - если при всех операциях со старшим словом приёмника флаг V был установлен нечётное число раз - итоговый флаг V устанавливается, иначе - сбрасывается (что и реализовано).
Нет, флаг V устанавливается, когда результат не просто меняет знак, а меняет знак в связи с тем, что число вышло за пределы разрядности. Тогда как смена знака через ноль, на флаг V не влияет.
Или говоря иначе. Если был перенос в знаковый разряд, но не было переноса во флаг C, или же был перенос во флаг C, но не было переноса в знаковый разряд - флаг V устанавливается.
В описании KDJ11 дается четкое определение (взято из описания ADD):
V: set if there was arithmetic overflow as a result of the operation, that is,
both operands were of the same sign and the result was of the opposite
sign; cleared otherwise
Если внимательно прочитать написанное, то можно понять, что именно это и имелось в виду.
Но помимо этого было отмечено, что установка флага V *всегда* означает смену знака результата.
Если флаг V был установлен, значит результат изменил знак.
Следовательно, если результат не изменил знак - флаг V не может быть установлен.
Теперь понятнее..
Это нас не волнует, поскольку мы используем "готовый" признак V, который процессор формирует именно тогда, когда надо. Наша задача лишь в том, чтобы определить тот случай, когда этот признак устанавливается чётное число раз, а значит - должен быть сброшен, поскольку чётное изменение знака оставляет его НЕИЗМЕННЫМ.
Так ещё понятнее :)
...
Максимальным быстродействием (на мой взгляд) обладает древовидная версия алгоритма, которую предлагаю обсудить:
Код:$Add32:
Add L1, L2
BEq LZ
AdC H2
BVS LV
BCS LC
Add H1, H2
ClZ
Return
LC:
Add H1, H2
SeC
ClZ
Return
LV:
BCS LVC
Add H1, H2
BVS HV
SeV
ClZ
Return
LVC:
Add H1, H2
SeC
BVS HV
SeV
ClZ
Return
HV:
<ClZ>!<ClV>
Return
LZ:
AdC H2
BVS LZV
BCS LZC
Add H1, H2
Return
LZC:
Add H1, H2
SeC
Return
LZV:
BCS LZVC
Add H1, H2
BVS HZV
SeV
Return
LZVC:
Add H1, H2
SeC
BVS HZV
SeV
Return
HZV:
ClV
Return
Думать лень. CLZV требует дополнительного описания.
Можно сделать без описания в виде <CLZ>!<CLV> :)
Для завершения доказательства правильности алгоритма необходимо убедиться в невозможности последовательного возникновения следующих двух событий:
1. Инкремент 16-разрядного приёмника приводит к изменению его знака без установки флага V.
2. Прибавление 16-разрядного источника приводит к установке флага V.
Да, мы ещё забыли случай, когда положительное 16-разрядное число прибавляется к 0100000 с изменением знака приёмника, но такое явно невозможно во всём диапазоне от 0 до 77777.
---------- Post added at 11:29 ---------- Previous post was at 10:37 ----------
Когда байты, используемые в эмуляторе для хранения 8-разрядных значений источника и приёмника - имеют нечётные адреса, а предыдущие байты с чётными адресами равны нулю - тогда 16-разрядное сложение слов с нечётными адресами будет давать правильный 8-разрядный результат с корректной установкой всех флагов в PSW.
Если бы такое было возможно - Ваш алгоритм определения признака V не был бы исчерпывающим.
Но такое невозможно, поэтому использованный алгоритм определения признака V предусматривает все возможные варианты результатов операций ADC H2 и ADD H1,H2.
Кстати, поскольку предложенный выше древовидный алгоритм не имеет внутренних состояний (т.е. не использует переменные) - для тестирования его правильности необходимо и достаточно, чтобы однократное выполнение каждой ветви дало правильный результат.
Так..
---------- Post added at 15:22 ---------- Previous post was at 14:23 ----------
Не так - нужно гарантировать исчерпывающий охват всех комбинаций признаков. Иначе даже алгоритм с единственной ветвью может быть признан правильным.
Подскажите пожалуйста, как такую вот конструкцию на МАКРО-11 правильно соорудить?
Спасибо )Код:PROCEDURE TTYOUT(N:INTEGER);
CONST R=200B;
VAR A ORIGIN 177564B, D ORIGIN 177566B:INTEGER;
BEGIN
WHILE (A AND R)=0 DO;
D:=N;
END; /* THIS PROCEDURE WAS COPIED FROM HPIC.PAS */
---------- Post added at 17:09 ---------- Previous post was at 15:47 ----------
Отвечаю сам себе )))
Код:...
PROCEDURE TTYOUT(N:INTEGER);
CONST R=200B;
VAR A ORIGIN 177564B, D ORIGIN 177566B:INTEGER;
BEGIN
WHILE (A AND R)=0 DO;
D:=N;
END; /* THIS PROCEDURE WAS COPIED FROM HPIC.PAS */
...
1$: CLR @#177564
TST @#177564
BEQ 1$
(вот как эта константа R (ЧТО ЭТО - УСЛОВИЕ?СОСТОЯНИЕ? ? я "тугой-тугой!")
MOVE N(SP), @#177566
КАК ТО ТАК НАВЕРНОЕ?
Код:CONST R=200B;
VAR A ORIGIN 177564B:INTEGER;
WHILE (A AND R)=0 DO;
Эквивалентно:
1$: BIT #200, @#177564
BEQ 1$
Но проще так:
1$: TSTB @#177564
BPL 1$