汇编语言:实验十 编写子程序

本文介绍了三个子程序实验:1) 如何在显存中显示字符串,理解内存布局;2) 解决除法溢出问题的编程技巧;3) 数值的逐位显示,涉及C语言基础知识。通过这些实践,提升汇编语言编程能力与问题解决策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

实验10 编写子程序

这次实验需要编写三个子程序,通过它们来认识几个常见的问题和掌握解决这些问题的方法。

1.显示字符串
问题

在这里插入图片描述

子程序描述

在这里插入图片描述

提示

在这里插入图片描述

Solution

跟之前的文章的风格相似,都是用王爽老师原书中的描述,在完成第一个程序之前,需要稍微了解一下显存相关的内容:汇编语言:实验九 根据材料编程
了解了显存相关的知识,可以动手操作操作了,题目需要将字符串显示在特定的行列,如果学习过数据结构或者对二维数组较为了解,求解二维数组中R行C列的元素前面一共有多少个元素?一定知道这么一个计算方法:(R * nums + C),nums表示每一行元素的个数。
在这里插入图片描述

显存内存的分布也可以类比成二维数组,每一行一共有 80 个元素,每个元素占用 2 个字节,那么每一行占用了80 * 2 = 160字节,求解R行C列前面占用的空间就可以得到表达式:(R * 160 + C * 2 ) 字节。

实现代码
assume cs:code, ds:data
data segment
	db 'Welcome to masm!',0
data ends
code segment
start:
	mov dh, 10	;行号 范围 0 ~ 24
	mov dl, 3	;列号 范围 0 ~ 79
	mov cl, 2	;颜色(取值范围0 ~ 79)
	
	mov ax, data
	mov ds, ax
	
	mov si, 0;ds偏移
	mov di, 0;es偏移
	mov bl, cl	;bl保存需要改变的颜色
	
	mov ax, 0B800H
	mov es, ax;显存位置
	
	mov al, dh
	mov ah, 160
	mul ah
	add di, ax	;行空间
	
	mov al, cl
	mov ah, 2
	mul ah
	add di, ax	;列空间
	
	call compare
	
	mov ax, 4c00h
	int 21h
	
compare:;调用子程序前的保存工作
	push cx
	push di
	push si
	
show_str:
	mov cl, ds:[si]
	mov ch, 0H
	jcxz ok	;循环结束
	mov es:[di], cl
	mov es:[di + 1], bl
	add di, 2
	inc si
	jmp short show_str
	
ok:;调用完子程序之后的恢复工作
	pop si
	pop di
	pop cx
	ret
code ends
end start
运行

在这里插入图片描述

2.解决除法溢出的问题

问题

在这里插入图片描述
在这里插入图片描述

子问题描述

在这里插入图片描述

提示

在这里插入图片描述

Solution

问题主要用于解决除法溢出的问题。

程序不难,只是用于熟悉子程序结构、构建。

assume cs:code, ss:stack
stack segment
	db 8 dup (0)
stack ends

code segment
start:
	mov ax, stack
	mov ss, ax
	
	mov ax, 4240H	;被除数低位
	mov dx, 000FH	;被除数高位
	mov cx, 0AH	;除数
	call divdw
	
	mov ax, 4c00h
	int 21H
divdw:
	push ax	;将被除数低位压栈
	
	mov ax, dx	;将被除数高位传入ax
	mov dx, 0	;进行高位除法
	
	div cx	;ax / cx,公式中 H / N
	mov bx, ax	;bx保存 int(H / N)
	
	pop ax	;低数位出栈,此时dx中为rem(H / N),正好满足 [ rem(H / N) * 65536 + L]
	
	div cx	;(dx * 16 + ax) / cx
	mov cx, dx
	mov dx, bx
	ret
code ends
end start

3.数值显示

问题

在这里插入图片描述

子程序描述

在这里插入图片描述
在这里插入图片描述

提示

在这里插入图片描述
在这里插入图片描述
读者对本题应该非常熟悉,在《C语言程序设计》课程中应该完成过“打印一个数字的每位数字”,这道题其实也是那个经典的while循环,按照王爽老师的解析还是可以很轻松地完成的哦。

assume cs:code, ss:stack
stack segment
	db 10 dup (0)
stack ends

data segment
	db 10 dup (0)
data ends

code segment
start:
	mov ax, 12666
	mov bx, data
	mov ds, bx
	mov bx, stack
	mov ss, bx
	
	mov dx, 0
	mov si, 0;计数器,表示共有多少位
	mov di, 10	;充当除数
	call dtoc
	
	mov dh, 8
	mov dl, 3
	mov cl, 2
	call show
	
	
dtoc:
	mov cx, ax
	jcxz change	;如果ax已经是零,循环结束,将数据转移到ds:[x]
	mov dx, 0
	
	div di	;10
	push dx	;将余数压栈
	inc si	;计数器加一
	jmp short dtoc

change:
	mov cx, si
	mov di, 0	;充当ds偏移
	
getAns:	;从栈中弹出数据,并加上30H
	jcxz ok	;数据弹出完毕
	pop ax
	add ax, 30H
	mov [di], ax
	inc di
	dec cx
	jmp getAns

ok:
	ret
	
show:
	mov si, 0;ds偏移
	mov di, 0;es偏移
	mov bl, cl	;bl保存需要改变的颜色
	
	mov ax, 0B800H
	mov es, ax;显存位置
	
	mov al, dh
	mov ah, 160
	mul ah
	add di, ax	;行空间
	
	mov al, cl
	mov ah, 2
	mul ah
	add di, ax	;列空间
	
	call compa
	
	mov ax, 4c00h
	int 21h
	
compa:;调用子程序前的保存工作
	push cx
	push di
	push si
	
show_str:
	mov cl, ds:[si]
	mov ch, 0H
	jcxz okk	;循环结束
	mov es:[di], cl
	mov es:[di + 1], bl
	add di, 2
	inc si
	jmp short show_str
	
okk:;调用完子程序之后的恢复工作
	pop si
	pop di
	pop cx
	ret
code ends
end start
运行结果

在这里插入图片描述

### 汇编语言实验 编写子程序 示例教程 在汇编语言中,编写子程序是一项重要的技能。子程序可以用来实现特定的功能,并且可以在程序的多个地方调用,从而提高代码的可重用性和可维护性。以下是一个关于实验编写子程序的示例教程[^3]。 #### 1. 子程序的基本结构 子程序通常由以下几个部分组成: - **入口点**:子程序开始执行的位置。 - **参数传递**:通过寄存器或堆栈将参数传递给子程序。 - **保护现场**:保存子程序调用前的状态(如寄存器值)。 - **核心逻辑**:实现子程序的主要功能。 - **恢复现场**:恢复子程序调用前的状态。 - **返回指令**:使用 `ret` 指令返回到调用位置。 #### 2. 示例:数字转换为字符串 根据引用内容,实验要求将一个数字转换为字符串并显示在屏幕上。以下是实现该功能的子程序示例: ```asm assume cs:code, ds:data data segment num dw 12666 ; 待转换的数字 str db 10 dup(0) ; 存储转换后的字符串 data ends code segment start: mov ax, data mov ds, ax lea si, str ; SI 指向字符串缓冲区 mov ax, [num] ; AX = 数字 call num_to_str ; 调用子程序将数字转换为字符串 mov ah, 9 ; DOS 显示字符串功能 lea dx, str ; DX 指向字符串 int 21h ; 调用 DOS 中断 mov ax, 4c00h ; 程序结束 int 21h ; 子程序:将 AX 中的数字转换为字符串,存储到 SI 指向的缓冲区 num_to_str proc push ax ; 保护 AX push bx ; 保护 BX push cx ; 保护 CX push dx ; 保护 DX xor cx, cx ; CX 用于计数 convert_loop: xor dx, dx ; 清零 DX mov bx, 10 ; 除数为 10 div bx ; AX = 商,DX = 余数 add dl, '0' ; 将余数转换为 ASCII 字符 push dx ; 将字符压入堆栈 inc cx ; 计数器加 1 test ax, ax ; 判断商是否为 0 jnz convert_loop ; 如果不为 0,继续循环 store_chars: pop dx ; 从堆栈弹出字符 mov [si], dl ; 存储到字符串缓冲区 inc si ; 指针加 1 loop store_chars ; 继续存储直到计数器为 0 mov byte ptr [si], '$'; 添加字符串结束标志 pop dx ; 恢复 DX pop cx ; 恢复 CX pop bx ; 恢复 BX pop ax ; 恢复 AX ret ; 返回 num_to_str endp code ends end start ``` #### 3. 关键点解析 - **数字转换为字符串**:通过不断对数字取余数和整除操作,将每一位数字提取出来并转换为对应的 ASCII 字符。 - **堆栈的使用**:由于数字的位数是从低位到高位依次计算的,而字符串需要从高位到低位存储,因此使用堆栈来反转顺序。 - **保护和恢复寄存器**:在子程序中,为了不影响主程序的寄存器状态,需要在进入子程序时保存寄存器值,并在退出时恢复。 #### 4. 显示字符串子程序 为了显示字符串,可以使用 DOS 中断功能 `INT 21H` 的 `AH=9` 功能。以下是显示字符串的子程序示例: ```asm show_str proc push ax ; 保护 AX push dx ; 保护 DX mov ah, 9 ; DOS 显示字符串功能 int 21h ; 调用 DOS 中断 pop dx ; 恢复 DX pop ax ; 恢复 AX ret ; 返回 show_str endp ``` #### 5. 注意事项 - 确保字符串以 `$` 结尾,这是 DOS 显示字符串功能的要求。 - 在编写子程序时,注意保护和恢复寄存器,以免影响主程序的运行。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

nepu_bin

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值