一.实验内容及结论
(一)实验任务一
补全程序t1.asm,完成在屏幕上输出内存单元的十进制两位数
源程序:
; 在屏幕上输出内存单元中的十进制两位数 assume cs:code, ds:data data segment db 12 db 0,0 ; 前一个字节用于保存商,后一个字节用于保存余数 data ends code segment start: mov ax,data ; 使得ds <-- data段地址 mov ds,ax mov ah,0 mov al,ds:[0] ; ax <-- data段字节单元的被除数12 mov bl,10 div bl mov ds:[1],al ; 让商保存到data段注释中指定的单元 mov ds:[2],ah ; 让余数保存到data段注释中指定的单元 mov ah,2 mov dl,ds:[1] ; 使得dl <-- data段中保存的商的字节单元数值 add dl,30h ; 使得dl中的数值转换为数字字符 int 21h mov ah,2 mov dl,ds:[2] ; 使得dl <-- data段中保存余数的字节单元数值 add dl,30h ; 使得dl中的数值转换为数字字符 int 21h mov ax,4c00h int 21h code ends end start
运行结果:
(二)补全t2.asm,完成在屏幕上输出data段定义的5个十进制两位数,数据和数据之间以空格间隔。
源程序:
assume cs:code, ds:data,ss:stack data segment db 12,35,96,55,67 ;0 1 2 3 4 dw 0 ;5 6 data ends stack segment dw 8 dup(0) stack ends code segment start: mov ax,data mov ds,ax mov ax,stack mov ss,ax mov sp,10h mov cx,5 mov bx,0 s: mov si,5 mov ah,0 mov al,ds:[bx] mov dl,10 div dl ;作除法 mov ds:[si],al ;商 mov ds:[si+1],ah ;余数 push cx ;将第一层循环次数(cx)入栈,开始第二层循环 mov cx,2 s0: mov ah,2 ;输出商和余数 mov dl,ds:[si] add dl,30h int 21h inc si loop s0 mov ah,2 ;输出空格 mov dl,0 int 21h inc bx pop cx loop s mov ax,4c00h int 21h code ends end start
运行结果:
(三)教材实验9,在屏幕中央分别显示绿色、绿底红色、白底蓝色的字符串'welcome to masm!'。
源程序:
; p187 实验9 assume ds:data, cs:code,ss:stack data segment db 'welcome to masm!' ;0-15 db 2H,24H,71H ;字符串属性值 16 17 18 data ends stack segment dw 8 dup(0) stack ends code segment start: mov ax,data mov ds,ax ;字符串及属性值所在段的段地址送入ds mov ax,stack mov ss,ax mov sp,10h mov ax,0b800H mov es,ax ;80×25彩色字符模式显示缓冲区段地址送入es mov di,16 mov si,06e0h mov cx,3 s0: push cx mov cx,16 mov bx,0 s1: mov al,ds:[bx] ;字符 mov ah,ds:[di] ;属性 mov es:[si],al mov es:[si+1],ah inc bx ;依次后移字符 add si,2 ;依次后移在屏幕中输出的位置 loop s1 pop cx inc di ;改变属性 add si,80h ;换行 loop s0 mov ax,4c00h int 21h code ends end start
运行结果:
二、总结与体会
虽说这3个实验只是简单地按照相应要求在屏幕上输出相应的字符数据等。但是当自己独立将它们都做出来后,还是成就感满满的。
1.首先第一个和第二个实验中,都运用到了无符号除法div指令和在屏幕上输出相关字符的固定代码(如下)。
assume cs:code code segment ... mov ah,2 mov dl,'A' ;将想要输出的字符(或其ASCII码)送入dl寄存器中 int 21h ... mov ax,4c00h int 21h code ends
同时由于实际需要(可能需要用ax寄存器完成其他相关操作),需要将商和余数分别从al,ah寄存器中送入内存单元中保存,这就涉及到了内存单元和寄存器的相关转换,在循环中需要注意其相应的偏移地址的改变是否一一对应。如实验任务2中,需要将5个十进制两位数在屏幕上显示出来,在这里我的处理是每次用div指令算出相应十进制数的商和余数后,将其送入内存单元中,以便后面的输出,在这里,存放这5个数的商和余数的内存单元地址不变,每次都用新的值去覆盖它,再将其送入dl寄存器中在屏幕上输出。
2.当要把一个数在屏幕上输出时,由于我们还没有学到可以将数字完整输出在屏幕上的指令,所以只能按照上述固定模式按个将字符输出。在这里就要用到高级语言中的数位分离的思想,把每一位上的数单独拿出来输出。只需要将该数连续除以10取余,直至商为0为止,在这过程中所得的余数即为该数每一位上的数字的反序排列。再利用循环将其输出。
3.通过完成实验任务3,我更加深入地理解到了80*25彩色字符模式显示缓冲区的相关知识。对之前做的实验有了进一步的了解。实验任务3中需要解决
①如何在屏幕中间输出相应字符 ②如何显示绿色、绿地红色、白底蓝色的字符串。
通过老师对该实验的补充和实验提示,我对其有了相应的了解。
对于问题①,
<1>总的来说,80*25彩色字符模式显示缓冲区早内存地址空间中为B8000H-BFFFFH共32KB的空间。显示缓冲区分为8页,一页4KB,显示器可以显示任意一页的内容,一般情况下显示第0页的内容。
<2>对于每个字符而言,一个字符占两个字节,分别存放字符的ASCII码和属性,每个字符有256种属性。显示器可以显示25行,每行80个字符即160个字节。故而在80*25模式下,一页的内容在显示缓冲区中共占2000个字符即4000个字节。
由图可得:屏幕中间的位置在Line11,12,13处,即要在屏幕中央输出字符只需在内存单元地址为B800:06E0H~B800:08BFH处写入相应字符信息。
对于问题②,
<1> 一个字符占两个字节,低位字节(偶地址)存储字符的ASCII码,高位字节(奇地址)存储字符的属性。由此可知,对于输出字符的颜色要求主要与属性字节有关。
属性字节的格式:
7 6 5 4 3 2 1 0
含义: BL R G B I R G B
闪烁 背景 高亮 前景
(R,G,B分别表示red,green,blue。)
可以通过按位设置属性字节,从而配出各种不同的前景色和背景色。
例如,常用的几种颜色如下:
解决了①②的问题后就可以比较顺利地把任务3完成啦。
总的来说,每次实验都是对知识的巩固和拓展,之前对div指令并不是很熟悉,只是觉得它存放商和余数的寄存器很绕,但是通过实验9的上机操作,对如何使用div指令,商和余数的存放都有了很深的印象。通过实验任务3的操作,对于之前感到很神奇的事情有了合理的解释,收获了许多。