练习1:
在屏幕上输出内存单元中的十进制两位数。

1 ; 在屏幕上输出内存单元中的十进制两位数 2 assume cs:code, ds:data 3 data segment 4 db 12 5 db 0,0 ; 前一个字节用于保存商,后一个字节用于保存余数 6 data ends 7 code segment 8 start: 9 mov ax,data 10 mov ds,ax ; 补全指令,使得ds <-- data段地址 11 12 mov ah,0 13 mov al,ds:[0] ; ax <-- data段字节单元的被除数12 14 mov bl,10 15 div bl 16 mov ds:[1],al ; 补全代码,让商保存到data段注释中指定的单元 17 mov ds:[2],ah ; 补全代码,让余数保存到data段注释中指定的单元 18 19 mov ah,2 20 mov dl,ds:[1] ; 补全代码,使得dl <-- data段中保存的商的字节单元数值 21 add dl,30H ; 补全代码,使得dl中的数值转换为数字字符 22 int 21h 23 24 mov ah,2 25 mov dl,ds:[2] ; 补全代码,使得dl <-- data段中保存余数的字节单元数值 26 add dl,30H ; 补全代码,使得dl中的数值转换为数字字符 27 int 21h 28 29 mov ax,4c00h 30 int 21h 31 code ends 32 end start
现在所学,只能输出当个字符。
1 mov ah,2 2 mov dl,30H 3 int 21h
dl是要输出的数值。
现在的想法是一位一位输出,先通过除十,商为十位上的值,余数为个位数上的值。
附除法(div)使用方法:
(1)除数:有8位或者16位两位,在一个reg或内存单元中。
(2)被除数:默认放在AX或者DX中,如果除数为8位,被除数则为16位,默认在AX中存放;如果除数为16位,被除数则为32位,在DX和AX中存放,DX存放高16位,AX存放低16位。
(3)结果:如果除数为8位,则AL存储除法操作的商,AH存储除法操作的余数;如果除数为16位,则AX存储除法操作的商,DX存储除法操作的余数。
练习2:
在屏幕上输出data段定义的5个十进制两位数,数据和数据之间以空格间隔。

1 assume cs:code, ds:data 2 data segment 3 db 12,35,96,55,67 4 data ends 5 code segment 6 start: 7 mov ax,data 8 mov ds,ax 9 mov cx,5 10 mov bx,0 11 s: mov ax,0 12 mov dl,10 13 mov al,[bx] 14 div dl 15 mov dl,al 16 mov dh,ah 17 18 mov ah,2 19 add dl,30H 20 int 21H 21 22 mov ah,2 23 mov dl,dh 24 add dl,30H 25 int 21H 26 27 mov ah,2 28 mov dl,20H 29 int 21H 30 inc bx 31 loop s 32 33 mov ax,4c00h 34 int 21h 35 code ends 36 end start
思路同第一问,先将data作为段地址,之后将数据读入al中,用除法,取每一位的个数,输出。
空格的ASCII码为20H。
练习3:
编程:在屏幕中间分别显示绿色、绿色红底、白色蓝色的字符串'Welcome to masm!'

1 ; p187 实验9 2 3 assume ds:data, cs:code 4 data segment 5 db 'Welcome to masm!' 6 db 2H,24H,71H ;字符串属性值 7 data ends 8 code segment 9 start: mov ax,data 10 mov ds,ax ;字符串及属性值所在段的段地址送入ds 11 mov ax,0b800H 12 mov es,ax ;80×25彩色字符模式显示缓冲区段地址送入es 13 mov si,06E0H ;es的偏移地址 14 mov bx,0 ;属性值的偏移地址 15 mov cx,3 16 17 k: mov dh,cl 18 mov cx,16 19 mov di,0 20 z: mov al,ds:[di] 21 mov es:[si],al 22 inc si 23 mov al,ds:[bx+16] 24 mov es:[si],al 25 inc di 26 inc si 27 loop z 28 29 inc bx 30 mov cl,dh 31 add si,0080H 32 loop k 33 34 mov ax,4c00h 35 int 21h 36 code ends 37 end start
里面用了两重循环,第一层循环为3,第二层循环为16。loop用执行的时候当且仅当判断CX的值,所以我们可以先藏起来。偷偷地赋给BH值,再给CX赋第二层循环的次数,第二层循环完了,BH再找回并把CX的初值交给CX,并执行loop。(感觉自己可以去一篇小说了。。。)如果想要n重循环(n>2),建议创建栈空间,分别利用PUSH,POP实行上述操作。因为寄存器个数有限。需要合理运用。且行且珍惜。AX不断重写数据,CX判断循环,不能乱放数据。一下子少了两个能用的寄存器,所以正常能用也就是BX和DX了。。
中间的行数是11,12,13行
注意:其中换行,应该si+80H,而不是A0H,是因为第一次循环结束时,si=06E0H+20H=0700H,再加A0H,就变成了07A0H,而第二次开始的si应该是0780H,其中多加商第一次循环所的20H。
全屏输出:

1 assume ds:data, cs:code 2 data segment 3 db 'Welcome to masm!' 4 db 2H,24H,71H ;字符串属性值 5 data ends 6 code segment 7 start: mov ax,data 8 mov ds,ax ;字符串及属性值所在段的段地址送入ds 9 mov ax,0b800H 10 mov es,ax ;80×25彩色字符模式显示缓冲区段地址送入es 11 mov si,0 ;es的偏移地址 12 mov bx,0 ;属性值的偏移地址 13 mov cx,125 14 15 k: mov dh,cl 16 mov cx,16 17 mov di,0 18 z: mov al,ds:[di] 19 mov es:[si],al 20 inc si 21 mov al,ds:[bx+16] 22 mov es:[si],al 23 inc di 24 inc si 25 loop z 26 27 inc bx 28 mov ax,bx 29 mov bx,3 30 div bl 31 mov bl,ah 32 mov cl,dh 33 loop k 34 35 mov ax,4c00h 36 int 21h 37 code ends 38 end start
其中比较重要的一个,就是多加一个bx取3的余数。
运行结果:
实验总结与感受
1.输出方法,单个字符输出。其中dl是ASCII码。
mov ah,2
mov dl,al
int 21h
2.第种输出方法是向B8000H-BFFFFH显示缓冲区,偶地址放入字符的ASCII码,奇地址存放字符的颜色属性。显示屏可以显示25行,每行80个字符,即B8000H~B8F9FH。
颜色属性:R:红色 G:绿色 B:蓝色
7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | |
含义 | BL | R | G | B | I | R | G | B |
闪烁 | 背景 | 高亮 | 前景 |
3.关于多重循环,loop只能判断CX的值,要多重循环时,就必须先把CX的值,藏起来,再给CX重新赋值。这个过程可以用栈来执行。如果循环次数少,则用寄存器来存。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现