2016-12-26 1 views
1

Я пытаюсь найти, если флаг CARRY равен 1 или нет, но я не знаю, как его проверить. Я написал следующий код, но мне нужна помощь для знаков вопроса я ставлю в.Как проверить, равен ли флаг CF 1 в emu8086?

LEA DX, MSG 
    MOV AH, 09H 
    INT 21H 

    MOV AH, 01H 
    INT 21H 
    MOV NUM, AL 

    SHR NUM, 1 
    CMP ??, 1 
    JE FINISH 

FINISH: MOV AH, 4CH 
    INT 21H 

    NUM DB 0 

    RET 
+6

Используйте 'jc'. См. [Эту документацию] (https://stackoverflow.com/documentation/x86/5808/control-flow/20470/conditional-jumps#t=201612261956377395382). –

+2

Или используйте 'sbb cx, cx', чтобы установить CX в 0 или -1, если это более полезно. –

+1

Также обратите внимание, что у вас есть ветка назад. Целью ветви для принятого случая является тот же адрес, что и для неудачного неудачного случая. Вы должны «jnc top_of_loop». Или, если вы на самом деле зацикливаете биты в регистре, вы должны «shr al, 1' /' jnz top_of_loop', чтобы продолжать цикл, пока не осталось никаких ненулевых битов. SHR устанавливает ZF в соответствии с результатом, а также CF в соответствии с последним разрядом. (Почему вы храните память и затем меняете это. Гораздо быстрее просто сдвинуть 'al'.) –

ответ

2

Вы не можете использовать CMP инструкции непосредственно, так как флаги не являются действительными операндами для х86. Они используются неявно с помощью определенных инструкций.

Проще всего использовать только a conditional branch. Это работает аналогично инструкции JE, с которой вы уже знакомы, за исключением того, что она разветвляется на основе значения флага переноса (CF), вместо флага нуля (ZF), такого как JE.

Чтобы условно разветвиться на статусе флага переноса (CF), вы должны использовать JC или JNC. JC будет ветвиться, если флаг переноса установлен (CF == 1), тогда как JNC будет ветвиться, если флаг переноса равен не set (CF == 0). Мнемоника для этих опкодами просто "J UMP, если С Arry" и "J UMP, если N OT C Arry".

jc CarryFlagIsSet  ; jump if CF == 1 
; else fall through: code for CF == 0 goes here 

Или по-другому:

jnc CarryFlagIsNotSet ; jump if CF == 0 
; else fall through: code for CF == 1 goes here 

Таким образом, в соответствии с вашим примером, что-то вроде:

shr num, 1  ; put least significant bit in CF 
jc num_was_odd ; (or jnc LSBNotSet aka num_was_even) 

Но as Peter Cordes points out in a comment код у вас есть почти наверняка неправильно, так как идентичный код будет выполнен независимо от того, берется ли ветка. Другими словами, назначение ветки эквивалентно провальному коду. Вы, вероятно, хотите иметь что-то подобное:

TOPOFLOOP: 
    LEA DX, MSG 
    MOV AH, 09H 
    INT 21H 

    MOV AH, 01H 
    INT 21H 
    MOV NUM, AL 

    SHR NUM, 1 
    JC TOPOFLOOP  ; keep looping as long as CF == 1 
        ; otherwise, if CF == 0, fall through to FINISH 
        ; (use JNC to reverse the logic) 

FINISH: 
    MOV AH, 4CH 
    INT 21H 
    RET 

(за исключением, что это гораздо быстрее работать на enregistered значения, чем один, хранящиеся в памяти, поэтому, если возможно, вы должны поместить NUM в регистре также. , коды операций с ассемблером и регистры нечувствительны к регистру, поэтому вы можете так же легко написать код в нижнем регистре. Я думаю, что это проще вводить и читать легче, но оно чисто стилистично и поэтому зависит от вас.)


Если вы хотите написать нераспакованный код (который почти всегда улучшает если вы можете найти достаточно умный способ написать код), вы можете использовать SBB instruction. Если оба операнда являются одним и тем же регистром, это установит этот регистр на -1, если флаг переноса установлен (CF == 1), или установите этот регистр на 0, если флаг переноса не установлен (CF == 0). Это работает, потому что SBB фактически выполняет операцию DEST = (DEST - (SRC + CF)). Когда DEST и SRC - это то же значение, это эквивалентно DEST = -CF.

В тех случаях, когда было бы удобнее иметь значение зеркало CF регистра один раз, это может быть объединено с NEG instruction:

; Perform operation that will set CF. 
... 

; Set AX to the same value as CF. 
sbb ax, ax ; CF == 1 then AX = -1; otherwise, AX = 0 
neg ax  ; AX == -1 then AX = 1; otherwise, AX = 0 

В некоторых случаях можно даже использовать ADC instruction аналогичным образом , Это выполняет операцию DEST = DEST + SRC + CF. Когда DEST и SRC равны нулю, это эквивалентно DEST = CF. Хитрость о том, что регистр назначения должен быть либо предварительно обнулить перед флаг переноса получает набор, или обнулить таким образом, что флаг переноса не влияет:

; Pre-zero AX in preparation for later use of ADC. 
xor ax, ax 

; Perform operation that will set CF. 
; (NOTE: Cannot modify AX register here, nor AL nor AH!) 
... 

; Set AX to the same value as CF. 
adc ax, ax 
; Perform operation that will set CF. 
... 

; Zero AX in such a way that flags are not clobbered. 
mov ax, 0 

; Set AX to the same value as CF. 
adc ax, ax 

Обратите внимание, что если вы хотите сохранить значение CF в памяти, вы можете использовать следующую форму ADC:

adc DWORD PTR [value], 0 

на более современных архитектурах, вы можете использовать либо SETC или CMOVC (или SETNC/CMOVNC для обратной логики - мнемоники такие же, как JC/JNC). Это, как правило, еще более быстрые способы написания нераспространяемого кода; однако на 8086 не имеется ни одного из них. Conditional set (SETcc) instructions были представлены с 386, а conditional moves (CMOVcc) были представлены с процессором Pentium Pro.

+0

В этом конкретном случае наличие NUM в регистре будет не намного быстрее, поскольку задействованы отображение сообщений и ввод символов в основном цикле. В других ситуациях это будет иметь значение. – rcgldr

+0

Обычно я бы сказал, что это правда. На 8086, я уверен, это все равно изменит ситуацию. Доступ к памяти очень * очень дорог. Но совет, безусловно, имел в виду более общий характер, в общем духе этого сайта, как рекомендацию для многих, в отличие от справочной службы для одного. –

1

Существует еще один способ проверить, установлен ли CF или нет, и с masking.

Вот рабочий пример:

TITLE 'Check if Carray Flag is set or not' 

.model small 
.data 
      cfMask equ 01h ; 01h because CF Flag is the least significant bit (right-most-bit) of the flag register 
      cfMsg db 10,13,'CF: $'  
.code 
.startup 
      mov ax, @data 
      mov ds, ax 

      mov ah, 09h 
      mov dx, offset cfMsg 
      int 21h      

      lahf ; Loads contents of flag register into ah 

      and ah, cfMask ; Check if CF is set or not by anding it with 1 

      mov dl, ah 
      add dl, 48 
      mov ah, 02h   
      int 21h 

      mov ah, 04ch 
      int 21h 
end 

Flag register

+0

'lahf/and' отлично, но сохранение маски в памяти (а не сразу) не имеет смысла. Кроме того, перемещение в 'cfVal' и перезагрузка также бессмысленно. Просто 'mov dl, 1' /' и dl, ah'. Или используйте 'CFMASK equ 1' для определения константы ассемблера. –

+0

@PeterCordes, что теперь? – Ahtisham

+0

Да, это лучше. Я бы рекомендовал называть 'mask' нечто более конкретное, например' CFmask'. Я также рекомендую использовать '' 0'' вместо '48', потому что символьные константы более самодокументированы. –

1

Если вы хотите, чтобы расшириться на нем, используйте jc/jnc; это именно то, для чего они предназначены.

Если вы хотите, чтобы значение CF как 0/1 целое число: ответ Коди хороший, но есть и другие способы.

Если emu8086 поддерживает the undocumented salc instruction, вы можете использовать его для установки al = 0/-1. Это однобайтная кодировка sbb al,al и поддерживается в 16 и 32-битном режиме всеми процессорами Intel и AMD, включая текущие, такие как Skylake и, по-видимому, даже Knight's Landing (Xeon Phi). Кодировки инструкций SSSE3/SSE4 могли быть короткими, если бы Intel отказалась от поддержки. (См. Сообщение в блоге Agner Fog Stop the instruction set war).

Но если Intel настаивает на ее поддержке, мы можем использовать его, даже если они не документируют его по какой-либо причине. (При условии, что мы оптимизируя для размера кода, то есть для 8086, а не для реального современного процессора. salc 3 микрооперации, но 1c задержка на SNB/Haswell/Skylake)


salc   ; 1 byte (but 3 uops on SnB-family) 
and al, 1  ; 2 byte special encoding of and al, imm8 

; neg al  ; 2 bytes also an option 

Или (оптимизированная версия ответа @ Доды): lahf эффективен.

lahf   ; 1 byte, 1 uop (SnB-family). CF is the low bit of FLAGS. 
and ah, 1  ; 3 bytes 

Или, если вы хотите, чтобы целое число 0/1 находилось где-то в другом месте, например. так что вы можете распечатать его с int 21h системного вызова, как @ ответ Дода в):

lahf 
mov dx, 1  ; or just dl if you don't need to zero-extend it. 
and dl, ah 

Upside против Коди sbb/neg: избегает sbb same,same (2 микрооперации и ложную зависимость от регистра, отличных AMD Bulldozer- процессоров семья)

Даунсайд: хуже размер кода (очень важно для реального 8086). Дополнительные инструкции. (Но такое же количество совпадений в Intel SnB-family перед Broadwell, где sbb - 2 раза). Проблемы с неполным регистром из записи dl, а затем чтение dx/edx на некоторых процессорах (Intel pre-Ivybridge).


Если вы не ограничены в 8086 только, используйте setc dl (или любой 8-битный регистр, или непосредственно в памяти).

Также очень хорошо, особенно на Бродуэлла и позже: от @ ответа Коди:

xor eax,eax 
; do something that sets CF 
adc eax, 0 

adc не имеет никаких преимуществ перед setc al хотя if eax was xor-zeroed (избегает частичного регистра киосков и сливающихся штрафов на Intel P6-семье из написание al и затем чтение eax).

Смежные вопросы