
- •Для бакалавров, обучающихся по направлению 230400
- •2. Регистры
- •2.1. Регистр флагов
- •2.2. Регистры общего назначения
- •2.3. Индексные регистры
- •2.4. Регистры - указатели
- •2.5. Указатель инструкций
- •2.6. Сегментные регистры
- •3. Вывод символов на экран
- •Int 21h ; завершить программу
- •4. Метки
- •5. Операнды
- •5.1. Регистровые операнды
- •5.2. Постоянные операнды
- •6. Работа со стеком
- •7. Арифметические операции
- •8. Пример прикладной программы
- •9. Задания
- •10. Контрольные вопросы
6. Работа со стеком
На вершину стека всегда указывает регистр SP. Для обращения к данным в стеке с использованием режимов адресации памяти, при которых в указателем базы является регистр BP, можно использовать инструкцию MOV. Например, инструкция:
mov ax,[bp+4]
загружает регистр AX содержимым слова в сегменте стека со смещением BP+4.
Однако чаще к стеку обращаются с помощью инструкций PUSH и POP. Инструкция PUSH сохраняет операнд в вершине стека, а инструкция POP извлекает значение из вершины стека и сохраняет его в операнде. Например, инструкции:
mov ax,1
push ax
pop bx
заносят значение (равное 1) в регистре AX в вершину стека, затем извлекают 1 из вершины стека и сохраняют ее в BX.
7. Арифметические операции
Выполнять обмен содержимого двух операндов позволяет инструкция XCHG. Это предоставляет удобный способ выполнять операцию, которая в противном случае потребовала бы трех инструкций. Например, инструкция:
xchg ax,dx
выполняет обмен содержимого AX и DX, что эквивалентно выполнению инструкций:
push ax
mov ax,dx
pop dx
Операции ADD и SUB работают с 8- или 16-битовыми операндами. Если нужно сложить или вычесть 32-разрядные операнды, надо разбить операцию на ряд операций со значениями размером в слово и использовать инструкции ADC и SBB.
Когда складываются два операнда, процессор записывает состояние во флаг переноса (бит С в регистре флагов), которое показывает, был ли выполнен перенос из приемника. Младшее слово результата равно нулю, перенос равен 1, поскольку результат (10000h) не вмещается в 16 битов. Инструкция ADC аналогична инструкции ADD, но в ней учитывается флаг переноса (предварительно установленный предыдущим сложением). Всякий раз когда складываются два значения, превышающие по размеру слово, то младшие (менее значащие) слова нужно сложить с помощью инструкции ADD, а остальные слова этих значений - с помощью одной или нескольких инструкций ADC, последними складывая самые значащие слова. Например, следующие инструкции складывают значение в регистрах CX:BX, размером в двойное слово, со значением, записанным в регистрах DX:AX:
add ax,bx
adc dx,cx
а в следующей группе инструкций выполняется сложение четверного слова в переменной DoubleLong1 с четверным словом в переменной DoubleLong2:
mov ax,[DoubleLong1]
add [DoubleLong2],ax
mov ax,[DoubleLong1+2]
adc [DoubleLong2+2],ax
mov ax,[DoubleLong1+4]
adc [DoubleLong1+4],ax
mov ax,[DoubleLong1+6]
adc [DoubleLong2+6],ax
Инструкция SBB работает по тому же принципу, что и инструкция ADC. Когда инструкция SBB выполняет вычитание, в ней учитывается заем, произошедший в предыдущем вычитании. Например, следующие инструкции вычитают значение, записанное в регистрах CX:BX, из значения размеров в двойное слово, записанного в регистрах DX:AX:
sub ax,bx
sbb dx,cx
При работе с инструкциями ADC и SBB нужно убедиться, что флаг переноса не изменился с момента выполнения последнего сложения или вычитания, иначе состояние заема/переноса, хранящееся во флаге переноса, будет потеряно. Например, в следующем фрагменте программы сложение CX:BX с DX:AX выполняется некорректно:
add ax,bx ; сложить младшие слова
sub si,si ; очистить SI (флаг переноса сбрасывается в 0)
adc dx,cx ; сложить старшие слова...
; это будет работать некорректно,
; так как с момента последней
; операции сложения содержимое
; флага переноса потеряно
Процессор 8086 может выполнять отдельные типы операций умножения и деления. Эта одна из сильных сторон процессора, поскольку во многих микропроцессорах вообще отсутствует непосредственная поддержка операций умножения и деления, а эти операции довольно сложно выполнить программным путем.
Инструкция MUL перемножает 8- или 16-битовые беззнаковые сомножители, создавая 16- или 32-битовое произведение.
При 8-битовом умножении один из операндов должен храниться в регистре AL, а другой может представлять собой любой 8-битовый общий регистр или переменную памяти соответствующего размера. Инструкция MUL всегда сохраняет 16-битовое произведение в регистре AX. Например, во фрагменте программы:
mov al,25
mov dh,40
mul dh
AL умножается на DH, а результат (1000) помещается в регистр AX.
Инструкция перемножения 16-битовых сомножителей работает аналогично. Один из сомножителей должен храниться в регистре AX, а другой может находиться в 16-разрядном общем регистре или в переменной памяти. 32-битовое произведение инструкция MUL помещает в этом случае в регистры DX:AX, при этом младшие (менее значащие) 16 битов произведения записываются в регистр AX, а старшие (более значащие) 16 битов - в регистр DX. Например, инструкции:
mov ax,1000
mul ax
загружают в регистр AX значение 1000, а затем возводят его в
квадрат, помещая результат (значение 1000000) в регистры DX:AX.
В отличие от сложения и вычитания, в операции умножения не учитывается, являются ли сомножители операндами со знаком или без знака, поэтому имеется вторая инструкция умножения IMUL для умножения 8- и 16-битовых сомножителей со знаком. Если не принимать во внимание, что перемножаются значения со знаком, инструкция IMUL работает аналогично инструкции MUL. Например, при выполнении инструкций:
mov al,-2
mov ah,10
imul ah
в регистре AX будет записано значение -20.
Процессор позволяет с определенными ограничениями разделить 32-битовое значение на 16-битовое, или 16-битовое значение на 8-битовое. Сначала рассмотрим деление 16-битового значения на 8-битовое.
При беззнаковом делении 16-битового значения на 8-битовое делимое должно быть записано в регистре AX. 8-битовый делитель может храниться в любом 8-битовом общем регистре или переменной в памяти соответствующего размера. Инструкция DIV всегда записывает 8-битовое частное в регистр AL, а 8-битовый остаток - в AH. Например, в результате выполнения инструкций:
mov ax,51
mov dl,10
div dl
результат 5 (51/10) будет записан в AL, а остаток 1 (остаток от
деления 51/10) - в AH. Частное представляет собой 8-битовое значение. Это означает, что результат деления 16-битового операнда на 8-битовый операнд должен превышать 255. Если частное слишком велико, то генерируется прерывание 0 (прерывания по делению на 0). Инструкции:
mov ax,0fffh
mov bl,1
div bl
генерируют прерывание по делению на 0 (как можно ожидать, прерывание по делению на 0 генерируется также, если 0 используется в качестве делителя).
При делении 32-битового операнда на 16-битовый операнд делимое должно записываться в регистрах DX:AX. 16-битовый делитель может находиться в любом из 16-битовых регистров общего назначения или в переменной в памяти соответствующего размера. Например, в результате выполнения инструкций:
mov ax,2
mov dx,1 ; загрузить в DX:AX 10002h
mov bx,10h
div bx
частное 1000h (результат деления 10002h на 10h) будет записано в регистре AX, а 2 (остаток от деления) - в регистре DX.
Как и при умножении, при делении имеет значение, используются операнды со знаком или без знака. Для деления беззнаковых операндов используется операция DIV, а для деления операндов со знаком - IDIV. Например, операции:
.DATA
TestDivisor DW 100
.CODE
mov ax,-667
cwd ; установить DX:AX в значение -667
idiv [TestDivisor]
сохраняют значение -6 в регистре AX и значение -67 в регистре DX.