среда, 26 января 2011 г.

EFLAGS. Перенос (CF) и переполнение (OF)

Есть в процессоре (будем говорить об архитектуре Intel) такой замечательный регистр - EFLAGS или регистр флагов. Данный 32-битный регистр содержит группу статусных флагов, группу флагов контроля и группу системных флагов. Как правило, в учебниках по архитектуре компьютера или ассемблеру присутствует довольно сухое описание, для чего определенный флаг предназначен, и еще чаще отсутствуют примеры, связанные с данным флагом. Рассмотрим два важных флага - флаг переноса (carry flag - CF) и флаг переполнения (overflow flag - OF) и постараемся более подробно осветить эту тему, чем в обычно это делают в учебниках.
Из руководства "Intel Software developer`s manual vol.1" (перевод):
CF - устанавливается, если в результате арифметической операции произошло переполнение разрядной сетки либо заем из старшего бита. Флаг свидетельствует о переполнении для беззнаковых чисел.

Будем рассматривать все на примере 8-битных чисел/регистров.
С переполнением разрядной сетки все достаточно просто. Диапазон беззнаковых чисел, которые могут быть представлены 8 битами - это 0...255. Следовательно переполнение наступит, если результатом выполнения операции будет число больше 255.

    clc
    mov al, 250
    mov bl, 10
    add al, bl ; должно быть 260, но на деле в al 4

В данном случае число не помещается в 8 бит, и возникает переполнение.

Рассмотрим числа 8 и 6. Что будет если из 6 вычесть 8? В двоичном представлении 6 имеет вид 00000110, 8 - 00001000. При поразрядном вычитании в 4 бите произойдет заем, который породит заем из 5,6,7,8 битов. Т.е. в итоге происходит заем из старшего бита, а это значит что флаг переноса устанавливается.

    clc
    mov al, 6
    mov bl, 8
    sub al, bl ; теперь в al -2

Для чего в данном случае устанавливается флаг. Дело в том, что результат получается отрицательным, что логично, но это неверно для беззнаковых чисел, которые по определению не могут иметь отрицательного значения. Т.е. результат опять не входит в диапазон 0...255 Из этого можно сделать вывод - если происходит вычитание большего числа из меньшего, то флаг переноса будет установлен, т.к. в этом случае происходит заем из старшего бита.
А при сложении отрицательного и положительного (минус на минус дает плюс) и получая по сути тот же результат, флаг переноса устанавливаться не будет, несмотря на то, что результат является слишком малым и не входит в диапазон 0...255. Дело в том, что отсутствует заем из старшего бита.
    mov al, -79
    mov bl, 49
    add al, bl ; CF = 0
Но если взять вместо -79 число -49, то флаг переноса установится, т.к. -49 для беззнаковых чисел равно 207, а 207+49=256, что является переполнением 8 бит.
Флаг переноса полезен при работе с большими числами, не помещающимися в 32-разрядный регистр. Например, если требуется сложить 2 числа, каждое из которых расположено в паре 32-рязрядных регистров.

Рассмотрим флаг переполнения.
Из руководства "Intel Software developer`s manual vol.1" (перевод):
Флаг OF устанавливается, если число - слишком большое положительное, либо слишком маленькое отрицательное. Флаг свидетельствует о переполнении для знаковых чисел.
Диапазон знаковых чисел в 8-битном случае -128...127, т.е., как сказано в руководстве, 2 варианта:
  • Число слишком мало для представления в виде 8-битного знакового
  • Число слишком велико для представления в виде 8-битного знакового

Первый случай:
    mov al, 117
    mov bl, 92
    add al, bl ;
В итоге имеем -47 вместо 209 из-за переноса в знаковый бит, т.е. число 209 выходит за допустимый диапазон сверху.
Второй случай:
    mov al, -20
    mov bl, -118
    add al, bl ;
Складываем 2 отрицательных числа и получаем тем не менее положительную сумму (118 вместо -138), т.к. -138 выходит за допустимый диапазон снизу.
Но что такое "знаковые числа"? Как процессор понимает, когда используется знаковое, а когда - беззнаковое число. На самом деле процессор не знает этого. Он сразу предполагает оба случая и в соответствии с этим выставляет флаги.