汇编语言实验十完整代码和详细解析

汇编语言实验十完整代码和详细解析


建议先自己思考问题的答案,不懂则返回看书

扩展建议: 【非必要内容,个人经验感悟】
从这儿开始,我们正式接触子程序,主要是通过 call和 ret 来实现的。这样就涉及到了入口参数问题,提个写代码风格习惯的建议,只写必要的入口参数,尽量简化参数,为了更好的移植性。
例如写一个清屏函数,它的功能是清除屏幕上的所有字符,就是把相应内存的数据都写为零,它不需要任何参数,随时随地,只要 call clear_screen (函数名),就可以实现此功能, 移植性非常好。如果写一个显示字符串的函数 ,必要的入口参数有行、列、颜色,可以规定此函数的入口参数,用dh 表示列,dl 表示行 ,cl 表示颜色,显示以零结尾的字符串,这样的话它的移植性也是很好的。方便以后的使用。

1、显示字符串
要求: 在指定的位置,用指定的颜色,显示用0结束的字符串。行号,列号,我修改过。可能参数值不太一样,参数值自己设置

;在指定的位置,用指定的颜色,显示用0结束的字符串
assume cs:code
data segment
  db 'Hello world',0	;修改成welcome to masm 也是可以的,通用,可移植性好。
data ends

code segment
start:
	mov dh,24		;dh 行号,0-24
	mov dl,8		;dl 列号,0-79
	mov cl,1		;cl 颜色

	mov ax,data
	mov ds,ax
	mov si,0		;ds:si指向字符串首地址
	
	call show_str
	
	mov ax,4c00h
	int 21h

show_str:
	push dx
	push ax
	push di
	push es
	push cx
	push ds
	push si

;实际行号
	mov ax,160
	mul dh		;行号偏移地址存在al中

	mov dh,0
;实际列号
	dec dl
	add dl,dl		;列偏移地址存在dl中
	add ax,dx		
	mov di,ax		;总体偏移地址在di中

	mov ax,0b800h
	mov es,ax		;所以es:[di]指向显存中的第一个位置
	
	mov ah,cl		;颜色存在ah中
	mov cx,0
s:
	mov al,ds:[si]
	mov cl,al
	jcxz over
	mov es:[di],ax
	inc si
	add di,2
	jmp short s
over:
	pop si
	pop ds
	pop cx
	pop es
	pop di
	pop ax
	pop dx
	ret

code ends
end start

此代码在DOS下的运行结果截图:
在这里插入图片描述

2、解决除法溢出的问题
a、主要是表达出这个计算公式,X65536=X10000H,将十六进制的X左移四位。
b、另外一个可能忽视的点,当子程序返回后,相应的值用ax ,cx ,dx 存储,也就是ret执行后,已经是存储好了,所以设置好相应的值后,再ret返回。

assume cs:code
data segment
    dw 8 dup(0)
data ends

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

	mov ax,4240h	;被除数低16位
	mov dx,000fh	;被除数高16位
	mov cx,0ah	;除数
	
	call divdw
	
	mov ax,4c00h
	int 21h
	
divdw:
	push ax

	mov ax,data
	mov ds,ax

	mov ax,dx		;被除数:dx存放高位,ax存放低位
	mov dx,0
	div cx		;ax存放着商,dx存放着余数

	mov ds:[0],ax	;左侧数据的高位,也是所求表达式的商的高位

	;对与右侧数据而言,余数dx的值为高位数据,L(原始被除数低16的值)
;仅为直观	mov dx,dx	
	pop ax		;右侧数据的被除数的低位
	push ax		;再恢复元数据
	div cx		;ax存放着商,dx存放着余数

	mov ds:[2],ax	;此内存单元的值为所求表达式的商的低位
	mov ds:[4],dx	;此内存单元的值为所求表达式的余数

	pop ax

	mov ax, ds:[2]
	mov dx, ds:[0]
	mov cx, ds:[4]
	ret
code ends
end start

此程序在DOSBox下的运行结果:
在这里插入图片描述
3、数值显示
将data段中的数据以十进制形式显示出来,8行,3列,绿色。显示比较容易,直接用之前写好的showstr 函数。关键呢就是把数据“分开”,还是老祖宗的办法,除k取余法!

;将data段中的数据以十进制形式显示出来,8行,3列,绿色
assume cs:code
data segment
  dw 123,12666,1,8,3,38
data ends

out segment
  db 32 dup (0)
out ends

stack segment
 dw 24 dup (0)
stack ends

code segment
start:
	mov ax,data
	mov ds,ax
	mov di,0
	
	mov ax,stack
	mov ss,ax
	mov sp,48

	mov ax,out
	mov es,ax
	mov si,0

	mov cx,6
        css:
	call divw	
	dec cx
	jcxz delete
	inc cx
	jmp next
         delete:
	inc cx
	dec si		;divw函数结束,指向out段中最后一个逗号
	mov al,0
	mov es:[si],al	;将该逗号去掉,将值修改为0,即字符串结束标志
          next:	
	loop css	


	mov dh,8		;在8行3列显示为绿色
	mov dl,3
	mov cl,2
	
	call show_str
	
	mov ax,4c00h
	int 21h
	
	
divw:	;将data中的每一个十进制数值拆分存储在out段中
	;12666=317aH,需要16位除法,段中数据无溢出
	;16位除法,被除数32位,dx高位,ax低位,ax商,dx余数
	push ax
	push bx
	push cx
	push dx
	push es

	mov cx,0
	mov ax,ds:[di]	;低位放data中数据
	mov bx,10		;除数一直为10
          divnei:
	mov dx,0		;高位放0
	div bx		;ax商,dx余数
	add dl,30h
	push dx		;先将余数存放起来(逆序)
	inc cx		;用cx记录数据的位数,为pop计数
	push cx		;A   将该计数cx先存放,防止jcxz
	
	mov cx,ax
	jcxz divneiover
	pop cx		;内部未结束,对应A处的push cx,继续计数
	jmp short divnei

          divneiover:	;一个数据分离完毕,进行存放
		;cx已经对数据的位数计数,直接使用
		pop cx	;内部结束,对应A处的push cx,结束计数
;		mov cx,cx
	divcun:
		pop ax
		mov es:[si],al
		inc si
		loop divcun

		;一个数据处理完毕,隔一个逗号
		mov al,2ch
		mov es:[si],al	;逗号的ascii是2ch
		inc si

	;一个数据处理完毕,loop到下一个数据,divwai开始
	inc di
	inc di		;word型数据,加两字节


	;数据处理完毕,返回原程序处
	pop es
	pop dx
	pop cx
	pop bx
	pop ax
	ret

show_str:
	push dx
	push ax
	push di
	push es
	push cx
	push ds
	push si

	mov ax,out
	mov ds,ax
	mov si,0		;ds:si指向字符串首地址

;实际行号
	inc dh		;不太懂,可能是因为有第0行??
	mov ax,160
	mul dh		;行号偏移地址存在al中

	mov dh,0
;实际列号
	dec dl
	add dl,dl		;列偏移地址存在dl中
	add ax,dx		
	mov di,ax		;总体偏移地址在di中

	mov ax,0b800h
	mov es,ax		;所以es:[di]指向显存中的第一个位置
	
	mov ah,cl		;颜色存在ah中
	mov cx,0
s:
	mov al,ds:[si]
	mov cl,al
	jcxz over
	mov es:[di],ax
	inc si
	add di,2
	jmp short s
over:
	pop si
	pop ds
	pop cx
	pop es
	pop di
	pop ax
	pop dx
	ret
	
code ends	
end start	

此代码在DOSBox下的运行结果:
在这里插入图片描述

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值