汇编语言笔记实验10编写子程序
汇编语言笔记实验10编写子程序
0x00 任务一
直接上代码~:
assume cs:code,ds:data,ss:stack
data segment
db 'Nobody knows coding better than me',0
data ends
stack segment
db 16 dup (0)
stack ends
code segment
start: mov ax,data
mov ds,ax
mov ax,stack
mov ss,ax
mov sp,10h
mov dh,14
mov dl,30
mov cl,2
mov si,0
call show_str
mov ax,4c00h
int 21h
show_str:push ax
push bx
push es
mov ax,0b800h
mov es,ax
mov al,160
mul dh
mov bx,ax
add dl,dl
mov dh,0
add bx,dx
mov ax,cx
s: mov cl,ds:[si]
mov ch,0
jcxz fin
mov es:[bx],cl
mov es:[bx+1],al
add bx,2
inc si
jmp short s
fin: pop es
pop bx
pop ax
ret
code ends
end start
该子程序show_str除了占用需要的参数寄存器外,其余用到的寄存器都使用栈保存现场,确保不会干扰调用者。
0x01 任务二
这个任务的关键在于这个提示的公式,这个公式将 x/n 变成3部分 (1)int(H/N)*65536这部分的结果是我们divdw商的高位,不产生余数 (2)rem(H/N)x65536/N 这部分的商是我们商的低位 并产生余数 (3)L/N 这部分的商是我们商的低位 ,也产生余数
所以 商的高位 也就是dx=int(H/N)*65536 商的低位=后两个除法的商 , 余数=后两个除法的余数 上代码!
assume cs:code,ss:stack
stack segment
db 32 dup (0)
stack ends
code segment
start: mov ax,stack
mov ss,ax
mov sp,20h
mov ax,4240h
mov dx,000fh
mov cx,0ah
call divdw
mov ax,4c00h
int 21h
divdw: push bx
push bp
mov bp,sp
sub sp,16
mov [bp-2],ax ;-2
mov [bp-4],dx;-4
mov [bp-6],cx;-6
mov ax,dx
mov dx,0
div cx
mov [bp-8],ax;高16位结果已经算出 放在ax 余数在dx -8
mov ax,0
div cx
mov [bp-10],ax;结果商放在ax 余数放在dx -10
mov [bp-12],dx;-12
mov dx,0
mov ax,[bp-2]
div word ptr [bp-6]
mov [bp-14],ax;-14
mov [bp-16],dx;-16
mov dx,word ptr [bp-8]
mov ax,word ptr [bp-10]
add ax,word ptr [bp-14]
mov cx,word ptr [bp-12]
add cx,word ptr [bp-16]
add sp,16
pop bp
pop bx
ret
code ends
end start
运行结果正确,这个题 几个关键点
(1)公式的理解
①该公式 H/N 运算结果 ax是 最后结果的高位商, ②dx 是后面 rem(H/N)的结果an 而该结果*65536实际上就是让16位的an后面加16个0,那么成为了一个32位数 再除 N 也就是 高位还是dx①的结果不用变,ax为0,然后 div N 得到的商 是最终结果的商的低位的一部分 余数也是 最终余数的一部分 ③L/N 16位除16位,没有这种div指令 所以要把dx填0 ,最终结果,得到的商 是最终结果的商的低位的一部分 余数也是 最终余数的一部分
(2)中间过程量怎么存,使用局部变量,使用方法:
mov bp,sp
sub sp,xx
mov [bp-2],ax
...
add sp,xx
利用栈来定义局部变量 ,用赋值栈顶sp到bp 然后让栈顶sp减小xx(不减小后面存储不了,这是一个奇怪的现象,和栈的初始化有关?不用push 不改sp 直接写栈段空间改不了好像),xx的值是局部变量占用的总大小,然后就可以往扩充的栈内存放临时变量了(就是[bp-x]的方式读写),最后要把sp add回来。
//2020-6-3修改
因做课程设计1,要使用到该函数divdw 发现90f24h (593700) / 0ah (10)结果怎么都不对 正确结果是 商 0e7eah 余数 0 而这个函数的结果是 0e7e9h 余数是0。 仔细分析后修改代码如下
divdw: push bx;参数 ax存放低位 dx存放高位 cx存放除数
push bp;结果 ax存放商低位 dx存放高位 cx存放 余数
mov bp,sp
sub sp,12
mov [bp-2],ax;保存传入参数ax
mov [bp-4],dx;保存传入参数dx
mov [bp-6],cx;保存传入参数cx
mov ax,dx
mov dx,0
div cx
mov [bp-8],ax;
mov ax,[bp-2]
div cx;(rem(H/N)*65536+L)/N
mov [bp-10],ax;
mov [bp-12],dx;-12
mov dx,word ptr [bp-8]
mov ax,word ptr [bp-10]
mov cx,word ptr [bp-12]
add sp,12
pop bp
pop bx
ret
原代码高16位结果计算没问题(这么简单要出问题就别混了),错误在低16位,把(rem(H/N)*65536+L)/N展开成两个部分计算了 ,但是这是不可以展开的(用余数和商的表示情况下),比如 (4+5)/9 = 1…0 分开算就是商0余9了 这是错误的。
而修改后的正确代码简洁了很多,原因在于在求高16位结果时的余数dx ,在求低16位时因为*65536因而不用挪动位置还是在dx上,再加上传入的低16位 就可以直接 div cx了 执行后的ax即是低16位的商 dx即是 最后的余数!
0x02 任务三
在c语言中 这很简单 很常见,伪代码描述如下:
汇编语言如下:
assume cs:code,ds:data,ss:stack
data segment
db 32 dup (0)
data ends
stack segment
db 32 dup (0)
stack ends
code segment
start: mov ax,data
mov ds,ax
mov ax,stack
mov ss,ax
mov sp,20h
mov ax,1266
mov si,0
call dtoc
mov ax,4c00h
int 21h
dtoc: push dx
push cx
push bx
mov bx,0
s: mov cl,10
div cl
mov dl,ah
mov dh,0;余数放在dx中
push dx
inc bx
mov ah,0
mov cx,ax
jcxz s1
jmp short s
s1: mov cx,bx
s2: pop dx
add dl,30h
mov ds:[si],dl
inc si
loop s2
mov byte ptr ds:[si],0
inc si
pop bx
pop cx
pop dx
ret
code ends
end start
运行结果正确!nice
//2020-6-3日修改任务三代码
assume cs:code,ds:data,ss:stack
data segment
db 32 dup (0)
data ends
stack segment
db 64 dup (0)
stack ends
code segment
start: mov ax,data
mov ds,ax
mov ax,stack
mov ss,ax
mov sp,40h
mov ax,10086
mov si,0
call dtoc
mov ax,4c00h
int 21h
dtoc: push dx
push cx
push bx
mov bx,0
s: mov cx,10
mov dx,0
div cx
push dx
inc bx
mov cx,ax
jcxz s1
jmp short s
s1: mov cx,bx
s2: pop dx
add dl,30h
mov ds:[si],dl
inc si
loop s2
mov byte ptr ds:[si],0
inc si
pop bx
pop cx
pop dx
ret
show_str:push ax
push bx
push es
mov ax,0b800h
mov es,ax
mov al,160
mul dh
mov bx,ax
add dl,dl
mov dh,0
add bx,dx
mov ax,cx
s5: mov cl,ds:[si];修改每次求余数的除法 为32位除16位
mov ch,0
jcxz fin
mov es:[bx],cl
mov es:[bx+1],al
add bx,2
inc si
jmp short s5
fin: pop es
pop bx
pop ax
ret
code ends
end start
昨天的代码在测试参数ax为10086时 程序崩溃,经过调试和分析,发现bug位于‘每次除10求余数’这一过程,昨天的代码用的是16位除8位,这样结果商和余数都只能是8位,然后10086/10 商1008>256要用16位,所以才改成32位/16位 这样在执行除法前 要将dx置0,商直接在ax里 所以删除了 mov ah,0 余数也直接在dx里 所以删除 mov dl,ah mov dh,0
两个极值下都正确运行