练习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重新赋值。这个过程可以用栈来执行。如果循环次数少,则用寄存器来存。