吾爱汇编

 找回密码
 立即注册

QQ登录

绑定QQ避免忘记帐号

查看: 3777|回复: 73

[汇编] 8086汇编-算数运算指令

  [复制链接]
啥都不会 发表于 2022-5-8 10:42 | 显示全部楼层 |阅读模式

本帖最后由 啥都不会 于 2022-5-9 09:24 编辑

本帖最后由 啥都不会 于 2022-5-8 11:07 编辑

本帖最后由 啥都不会 于 2022-5-8 10:45 编辑

本帖最后由 啥都不会 于 2022-5-8 10:43 编辑

前言:

这篇笔记主要学习算数运算指令。

写汇编代码比较讲究细节,每个人思路不一样,写的代码自然也是不一样的。所以,建议大家先看题目自己验证写一遍代码,后续再看别人写的代码,并做好代码优化。

编程语言:

汇编语言

以下为主题内容:

笔记参考 "杨季文《80x86汇编语言程序设计》"

加法指令

普通加法指令(ADD)

普通加法指令格式如下:

ADD OPRD1, OPRD2

这条指令两个操作数相加,结果送至目的操作数 OPRD1,即:

OPRD1<== OPRD1 + OPRD2

ADD 指令对各标志位的影响:

1

2

3

带进位加法指令(ADC)

带进位加法指令格式如下:

ADC OPRD1, OPRD2

这条指令与ADD指令类似,完成两个操作数相加,但还要把进位标志CF的值加上去,把结果传送至目的操作数 OPRD1 即:

OPRD1 <== OPRD1 + OPRD2 + CF

ADC指令的运用:

  1. 验证 ADC 运算指令
mov ax,2
mov bx,1
sub bx,ax
adc ax,1

4

  1. 计算超过 16 位数的加法
;计算 ffffh+1 的值
;DX:AX(高16位:低16位)
mov ax,ffff
add ax,1
adc dx,0

5

;计算 1ef000h+201000h,结果存放在 AX 高 16 位,和 BX 低 16 位。
mov ax,001eh
mov bx,f000h
add bx,1000h
adc ax,0020h

6

加1指令(INC)

加 1 指令格式如下:

INC OPRD

这条指令完成对操作数 OPRD 加 1,然后把结果送回OPRD,即:

OPRD <== OPRD+1

例如:

inc al
inc VARB

操作数可以是通用寄存器,也可以是存储单元。这条指令执行的结果影响标志 ZF、SF、OF、PF 和 AF,但不影响 CF。

  1. 验证 INC 指令不影响 CF 位

9

INC 指令确实不会影响 CF 位,那为什么会这样呢?

个人认为:因为 INC 指令通常用在循环语句中,且主要用于偏移地址的运算,偏移地址加 1 严格意义上来说并不属于算数运算指令,所以没有必要把 CF 位置为 1。如果再循环中 INC 指令把 CF 位置 1 了,这样会影响到算数运算指令的结果。

7

8

减法指令

普通减法指令(SUB)

普通减法指令格式如下:

sub OPRD1, OPRD2

这条指令完成两个操作数相减,从 OPRD1 中减去 OPRD2,结果送到目标操作数 OPRD1 中,即:

OPRD1<== OPRD1 - OPRD2

SUB 指令对各标志位的影响:

10

11

带进(借)位减法指令(SBB)

带进(借)位减法指令:

SBB OPRD1, OPRD2

这条指令与sub指令类似,再操作数 OPRD1 减去操作数 OPRD2 的同时好药减借位(进位)标志 CF 位的值,即:

OPRD1<== OPRD1-OPRD2-CF

例如:计算 003E1000H-00202000H,结果放在AX:BX中

mov bx,1000h
mov ax,003eh
sub bx,2000h
sbb ax,0020h

12

减 1 指令(DEC)

减1指令格式如下:

DEC OPRD

这条指令把操作数 OPRD 减 1,并把结果送回 OPRD,即:

OPRD <== OPRD-1

DEC 指令和 INC 的作用一样,且不会影响 CF 位。

取补指令(NEG)

13

14

比较指令(CMP)

15

乘法指令

在乘法指令中,被乘数是一个隐含的操作数AL(8位数相乘)或者 AX(16位数相乘)。乘数可以用除立即数以外的任何一种寻址方式。

无符号数乘法指令(mul)

无符号数指令格式:

MUL OPRD

  1. 如果 OPRD 是 8 位的字节操作数,则 AL*OPRD ,16 位结果送到 AX 中
  2. 如果 OPRD 是 16 位的字操作数,则 AX*OPRD,32位结果传送到 DX:AX 中,DX 为高 16 位,AX 为低 16 位。

例如:

MUL BL
MUL AX
MUL VARW

如果乘积结果的高半部分(字节相乘时为 AH,字相乘时为 DX)不等于零,则标志位 CF=1,OF=1;否则CF = 0,OF = 0

有符号数乘法指令(imul)

有符号数指令格式:

IMUL OPRD

  1. 如果 OPRD 是 8 位的字节操作数,则 AL*OPRD ,16 位结果送到 AX 中
  2. 如果 OPRD 是 16 位的字操作数,则 AX*OPRD,32位结果传送到 DX:AX 中,DX 为高 16 位,AX 为低 16 位。

例如:

IMUL CL
IMUL DX
IMUL VARW

如果乘积结果的高半部分(字节相乘时为 AH,字相乘时为 DX)不是低半部分的符号扩展,则标志位 CF=1,OF=1;否则CF = 0,OF = 0

除法指令

在除法指令中,被除数是一个隐含的操作数AX(除数是8位数)或者 DX:AX(除数是16位数)。除数可以用除立即数以外的任何一种寻址方式。

无符号数除法指令(div)

16

有符号数除法指令(idiv)

17

符号扩展指令

由于除法指令隐含使用字被除数或双字被除数,所以在做除法操作之前需要先将 AH 或者 DX 重置,避免出现运算错误。为此8086专门提供了符号扩展指令。

字节转换为字指令(CBW)

18

字节转换为双字指令(CWD)

19

20

  1. 除数为0

    22

  2. 除以8位,商超过 8 位,出现除法溢出。

    21

代码练习

分为两部分:完善代码片段+练习相关算数运算指令

add.asm

;程序名:add.asm
;功能:计算 1234:5678H 开始的 100 个无符号数的和。把32位的和保存在 DX(高位)和 AX 寄存器中。

assume cs:code,ds:data

;增加一个变量来存储结果
data segment
result dd ?   ;定义一个未初始化的变量
data ends

code segment
start:
  mov ax,data
  mov ds,ax

  ;增加了ds 数据段,后面还要将结果存储到 ds:[result] 中
  ;所以最好用 es 来存储段值 1234h
  mov ax,1234h
  mov es,ax

  ;计算前做好初始化工作
  mov si,5678h
  mov ax,0

  ;初始化操作,还需要考虑哪些标志位需要初始化。
  ;如果该程序作为子程序使用,父程序的标志位可能会影响到运算结果
  ;例如这里 "无符号数进位加法运算" 需要考虑到 CF 位的初始化。
  ;mov dx,ax  ;mov 指令不会使 CF 标志位置0

  ;使用逻辑运算指令将 dx 置为0
  xor dx,dx
  mov cx,100
next:
  ;si 默认用的是 ds 寄存器,所以这里要加上段前缀
  add ax,es:[si]
  adc dx,0

  ;inc si
  ;inc si
  ;可替换为,因为这样写并不会影响到后面的指令执行。
  add si,2
  ;cx=cx-1,如果 cx 不为 0,就跳到 next 标号处。这是做了一个循环。
  dec cx
  jnz next

  ;两个数据类型不一样,需要指定大小
  mov word ptr result,ax
  ;一个地址对应一个字节,ax == 2字节,所以偏移地址要+2
  mov word ptr result+2,dx

  mov ax,4c00h
  int 21h
code ends
    end start

add.asm 优化

  1. 定义一个变量验证执行结果
  2. 增加 "复制字符串" 功能
  3. 最后运算结果在 DX:AX 中,并存储到变量 result 中
  4. 打印 result 变量中存储的结果
;程序名:add1.asm
;功能:计算 1234:5678H 开始的内存中的20个16位无符号整数的和
;注意有进位值,结果保存到 DX:AX,或者 result 变量中

;程序设计:
;1、自定义一个number,将 20 个 16 位无符号整数,复制到 1234:5678H 处,主要是为了验证代码执行结果是否正确。
;2、计算整数之和使用 add 指令,如果两数之和超过ax寄存器(CF 有进位),使用 ADC 指令将最高位存到DX中
;3、使用循环执行该段代码,直到运算结束。
;4、将 DX:AX 中的结果,存放到 result 变量中,result 定义为双字 dd。
;5、使用循环将 result 中的结果,以16进制打印出来

;====================================
assume cs:code,ds:data

data segment
number dw '10',0ABh,0ABh,'40','50','60','70','80','90','00','11','22','33','44','55','66','77','88','99','00'
result dd ?
buffer db 0,0
buffer1 dw 0,0
stop db '$'
data ends

code segment
start:
;复制字符串
;========================================
    mov ax,data
    mov ds,ax
    mov ax,1234h
    mov es,ax
    mov si,offset number
    mov bx,5678H
    mov di,bx
    mov cx,20
    rep movsw               ;si寄存器,默认使用ds段寄存器;di寄存器,使用 es 段寄存器


;计算结果,并存储到 result 中
;===============================================================
    mov ax,0
    mov dx,0
    mov bx,0
    mov di,5678h
    mov cx,20
sum:
;计算16位无符号整数
    clc
    add ax,word ptr es:[di]
    JB ADC1
ADC1:
;判断并计算进位
    adc dx,0
    inc di
    inc di
    dec cx
    jnz sum
;复制结果到result
    mov word ptr result,ax
    mov word ptr result+2,dx


;将结果以16进制显示
;===================================================================
    mov cx,4                    ;定义循环次数(每次循环打印1字节)
    push cx                     ;堆栈中保存循环次数
    mov bx,3                    ;定义要打印字符串的指针位置(从最高位开始)
print:
    xor ch,ch
    pop cx      
    push cx                     ;使用并保存循环次数
    xor ax,ax
    mov si,offset result        ;从高位开始取值
    mov al,byte ptr [si+bx]
    push ax

;计算高4位
    mov cl,4
    shr al,cl
    cmp al,0Ah                  ;如果高4位是字母就转换成字母字符
    jnb char1
    add al,30h                  ;如果高4位是数字就转换成数字字符
    mov buffer,al               ;此时4位扩展成8位,将结果存储到 buffer 的低8位
    jmp low1                    ;高4位计算结束,跳转到低4位计算
char1:
    add al,37h                  ;此时4位扩展成8位,将结果存储到 buffer 的低8位
    mov buffer,al

low1:
;计算低4位
    pop ax
    and al,0Fh
    cmp al,0Ah                  ;如果低4位是字母就转换成字母字符
    jnb char2
    add al,30h                  ;如果低4位是数字就转换成数字字符
    mov buffer+1,al             ;此时4位扩展成8位,将结果存储到 buffer 的高8位
    jmp loops
char2:
    add al,37h
    mov buffer+1,al             ;此时4位扩展成8位,将结果存储到 buffer 的高8位

;将字符以大端形式打印出来
loops:
;内循环,传输高16位
    dec bx                      ;循环一次,指针减1
    pop cx
    dec cx
    push cx                     ;计算并保存循环次数
    pushf
    test cx,01h
    jz loops1                   ;判断此次是内循环还是外循环
    ;
    mov ax,word ptr buffer
    mov buffer1,ax              ;把buffer中的高16位,传给 buffer1 的低16位
    ;
    popf
    jnz print                   ;判断循环次数是否等于0,不等于0,则继续循环

loops1:
;外循环传输低 16 位,并打印出来
    mov ax,word ptr buffer
    mov buffer1+2,ax            ;把buffer中的低16位,传给 buffer1 的高16位
    mov dx,offset buffer1
    mov ah,09h
    int 21h                     ;打印循环结果
    popf
    jnz print                   ;判断是否循环结束
    mov ax,4c00h
    int 21h
code ends
    end start

sub.asm

;程序名:sub.asm
;功能:两个 64 位数按照高高低低的原则分别存放到 DATA1 和 DATA2 两个缓冲区中
;计算 DATA1-DATA2
;DATA1 = 2000156781234
;DATA2 = 1000212345678

assume cs:code,ds:data

data segment
data1 dw 6AB2h,0B2A2h,01D1h,0
data2 dw 334Eh,0E14Dh,00E8h,0
result dd 0
data ends

code segment
start:
    mov ax,data
    mov ds,ax

    ;优化后程序片段
    xor si,si       ;16位汇编,数据默认从 ds:0 处开始存储
    mov cx,4

    ;源程序片段
    ;mov cx,4
    ;mov bx,6
sub1:
    ;mov si,offset data1
    ;mov di,offset data2
    ;mov ax,[si+bx]
    ;sbb ax,[di+bx]
    ;mov si,offset result
    ;mov word ptr [si+bx],ax


    ;编译器会将标号变成偏移地址,所以定位字符串的时候可使用 [si+标号] == 标号[si]
    mov ax,data1[si]
    ;在运算加法和减法(有符号)时要注意使用 adc 和 sbb 这种带进位和借位的运算指令。
    sbb ax,data2[si]
    mov word ptr result[si],ax
    ;这里使用 add,是因为cx判断在下面,并不影响 ZF 标志寄存器
    add si,2
    dec cx
    jnz sub1

    ;sub bx,2
    ;dec cx
    ;jnz sub1
    ;
    mov ax,4c00h
    int 21h
code ends
    end start

mul.asm

;程序名:mul.asm
;功能:乘法运算

;imul(有符号数乘法)、mul(无符号数乘法)
;两条指令都是对AL、AX 执行无符号数乘法操作
;=================================
assume cs:code,ds:data

data segment
val1 db 2
val2 db 9
val3 db 20
data ends

code segment
start:
    mov ax,data
    mov ds,ax
    ;8位数乘法会影响 AX,16位数乘法会影响 DX:AX,这点非常重要(特别时16位乘法运算)。
    ;也就是说在做16位乘法时,要少使用dx,不然会影响代码的结果。
    ;如果乘数是 8 位,被乘数在 al 中,积在 ax 中。
    mov al,val1
    mul val1
    mov bl,val2
    mul bl
    ;如果乘数是 16 位的,被乘数在 ax 中,积在 DX:AX 中。
    mov ah,1
    mov al,val3
    mov bx,10
    mul bx
    ;
    mov ax,word ptr val1
    mov cx,-1
    mul cx
    ;如果乘积扩展到 DX 中,则 CF、OF 置为 1,如果 DX=FFFF 说明这是符号位,CF、OF 为 0
    mov bx,word ptr val2
    mov ax,-1
    imul bx
    ;
    mov ax,4c00h
    int 21h
code ends
end start

div.asm

;程序名:div.asm
;功能:除法运算
;=================================
assume cs:code,ds:data

data segment
val1 db 2
val2 db 9
data ends

code segment
start:
    mov ax,data
    mov ds,ax
    ;
    mov al,val1
    mov bl,val2
    xor ah,ah           ;无符号数除8位数,使用 xor 指令扩展ah
    div bl
    ;
    mov ax,-1
    xor dx,dx           ;无符号数除16位数,使用 xor 指令扩展dx
    mov bx,word ptr val1
    div bx
    ;
    mov ax,-1
    cwd                 ;有符号数除16位数,使用 CWD 指令扩展符号位dx
    mov bx,word ptr val2
    idiv bx
    ;
    mov ax,4c00h
    int 21h

code ends
end satrt

summary.asm

算数运算代码总结

;程序名:summary.asm
;功能:算数运算总结。计算表达式:(X*Y+Z-1024)/75
;假设其中的X、Y和Z均为16位带符号数,分别存放在名为XXX、YYY和ZZZ的
;变量单元中。计算结果保存在 AX 中,余数保存在 DX 中。
;===================================================================

assume cs:code,ds:data

data segment
XXX dw 512
YYY dw -2
ZZZ dw 2203
chus dw 75
data ends

code segment
start:
    mov ax,data
    mov ds,ax

    mov ax,XXX
    ;mov bl,byte ptr YYY    ;错误 512=200h 是字大小
    ;imul bl
    imul YYY
    ;add 指令本身会对 CF 位初始化,所以不需要其他操作
    add ax,ZZZ
    ;加法可能出现进位,执行该指令之前还需要考虑CF位初始化
    adc dx,0
    ;减法要考虑借位
    sub ax,1024
    sbb dx,0
    ;有符号数除法考虑扩展符号位
    cwd
    idiv chus

    mov ax,4c00h
    int 21h

code ends
    end start

评分

参与人数 20HB +23 THX +12 收起 理由
浅酌◇咖啡 + 1 + 1
花盗睡鼠 + 1 [吾爱汇编论坛52HB.COM]-学破解防破解,知进攻懂防守!
消逝的过去 + 1
今日下雨 + 1
后学真 + 1 [吾爱汇编论坛52HB.COM]-吃水不忘打井人,给个评分懂感恩!
风里去 + 1 [吾爱汇编论坛52HB.COM]-学破解防破解,知进攻懂防守!
飞刀梦想 + 1
数码光标 + 2 + 1
微熊猫 + 1
Wayne + 1 [吾爱汇编论坛52HB.COM]-吃水不忘打井人,给个评分懂感恩!
fjgh + 2 + 1 [吾爱汇编论坛52HB.COM]-学破解防破解,知进攻懂防守!
虚伪的笑容 + 1
今天去 + 2 + 1
小菜虫 + 1 [吾爱汇编论坛52HB.COM]-感谢楼主热心分享,小小评分不成敬意!
落雪玉 + 1
xgbnapsua + 2
徐闯 + 1
莣孒嬡沵芣蓜 + 6 + 1 楼主这类电子书有嘛?
zxjzzh + 2 [吾爱汇编论坛52HB.COM]-软件反汇编逆向分析,软件安全必不可少!
kkk1l + 1

查看全部评分

吾爱汇编论坛-学破解,防破解!知进攻,懂防守!逆向分析,软件安全!52HB.COM
iytwfx01 发表于 2022-5-8 10:42 | 显示全部楼层
吾爱汇编论坛-学破解,防破解!知进攻,懂防守!逆向分析,软件安全!52HB.COM
yRAEnGO34 发表于 2022-5-8 10:43 | 显示全部楼层

感谢楼主
吾爱汇编论坛-学破解,防破解!知进攻,懂防守!逆向分析,软件安全!52HB.COM
osLHEYQ3768 发表于 2022-5-8 10:43 | 显示全部楼层
吾爱汇编论坛-学破解,防破解!知进攻,懂防守!逆向分析,软件安全!52HB.COM
zwVqG0972 发表于 2022-5-8 10:45 | 显示全部楼层

谢谢分享
吾爱汇编论坛-学破解,防破解!知进攻,懂防守!逆向分析,软件安全!52HB.COM
GSkulFOjfAW 发表于 2022-5-8 10:47 | 显示全部楼层
吾爱汇编论坛-学破解,防破解!知进攻,懂防守!逆向分析,软件安全!52HB.COM
wLlqmUDo49 发表于 2022-5-8 10:55 | 显示全部楼层

大佬无敌
吾爱汇编论坛-学破解,防破解!知进攻,懂防守!逆向分析,软件安全!52HB.COM
DGY24736 发表于 2022-5-8 10:58 | 显示全部楼层

膜拜大神!
吾爱汇编论坛-学破解,防破解!知进攻,懂防守!逆向分析,软件安全!52HB.COM
mSYxEdBpL 发表于 2022-5-8 11:00 | 显示全部楼层

不知道来晚了没有
吾爱汇编论坛-学破解,防破解!知进攻,懂防守!逆向分析,软件安全!52HB.COM
eoCqP 发表于 2022-5-8 11:02 | 显示全部楼层

谢谢分享
吾爱汇编论坛-学破解,防破解!知进攻,懂防守!逆向分析,软件安全!52HB.COM
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

警告:本站严惩灌水回复,尊重自己从尊重他人开始!

1层
2层
3层
4层
5层
6层
7层
8层
9层
10层

免责声明

吾爱汇编(www.52hb.com)所讨论的技术及相关工具仅限用于研究学习,皆在提高软件产品的安全性,严禁用于不良动机。任何个人、团体、组织不得将其用于非法目的,否则,一切后果自行承担。吾爱汇编不承担任何因为技术滥用所产生的连带责任。吾爱汇编内容源于网络,版权争议与本站无关。您必须在下载后的24个小时之内,从您的电脑中彻底删除。如有侵权请邮件或微信与我们联系处理。

站长邮箱:SharkHeng@sina.com
站长QQ:1140549900


QQ|RSS|手机版|小黑屋|帮助|吾爱汇编 ( 京公网安备11011502005403号 , 京ICP备20003498号-6 )|网站地图

Powered by Discuz!

吾爱汇编 www.52hb.com

快速回复 返回顶部 返回列表