汇编语言笔记实验10编写子程序

汇编语言笔记实验10编写子程序

0x00 任务一

QQ截图20200602110637

QQ截图20200602110712

QQ截图20200602110723

​ 直接上代码~:

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
		

QQ截图20200602115425

​ 该子程序show_str除了占用需要的参数寄存器外,其余用到的寄存器都使用栈保存现场,确保不会干扰调用者。

0x01 任务二

QQ截图20200602152338

QQ截图20200602152412

QQ截图20200602152438

QQ截图20200602152503

​ 这个任务的关键在于这个提示的公式,这个公式将 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

QQ截图20200602164624

​ 运行结果正确,这个题 几个关键点

​ (1)公式的理解

QQ截图20200602164759

​ ①该公式 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 任务三

QQ截图20200602172208

QQ截图20200602172436

QQ截图20200602172450

QQ截图20200602172459

QQ截图20200602172510

​ 在c语言中 这很简单 很常见,伪代码描述如下:

QQ截图20200602172510

QQ截图20200602172743

​ 汇编语言如下:

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

QQ截图20200602175557

​ 运行结果正确!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

QQ截图20200603085452

QQ截图20200603085523

​ 两个极值下都正确运行