汇编实验
关于debug
-t逐步调试,注意各个寄存器的值即可
实验1
编写一个累计加法,从 1 加到 5,将结果保存至 AX 中。
assume cs:cseg,ds:data
data segment
infor1 db 'welcome to work1!'
data ends
cseg segment
start:mov dx,offset infor1;offsert返回的是infor1的偏移地址
mov ax,1234h
mov ds,ax
mov bx,0005h
mov ax,0
mov dx,1
mov cx,5
s:add ax,dx
inc dx;(dx)++
loop s
mov ds:[bx],ax;程序返回
mov ax,4c00H;程序返回
INT 21H
cseg ends
end start
实验2
编写一个累计减法,被减数是 10011000B,减数是 01000000B,连续减 5 次, 观察 FLAGS 的变化
assume cs:cseg,ds:data
data segment
db 'welcome to masm!'
data ends
cseg segment
start:mov ax,1234h
mov ds,ax
mov bx,0005h
mov ax,10011000B
mov dx,01000000B
mov cx,5
s:sub ax,dx
loop s
mov ds:[bx],ax
mov ax,4c00H
INT 21H
cseg ends
end start
98-40-…减到0时从FFFF再开始减
标识寄存器flags:
有符号溢出标志位OF(Over flow flag) OV(1) NV(0)
方向标志位DF(Direction flag) DN(1) UP(0)
中断标志位IF(Interrupt flag) EI(1) DI(0)
符号标志位SF(Sign flag) NG(1) PL(0)
零标志位ZF(Zero flag) ZR(1) NZ(0)
辅助进位标志位AF(Auxiliary carry flag) AC(1) NA(0)
奇偶标志位PF(Parity flag) PE(1) PO(0)
无符号进位标志位CF(Carry flag) CY(1) NC(0)
实验3
编写一个 16 位的乘法,被乘数是 100H,乘数是 100H,观察 Flags 的变化
assume cs:cseg,ds:data
data segment
db 'welcome to masm!'
data ends
cseg segment
start:mov ax,1234h
mov ds,ax
mov bx,0005h
mov ax,0
mov dx,100H
mov cx,100H
s:add ax,dx
loop s
mov ax,4c00H
INT 21H
cseg ends
end start
编写一个 32 位的乘法,被乘数是 0F0FH,乘数是 FF00H,观察 Flags 的变化
assume cs:ccode, ds:ddata
ddata segment
x1 dw 0F0FH ;被乘数低四位
x2 dw 0000H ;被乘数高四位
y1 dw 0FF00H ;乘数低四位
y2 dw 0000H ;乘数高四位
xy dw 4 dup (?) ;dup重复定义指令,dw为定义的类型,开辟出4个字的单元空间
ddata ends
ccode segment
start:mov ax,ddata
mov ds,ax
mov ax,x1
mov dx,y1
mul dx
mov [xy],ax
mov [xy+2],dx ;被乘数低位4字符x1和乘数低位4字符y1相乘结果低位存入xy,高位存入xy+2
mov ax,x2
mov dx,y1
mul dx
add [xy+2],ax
adc [xy+4],dx ; 被乘数高位4字符x2和乘数低位4字符y1相乘结果低位存入xy+2,高位存入xy+4
mov ax,x1
mov dx,y2
mul dx
add [xy+2],ax
adc [xy+4],dx
adc [xy+6],0 ; 被乘数低位4个字符x1和乘数高位4个字符y2相乘结果低位存入xy+2,高位存入xy+4
mov ax,x2
mov dx,y2
mul dx
add [xy+4],ax
adc [xy+6],dx ; 被乘数高位4个字符x2和乘数高位4个字符y2相乘结果低位存入xy+4,高位存入xy+6
mov ah,4ch
int 21h
ccode ends
end start
实验4
编写一个 16 位的除法,被除数是 100H,除数是 100H,观察 Flags 的变化
assume cs:cseg,ds:data
data segment
db 'welcome to masm!'
data ends
cseg segment
start:mov ax,1234h
mov ds,ax
mov bx,0005h
mov ax,100H
mov bx,100H
div bx
mov ax,4c00H
INT 21H
cseg ends
end start
编写一个 32 位的除法,被除数是 0F0FH,除数是 00FFH,观察 Flags 的变化
借鉴TeaMakCoder思想:https://blog.csdn.net/Fgoodboy/article/details/109067736
这里需要用到一个公式:
X/N = int(H/N)*65536 + [rem(H/N)*65536+L]/N -----------------------公式①
X:被除数
N:除数
H:被除数X的高16位
L:被除数X的低16位
int()取商
rem()取余数
给出示例参数:
(ax)寄存器储存被除数的低16位 mov ax,4240h
(dx)寄存器储存被除数的高16位 mov dx,000fh
(cx)寄存器储存16位的除数 mov cx,000ah
除法结果的高16位储存在(dx)中,低16位储存在(ax)中,余数储存在(cx)中
仔
仔细观察公式①的第一项:
int(H/N)*65536
int(H/N):被除数的高16位/除数,这里将H拓展为一个32位的数(0000xxxxh),由于其高16位全为0,所以H/N的商一定不会溢出(超过16位)。
那乘以65536应该怎么做呢?我们只需要记得将H/N的商放到一个代表高16位的寄存器中就可以了,并不需要真的乘65536(事实上*10000h也无法简单地用16位乘法实现)。
再看公式②的第二项:
[rem(H/N)*65536+L]/N
rem(H/N)已经在第一项除法中得到了,假设 X = rem(H/N),接下来实现(X+L)/N,显然也是一个32位数/16位数的除法。但这里存在一个问题:如何保证除法的商不会溢出呢?
在这里简单的证明一下:
因为 X 是 H/N 的余数,所以 X<=N-1 (X不可能大于N),L是被除数的低16位,不妨令 X=N-1,L=FFFFh。只需证明
[(N-1)|FFFFh]/N<10000h
(((N-1)|FFFFh)以N-1为高16位,以FFFF为低16位。比如N-1 = FFFE,则分子为FFFEFFFFh)。
原式=[(N-1)*65536 + 65535]/N=65536 - 1/N < 10000h
assume ds:datasg,ss:stacksg,cs:codesg
datasg segment
datasg ends
stacksg segment
dw 0,0,0,0,0,0,0,0
stacksg ends
codesg segment
start:mov ax,stacksg
mov ss,ax
mov sp,16
mov ax,0000h
mov dx,0F0Fh
mov cx,0FF00h
call divdw
mov ax,4c00h
int 21h
;参考公式X/N=int(H/N)*65536+[rem(H/N)*65536+L]/N
;可以把该32位/16位的除法视作2个伪16除法,高位(dx)中存的是0
;首先低四位除以除数,得到16位的商,这个商就是高16位结果(为什么?)
;上一步的余数左移16位后加上低16位构成32位数
;尝试证明第二步除法不会溢出:
;rem<=N-1,L<=0FFFFh
;res = (N-1)|0FFFFh/N = [65535+(N-1)*65536]/N = 65536-1/N
;1<=N<=FFFFh,所以res<=FFFF(极端情况下也不会溢出)
divdw:push bx;防止寄存器冲突覆盖原有的值
push ax;低16位进栈
mov ax,dx;32位/16位
mov dx,0
div cx;int(H/N)*65536,商AX,余数DX,商AX就是最终结果的高16位
mov bx,ax;bx保存高16位商
pop ax;低16位送入ax中
push bx
div cx;[rem(H/N)*65536+L]/N,低16位商AX不用动,余数DX
mov cx,dx;cx中保存的是余数
pop dx;dx中是高16位商
pop bx;恢复寄存器
ret
codesg ends
end start
实验5
编写一个累计加法,被加数是 0FH,加数是 01H,观察 Flags 的变化,被加数是 0FFH,加数是 01H,观察 Flags 的变化,被加数是 0FFFH,加数是 01H, 观察 Flags 的变化,被加数是 FFFFH,加数是 01H,观察 Flags 的变化,被 加数是 FFFFFFFFH 加数是 01H,观察 Flags 的变化
assume cs:cseg,ds:data
data segment
infor1 db 'welcome to work1!'
data ends
cseg segment
start:mov dx,offset infor1
mov ax,1234h
mov ds,ax
mov bx,0005h
mov ax,0FH
mov dx,01H
add ax,dx
mov ds:[bx],ax
mov ax,4c01H
mov ax,0FFH
mov dx,01H
add ax,dx
mov ds:[bx],ax
mov ax,4c02H
mov ax,0FFFH
mov dx,01H
add ax,dx
mov ds:[bx],ax
mov ax,4c03H
mov ax,0FFFFH
mov dx,01H
add ax,dx
mov ds:[bx],ax
mov ax,4c04H
mov ax,0FFFFH
mov bx,0FFFFH
add bx,0001H
adc ax,0000H ;带进位的加法,将CF一起加进去
mov ax,4c05H
mov ax,4c00H
INT 21H
cseg ends
end start
实验6
编写一个移位运算,将 8F1DH 存至 AX,然后用指令右移 1 位然后左移 1 位, 显示结果并观察 Flags 的变化。将 8F1DH 存至 AX 中,然后带 CF 位左移 5 位,并右移 7 位,观察 Flags 的变化,并给出结果
assume cs:cseg,ds:data
data segment
db 'welcome to masm!'
data ends
cseg segment
start:mov ax,1234h
mov ds,ax
mov bx,0005h
mov ax,8F1DH
sar ax,1 ;算数右移
sal ax,1 ;算术左移
mov ax,4c01H
mov ax,8F1DH
mov cx,5
loop1:
rol ax,1 ;循环左移
loop loop1
mov cx,7
loop2:
sar ax,1
loop loop2
mov ax,4c02H
mov ax,4c00H
INT 21H
cseg ends
end start
实验7
将 71D2H 存至 AX 中,5DF1H 存至 CX 中,DST 为 AX,REG 为 AX,实现双精度右移 2 次,交换 DST 与 REG,然后左移 4 次,分别查看结果
这里本来用的是masm来写,发现masm识别不了shrd和shld
assume cs:cseg,ds:data
data segment
db 'welcome to masm!'
data ends
cseg segment
start:mov ax,1234h
mov ds,ax
mov bx,0005h
mov ax,71D2H
mov bx,5DF1H
mov cx,2
l1: sar bx,1
sar ax,cl
loop l1
mov ax,5DF1H
mov bx,71D2H
mov cx,4
l2: sar bx,1
sar ax,cl
loop l2
mov ax,4c00H
INT 21H
cseg ends
end start
实验8
实现压缩BCD码的加减法,用压缩BCD码实现(21+71),(12+49),(65+82), (46-33),(74-58),,(43-54)的十进制加减法。然后又用非压缩 BCD 实现 上述 6 个式子。
assume cs:cseg,ds:data
data segment
infor1 db 'welcome to work1!'
data ends
cseg segment
start:mov dx,offset infor1
mov ax,1234h
mov ds,ax
mov bx,0005h
;压缩BCD码
;-------------------
;21+71
mov al,21h
mov bl,71h
add al,bl
daa ;压缩BCD码的调整指令
;12+49
mov al,12h
mov bl,49h
add al,bl
daa
;65+82
mov al,65h
mov bl,82h
add al,bl
daa
;46-33
mov al,46h
mov bl,33h
sub al,bl
das
;74-58
mov al,74h
mov bl,58h
sub al,bl
das
;43-54
mov al,43h
mov bl,54h
sub al,bl
das
;非压缩BCD码
;-------------------
;21+71
mov ax,0201H
mov bx,0701H
add ax,bx
aaa ;非压缩BCD码调节指令
;调整方法与AAA指令类似,不同的是DAA指令要分别考虑AL的高4位和低4位。
;如果AL的低4位大于9或AF=1,则AL的内容加06H,并将AF置1;然后如果AL的高4位大于9或CF=1,则AL的内容加60H,且将CF置1。如果两个都不满足,则将AF,CF清零。
;12+49
mov ax,0102H
mov bx,0409H
add ax,bx
aaa ;AAA为非压缩BCD码调整,即如果al低四位大于9,就将al加6,ah加一,al高四位清零,cf、af置1。
;65+82
mov ax,0605h
mov bx,0802h
add ax,bx
aaa
;46-33
mov ax,0406h
mov bx,0303h
sub ax,bx
aas
;74-58
mov ax,0704h
mov bx,0508h
sub ax,bx
aas
;43-54
mov ax,0403h
mov bx,0504h
sub ax,bx
aas
mov ax,4c00H
INT 21H
cseg ends
end start
实验9
实现 KMP 算法,输入两个字符串(可以直接保存在内存中),实现快速匹配
DATAS SEGMENT
;此处输入数据段代码
mess1 DB 'Enter keyword:','$'
mess2 DB 'Enter Sentence:','$'
mess3 DB 'Match at location:','$'
mess4 DB 'NOT MATCH.',13,10,'$'
mess5 DB 'H of the sentence',13,10,'$'
change DB 13,10,'$'
stoknin1 label byte ;LABEL可以使同一个变量具有不同的类型属性,其中变量的数据类型可以是BYTE,WORD,DWORD
max1 db 10 ;关键字大小
act1 db ? ;记录
stokn1 db 10 dup(?)
stoknin2 label byte
max2 db 50 ;字符串大小
act2 db ? ;记录
stokn2 db 50 dup(?)
DATAS ENDS
STACKS SEGMENT
STACKS ENDS
CODES SEGMENT
ASSUME CS:CODES,DS:DATAS,SS:STACKS
START:
MOV AX,DATAS
MOV DS,AX
LEA DX,mess1
MOV ah,09
INT 21h ;输出Enter keyword
LEA DX,stoknin1 ;lea,取有效地址
MOV ah,0ah ;用21号中段的0ah号功能获取关键字
INT 21h
cmp act1,0
je exit ;如果为空直接退出程序
a10:
;输入Sentence并判断
LEA DX,change
MOV ah,09
INT 21h ;输出回程,换行
LEA DX,mess2
MOV ah,09
INT 21h
;输出Enter Sentence:
LEA DX,stoknin2
MOV ah,0ah
INT 21h
;用21号中段的0ah号功能获取句子
MOV AL,act1
CBW
MOV CX,AX
;保存关键字长度到cx
PUSH CX
;cx入栈
MOV AL,act2
cmp AL,0
je a50
;保存句子长度到al,若句子为空则跳转显示not match
SUB AL,act1
js a50
;若句子长度小于关键字长度,则跳转显示not match
INC AL
CBW
LEA BX,stokn2
;将句子的首地址放进BX
MOV DI,0
MOV SI,0
a20:
;比较,内循环
MOV AH,[BX+DI]
CMP AH,stokn1[SI]
;遇见字符不相等就跳转到a30
jne a30
INC DI
INC SI
DEC CX
;没遇到一个相等的字符,cx-1,若cx不为0则比较下一个字符,当cx为0是说明关键字比较完
CMP CX,0
je a40
jmp a20
a30:
;外循环,BX+1,清空si,di继续内循环比较
INC BX
DEC AL
cmp AL,0
je a50
MOV DI,0
MOV SI,0
POP CX
push CX
jmp a20
a40:
;match,将bx减去句子的首地址加一得到关键字所在位置,调用二进制转十六进制子函数将位置输出
SUB BX,offset stokn2
INC BX
LEA DX,change
MOV ah,09
INT 21h
LEA DX,mess3
MOV ah,09
INT 21h
CALL btoh
LEA DX,mess5
MOV ah,09
INT 21h
jmp a10
;二进制转换十六进制
btoh PROC NEAR
MOV CH,4
rotate: MOV CL,4
ROL BX,CL
MOV AL,BL
and AL,0fh
add AL,30h
cmp al,3ah
jl printit
add al,7h
printit:
MOV dl,al
MOV ah,2
int 21h
dec ch
jnz rotate
ret
btoh endp
a50:
;显示not match
LEA DX,change
MOV ah,09
INT 21h
LEA DX,mess4
MOV ah,09
INT 21h
jmp a10
exit:
ret
CODES ENDS
END START
首先需要有输入限制要求,即输入的关键字长度不能大于句子的长度。然后是比较句子中的字符和关键字,利用cld依次将si、di增1,repz cmpsb比较si、di指向的字符,若三次比较均成功,则跳转至match(表示匹配成功),在match中实现将句子中匹配成功的首地址存放在bx中。否则继续下面的执行,恢复si指针指向关键字首地址,di指向句子的匹配失败的首地址的下一个地址,然后继续循环compare(继续寻找匹配),若最后失败则执行到wrong。
实验10
斐波纳契数列:1,1,2,3,5,8,13。通常可以使用递归函数实现,现用汇编实现该过程
assume cs:codesg,ds:datasg1
datasg1 segment
num dw 8 dup(0)
;42417671442657
extra dw 8 dup(0)
ans db 32 dup(0)
datasg1 ends
datasg2 segment
dw 1600 dup(0)
string db 'please input an int from 1 to 150:$'
datasg2 ends
codesg segment
start:
mov ax,datasg2
mov ds,ax
mov si,0
mov di,16
mov word ptr [si],0
mov word ptr [di],1
call fib
mov dx,offset string
mov ah,9h
int 21h
mov bx,10
mov cx,0
l1:
mov ah,01h ;中断调用,单字符输入
int 21h ;输入符号的ASCⅡ代码在AL寄存器中
cmp al,0dh
jz over
sub al,30h
add al,cl
mov ah,0
mul bx
mov cx,ax
jmp l1
over:
mov ax,cx
div bx
mov cx,16
mul cx
mov si,ax
mov ax,datasg1
mov es,ax
mov di,0
mov cx,16
rep movsb
call show_answer
mov ax,4c00h
int 21h
fib:
mov cx,150
s1:
call add_128
add si,16
add di,16
loop s1
ret
add_128: ;128位加法,把结果存放到di+16的相对内存中
push ax
push cx
push si
push di
mov cx,8
sub ax,ax
s0:
mov ax,[si]
adc ax,[di]
mov [di+16],ax
inc si
inc si
inc di
inc di
loop s0
pop di
pop si
pop cx
pop ax
ret
call show_answer
mov ax,4c00h
int 21h
show_answer:
mov ax,datasg1
mov ds,ax
add ax,1
mov es,ax
mov bx,36
mov byte ptr ans[bx],'$'
dec bx
l2:
mov si,14
mov di,14
call divlong
add cl,30h
mov ans[bx],cl
dec bx
mov si,0
mov cx,[si]
jcxz ok2
call clr_ex
jmp l2
ok2:
mov dx,bx
add dx,32+1
mov ah,9
int 21h
ret
clr_ex:
push si
push cx
mov si,0
mov cx,8
l5:
mov word ptr extra[si],0
add si,2
loop l5
pop cx
pop si
ret
divlong:
mov cx,7
l3:
push cx
mov ax,[si-2]
mov dx,[si]
mov cx,10
call divdw
add es:[di],dx
add es:[di-2],ax
mov [si-2],cx
mov word ptr [si],0
sub si,2
sub di,2
pop cx
loop l3
mov cx,[si]
push cx
mov cx,16
mov si,0
mov di,0
l4:
mov al,es:[di]
mov [si],al
inc si
inc di
loop l4
pop cx
ret
divdw: ;算dxax/cx,商dxax余cx
push bx
push ax
mov ax,dx
mov dx,0
div cx ;此时计算 H/N
mov bx,ax ;此时bx存放商,dx存放余数
pop ax
div cx
mov cx,dx
mov dx,bx
pop bx
ret
codesg ends
end start
①输入部分:接收用户键入的n值经十化二后存入num单元中。
②调用子程序fibp求出FIB(n)的值存放在result单元中。在递归子程序设计中,每次调用可把一帧信息存入堆栈。
l5:
mov word ptr extra[si],0
add si,2
loop l5
pop cx
pop si
ret
divlong:
mov cx,7
l3:
push cx
mov ax,[si-2]
mov dx,[si]
mov cx,10
call divdw
add es:[di],dx
add es:[di-2],ax
mov [si-2],cx
mov word ptr [si],0
sub si,2
sub di,2
pop cx
loop l3
mov cx,[si]
push cx
mov cx,16
mov si,0
mov di,0
l4:
mov al,es:[di]
mov [si],al
inc si
inc di
loop l4
pop cx
ret
divdw: ;算dxax/cx,商dxax余cx
push bx
push ax
mov ax,dx
mov dx,0
div cx ;此时计算 H/N
mov bx,ax ;此时bx存放商,dx存放余数
pop ax
div cx
mov cx,dx
mov dx,bx
pop bx
ret
codesg ends
end start
①输入部分:接收用户键入的n值经十化二后存入num单元中。
②调用子程序fibp求出FIB(n)的值存放在result单元中。在递归子程序设计中,每次调用可把一帧信息存入堆栈。
③输出部分:把运行结果经二化十后在屏幕上显示出来。