汇编语言(王爽第三版)实验9 根据材料编程

实验9 根据材料编程

编程:

       在屏幕中间分别显示绿色、绿底红色、白底蓝色的字符串:“welcome to masm!”

程序分析:

       1)在内存中定义并初始化一个字符串:“welcome to masm!”(这个在数据段中定义就可以,使用db)

       2)由材料提示(这里是8位的二进制的组合,形成一个属性)

属性字节的格式:

    7        6        5        4        3        2        1        0

  BL(闪烁)   R(背景)  G(背景)  B(背景)      I(高亮)    R(前景)    G(前景)    B(前景) 

              知道:      绿色属性:00000010B==02H

                            绿底红色:00100100B==24H

                            白底蓝色:01110001B==71H

       也就是说02H如果存储在显示缓冲区中的奇数内存单元时,它显示的字符是绿色的。

       3)在命令提示符窗口或dos窗口,我们可以显示80X25的字符(我的机器行数多,命令提示符窗口,跟设置有关)。每行80个字符,一共是25行。它们在内存中是在一个内存段中存储的,这个内存区域叫做显示缓冲区。从物理地址B8000H~BFFFH这个32K的内存区域就是显示缓冲区。

       我们可以使用debug查看下,例如段地址是B800H,偏移地址是:0000H;

d B800:0000

       我们可以在这里实验下,在当前屏幕下,使用e命令修改B800:0000H开始的内存,看看有什么反应?

ABC0:0030  20 07 20 07 20 07 20 07-20 07 20 07 20 07 20 07    . . . . . . . .

B800:0040  41 07 58 07 2C 07 34 07-43 07 30 07 30 07 20 07   A.X.,.4.C.0.0. .

B800:0050  20 07 20 07 20 07 20 07-20 07 20 07 20 07 20 07    . . . . . . . .

B800:0060  20 07 20 07 20 07 20 07-20 07 20 07 20 07 20 07    . . . . . . . .

B800:0070  20 07 20 07 20 07 20 07-20 07 20 07 20 07 20 07    . . . . . . . .

此处省略内存的显示。

-e b800:0000

B800:0000  42.41   07.02   38.42   07.02   30.43   07.02   30.

       我们发现,在B800:0000内存开始处写入数值,例如41H,0001单元:02H,在屏幕的第一行立即就出现了一个绿色的A(再换行就没了)。也就是说从B800:0000H开始,偶数地址是显示的字符,奇数地址是负责前面字符的显示属性的。也就是说在显示缓冲区中,每2个字节负责屏幕上一个字符的显示(包括显示的属性)。

       在显示缓冲区内写入的字符,立即就显示在屏幕上。

       因为每行要显示80个字符,故从0000H~009FH是显示的第一行(共160个字节)。每行可以类推。

       题目中由于需要让此字符串(共三行,显示在屏幕中间),故行数应该是在12、13、14行。那么它们的偏移地址的首地址是多少?0780H、0820H、08C0H。(粗略计算,实际上屏幕是多少行也不完全固定的。此处我们不重新计算段地址了,段地址还是B800H,只计算偏移地址。)

       “welcome to masm!”字符串有19个字符,也就是说它在显示内存区域中所占空间为38个字节。每行有80个字符显示,那么它在中间显示的话,取中显示,应该在0-159字节中间的第61-99字节(3C~63H);也就是在列上偏移3C(开始)

       行和列的偏移地址确定了。我们就可以试着输出第一行,它的偏移地址是:0780H+003

CH=7BCH(1980)。我们使用debug在B800:7BCH处写内存,发现实验成功了。依次类推。

       其它剩余2行的偏移地址,我们按照159-31=128计算,即(bx)=(bx)+128.

       字符串和字符的属性是存储在内存的data段中的,如何从内存中将它们(每个字节)取出,然后通过寄存器中转,然后写入到指定的显示缓冲区内(也是内存单元),并指定每个字符的属性(在奇数位置)。

       (es)= (data);(ds)= 0B800H,中转寄存器为ax(按要求将它们分开为2个8位寄存器)。

       由于在屏幕上输出3行“welcome to masm!”,故外循环3次来输出3行,每次一行输出。此为外循环。使用栈来存储CX的状态。

       内循环:将字符和字符属性按照顺序写入到显示缓冲区内。

源代码如下:

assume cs:codesg

data segment

    db 'welcome to masm!'

    db 02H,24H,71H              ;字符显示的属性值

data ends

stack segment

    db 10 dup(0)

stack ends

codesg segment

start:  ;初始化data数据段,es:di指向data

        mov ax,data

        mov es,ax

        mov di,0

        ;初始化显示缓冲区,ds:bx指向显示缓冲区。

        mov ax,0b800H

        mov ds,ax

        ;25行取中是121314 ,80列取中开始是61

        ;12行的偏移量是12*16=1920

        ;总偏移量为(偏移地址)1920+60=1980?

        mov bx,1980

       

        ;字符的属性在数据段中的偏移量

        mov si,16

        ;建栈,并初始化栈顶,熟悉栈结构。

        ;其实这里都不用人工建栈,有系统自动的。

        mov ax,stack

        mov ss,ax

        mov sp,0

           

        mov cx,3        ;计数器初始化为3(循环显示3次)

    s:  push cx         ;入栈保护CX,在stack

       

        mov cx,16       ;内循环为16次,16个字符

output: ;将字符写入显存中

        mov al,es:[di]

        mov [bx],al

        ;将字符属性写入显存中

        mov ah,es:[si]

        mov [bx+1],ah

       

        inc di

        add bx,2

        loop output

        ;每行输出的偏移量为128字节  

        add bx,128

       

        mov di,0

        inc si

        pop cx          ;出栈恢复cx计数器值

        loop s

           

        mov ax,4c00H

        int 21H

codesg ends

end start

       总结:

1.    合理利用栈结构保存寄存器变量的值。

2.    熟练掌握[bx+idata]这种CPU寻址的方式。

3.    在显存中,甚至是内存中,它们都是线性存储的,以列的形式存储的。不存在行的概念的,只不过在计算机屏幕上,还有debug中有行的概念,为了显示方便。

     

       

posted @ 2017-05-21 09:02  筑基2017  阅读(1680)  评论(0编辑  收藏  举报