一. abc 指令
adc (add carry)是带向假想的更高位进位加法指令,它利用了标志寄存器上 CF 标志位记录的进位值。
指令格式:adc 操作对象1, 操作对象2
功能:操作对象1 = 操作对象1 + 操作对象2 + CF
比如,指令:adc ax, bx ;(ax) = (ax) + (bx) + CF
CPU 提供 adc 指令的目的是用 adc 指令和 add 指令相配合,从而可以对更大的数据进行加法运算。
比如,以下指令实现的效果和 add ax, bx 是一样的
add al, bl ;
adc ah, bh ;(ah) = (ah) + bh + CF (CF的值为 add al, bl 指令执行后,设置的值)
比如,ax = 0191h,bx = 0283h
add ax, bx ;(ax) = (ax) + (bx) = 0191h + 0283h = 0414h
用以下指令进行计算实现的效果是一样的
add al, bl ;(al) = (al) + (bl) = 91h + 83h = 14h, CF = 1(向更高位进位了,所以 CF = 1)
adc ah, bh ;(ah) = (ah) + (bh) + CF = 01h + 02h + 1 = 04h
最后,ax = 0414h
1. 编程,计算 1EF000h + 201000h,结果放在 ax(高16位)和 bx(低16位)中
(1)分析
因为两个数都大于16位,只用 add 指令无法进行计算,所以,应该用 add 指令和 add 指令配合,进行更大数据的加法运算。
mov bx, 0F000h ;低16位
add bx, 1000h ;低16位, (bx) = (bx) + 1000h = 0F000h + 1000h = 0000h, CF = 1
mov ax, 001Eh ;高16位,mov是传送指令,不会对标志寄存器产生影响
adc ax, 0020h ;高16位,(ax) = (ax) + 0020h + CF = 001Eh + 0020h + 1 = 003Fh
(2)代码
;编程,计算 1EF000h + 201000h,结果放在 ax(高16位)和 bx(低16位)中
assume cs:code
code segment
start:
mov bx, 0F000h ;低16位
add bx, 1000h ;低16位
mov ax, 001Eh ;高16位,mov是传送指令,不会对标志寄存器产生影响
adc ax, 0020h ;高16位,(ax) = (ax) + 0020h + CF = 001Eh + 0020h + 1 = 003Fh
mov ax, 4c00h
int 21h
code ends
end start
2. 写一个子程序,对两个 128 位数据进行相加
名称:add128
功能:两个 128 位数进行相加
参数:ds:si 指向存储第一个数的内存空间,128 位数,需要 8 个字单元,由低地址单元到高地址单元依次存放 128 位数据从低到高的 8 个字单元。运算结果存储在第一个数的存储空间中。ds:di 指向存储第二个数的内存空间。
返回:无
(1)分析
因为两个数都大于16位,用 add 指令无法进行计算,所以,应该用 adc 指令,进行更大数据的加法运算。
(2)代码
assume cs:code, ds:data
data segment
dw 8888h,7777h,6666h,5555h,4444h,3333h,2222h,1111h
dw 8888h,7777h,6666h,5555h,4444h,3333h,2222h,1111h
data ends
code segment
start:
;初始化数据段
mov ax, data
mov ds, ax
mov si, 0 ;指向第一个数的首地址
mov di, 16 ;指向第二个数的首地址
call add128
mov ax, 4c00h
int 21h
;名称:add128
;功能:两个 128 位数进行相加
;参数:ds:si 指向存储第一个数的内存空间,128 位数,需要 8 个字单元,由低地址单元到高地址单元
; 依次存放 128 位数据从低到高的 8 个字单元。运算结果存储在第一个数的存储空间中。ds:di 指向存储第二个数的内存空间。
;返回:无
add128:
push bp ;保护原bp状态
mov bp, sp ;设置栈帧的栈顶基址
;保护子程序用到的原寄存器状态
push ax
push cx
push si
push di
sub ax, ax ;将CF设置为0
mov cx, 8 ;8个字单元
s:
mov ax, [si] ;mov是传送指令,不影响标志寄存器的状态
adc ax, [di] ;(ax) = (ax) + ds:[di] + CF
mov [si], ax
inc si ;inc指令不影响标志寄存器的状态,不能用 add si, 2 这条指令替代 inc si; inc si
inc si
inc di
inc di
loop s
pop di
pop si
pop cx
pop ax
pop bp
ret
code ends
end start
在子程序的循环中
第一个字单元相加时:
mov ax, 8888h
adc ax, 8888h ;ax = 8888h + 8888h + CF = 1110h + 0 = 1110h,CF = 1
第二个字单元相加时:
mov ax, 7777h
adc ax, 7777h ;ax = 7777h + 7777h + CF = 7777h + 7777h + 1 = EEEFh, CF = 0
第三个字单元相加后:ax = CCCCh, CF = 0
第四个字单元相加后:ax = AAAAh, CF = 0
第五个字单元相加后:ax = 8888h, CF = 0
第六个字单元相加后:ax = 6666h, CF = 0
第七个字单元相加后:ax = 4444h, CF = 0
第八个字单元相加后:ax = 2222h, CF = 0
所以,结果为:2222 4444 6666 8888 AAAA CCCC EEEF 1110h,跟截图中的子程序执行后的结果一致。
二. sbb 指令
sbb (sub borrow)是带向假想的更高位借位减法指令,它利用了标志寄存器上 CF 标志位记录的进位值。
指令格式:sbb 操作对象1, 操作对象2
功能:操作对象1 = 操作对象1 - 操作对象2 - CF
比如,指令:sbb ax, bx ;(ax) = (ax) - (bx) - CF
CPU 提供 sbb 指令的目的是用 sbb 指令和 sub 指令相配合,从而可以对更大的数据进行减法运算。
比如,以下指令实现的效果和 sub ax, bx 是一样的
sub al, bl ;
sbb ah, bh ;(ah) = (ah) - bh - CF (CF的值为 sub al, bl 指令执行后,设置的值)
比如,ax = 0001h,bx = 0002h
sub ax, bx ;(ax) = (ax) - (bx) = 0001h - 0002h = FFFFh,CF = 1
用以下指令进行计算实现的效果是一样的
sub al, bl ;(al) = (al) - (bl) = 01h - 02h = FFh, CF = 1(向假想的更高位借位了,所以 CF = 1)
sbb ah, bh ;(ah) = (ah) - (bh) - CF = 00h - 00h - 1 = FFh,CF = 1
最后,ax = FFFFh
1. 编程,计算 201000h - 1EF000h,结果放在 ax(高16位)和 bx(低16位)中
(1)分析
因为两个数都大于16位,只用 sub 指令无法进行计算,所以,应该用 sub 指令和 sbb 指令配合,进行更大数据的减法运算。
mov bx, 1000h ;低16位
sub bx, 0F000h ;低16位, (bx) = (bx) - 0F000h = 1000h - 0F000h = 2000h, CF = 1
mov ax, 0020h ;高16位,mov是传送指令,不会对标志寄存器产生影响
sbb ax, 001Eh ;高16位,(ax) = (ax) - 001Eh - CF = 0020h - 001Eh - 1 = 0001h,CF = 0
(2)代码
;编程,计算 201000h - 1EF000h,结果放在 ax(高16位)和 bx(低16位)中
assume cs:code
code segment
start:
mov bx, 1000h ;低16位
sub bx, 0F000h ;低16位, (bx) = (bx) - 0F000h = 1000h - 0F000h = 2000h, CF = 1
mov ax, 0020h ;高16位,mov是传送指令,不会对标志寄存器产生影响
sbb ax, 001Eh ;高16位,(ax) = (ax) - 001Eh - CF = 0020h - 001Eh - 1 = 0001h,CF = 0
mov ax, 4c00h
int 21h
code ends
end start
tips:
CF(Carry Flag):进位标志位,它一般记录了在做加法运算时,从最高有效位向更高位进位了,或做减法运算时,最高有效位向更高位借位了。
CY(Carried Yes)= 1:有进位或有借位
NC(Not Carried)= 0:没进位或没借位
2. 写一个子程序,对两个 128 位数据进行相减
名称:sub128
功能:两个 128 位数进行相减
参数:ds:si 指向存储第一个数的内存空间,128 位数,需要 8 个字单元,由低地址单元到高地址单元依次存放 128 位数据从低到高的 8 个字单元。运算结果存储在第一个数的存储空间中。ds:di 指向存储第二个数的内存空间。
返回:无
(1)分析
因为两个数都大于16位,用 sub 指令无法进行计算,所以,应该用 sbb 指令,进行更大数据的减法运算。
(2)代码
assume cs:code, ds:data
data segment
dw 8888h,7777h,6666h,5555h,4444h,3333h,2222h,1111h
dw 9999h,7777h,6666h,5555h,4444h,3333h,2222h,1111h
data ends
code segment
start:
;初始化数据段
mov ax, data
mov ds, ax
mov si, 0 ;指向第一个数的首地址
mov di, 16 ;指向第二个数的首地址
call sub128
mov ax, 4c00h
int 21h
;名称:sub128
;功能:两个 128 位数进行相减
;参数:ds:si 指向存储第一个数的内存空间,128 位数,需要 8 个字单元,由低地址单元到高地址单元
; 依次存放 128 位数据从低到高的 8 个字单元。运算结果存储在第一个数的存储空间中。ds:di 指向存储第二个数的内存空间。
;返回:无
sub128:
push bp ;保护原bp状态
mov bp, sp ;设置栈帧的栈顶基址
;保护子程序用到的原寄存器状态
push ax
push cx
push si
push di
sub ax, ax ;将CF设置为0
mov cx, 8 ;8个字单元
s:
mov ax, [si] ;mov是传送指令,不影响标志寄存器的状态
sbb ax, [di] ;(ax) = (ax) - ds:[di] - CF
mov [si], ax
inc si ;inc指令不影响标志寄存器的状态,不能用 add si, 2 这条指令替代 inc si; inc si
inc si
inc di
inc di
loop s
pop di
pop si
pop cx
pop ax
pop bp
ret
code ends
end start
在子程序的循环中
第一个字单元相减时:
mov ax, 8888h
sbb ax, 9999h ;ax = 8888h - 9999h - CF = EEEFh - 0 = EEEFh,CF = 1
第二个字单元相减时:
mov ax, 7777h
sbb ax, 7777h ;ax = 7777h - 7777h - CF = 0h - 1 = FFFFh,CF = 1
。。。
。。。
第八个字单元相减时:
mov ax, 1111h
sbb ax, 1111h ;ax = 1111h - 1111h - CF = 0h - 1 = FFFFh, CF = 1
所以,结果为:0FFFF FFFF FFFF FFFF FFFF FFFF FFFF EEEFh,跟截图中的子程序执行后的结果一致。
参考书籍:
《汇编语言(第4版)》- 王爽