课程设计1

课程设计1

 

任务:将实验七中的Power idea 公司的数据,按照图10.2所示的格式在屏幕中显示出来。

实验目的:

      1)进一步熟悉掌握子程序的编写。理解入口参数,返回值,程序目的。

      2)在主程序中利用子程序的入口参数,调用子程序。

      3)进一步熟悉内存的寻址。

      4)优化原先编写的几个子程序,注意标号、寄存器的保护等。

      5)将以前所学习的到的几个实验的程序,部分修改后,合理的运用它们;也进一步了解什么是子程序,子程序的调用,子程序的参数,返回值。

编程分析:

      1)讨论数据的存储问题,我们除非接触到数据库这种东东,在汇编语言或C语言中,存储数据(指有用的数据,例如统计信息等)的方式不外乎将数据存储在磁盘中,生成特定的文件,然后需要时将这些数据装载到内存中,通过程序进行读取。

      Power idea 公司的数据直接就将数据存储在了汇编文件中,这样在执行可执行程序时,直接就将数据装载到了内存中。省去了从数据文件中读取数据的过程了。

      这种存储数据的方式不太灵活,需要修改数据时,还必须重新修改汇编源文件,重新编译、链接。

      2)在实验七中,我们已经实现了将这些数据写入到了table段内存中;目前我们考虑的就是将table段内存怎样读取出来后,有的数据不是字符串,需要将数字转换成字符串形式;写入一个目标内存段中(此设计中是指的显存段中)。我们直接将实验七的程序改造成一个子程序即可,在主程序中直接调用,那么table段内存中就是我们程序员所期望的存储模式了。

      3)table段数据的读取并写入一个临时的内存存储段中。data段:对于字符串(例如:年份),我们直接写入目标内存,并加入0标记;对于其他的数值,我们调用实验10中的dtoc子程序,转换成字符串后写入目标内存段。此时在data段中存储的都是字符串形式的,并且以0为结尾。在此设计中,我们一律使用修改后的32位转换子程序ddtoc。

      4)由于对应“收入-income”的数据,采用的是dd类型,在dtoc子程序中,除法指令div运算的结果ax已经放不下了。需要我们调用另一个divdw子程序来支持。这样就需要修改下dtoc子程序,并编写成的32位转换子程序ddtoc。并重新看看divdw程序是否满足要求。

      5)程序的大体框架是:将实验七的程序作为一个子程序,例如:to_table;主程序负责将table段的数据写入data段中(其中读取的数值需调用ddtoc将数字转换成字符串),最后调用show_str来显示字符串。

编程实现:

      一)首先将实验七的程序改写下,变成一个子程序,方便主程序的调用。to_table

代码如下:

assume cs:code

data1 segment 

   db '1975','1976','1977','1978','1979','1980','1981','1982','1983' 

   db '1984','1985','1986','1987','1988','1989','1990','1991','1992' 

   db '1993','1994','1995'

    ;以上是表示21年的21个字符串

   dd 16,22,382,1356,2390,8000,16000,24486,50065,97479,140417,197514 

   dd 345980,590827,803530,1183000,1843000,2759000,3753000,4649000,5937000 

    ;以上是表示21年公司总收入的21个dword型数据

   dw3,7,9,13,28,38,130,220,476,778,1001,1442,2258,2793,4037,5635,8226 

   dw 11542,14430,15257,17800

    ;以上是表示21公司雇员人数的21个Word型数据。

data1 ends 

table segment 

   db 21 dup ('year summ ne ?? ') 

table ends

code segment

start:

       ;初始化2个数据段,并将ds:bx指向data1,es:si指向table

       mov ax,data1

       mov ds,ax

       mov ax,table

       mov es,ax

       ;将数据创建生成table段

       call to_table           ;调用子程序

      

       mov ax, 4c00H

       int 21H

;------

;to_table:

;功能:将data1中的数据整理并写入table段中

;入口参数:data1内存段、table内存段

;返回值:无

;------   

to_table:

       ;保护寄存器变量

       push ax

       push ds

       push es

       push bx

       push si

       push di

       push cx

       ;将to_table中需要使用的寄存器变量都保存起来。

      

       ;初始化偏址变量

       mov bx,0

mov si,0

       mov di,0

      

       mov cx,21               ;初始化计数器

   s66:  

       ;写入年份

       mov ax,0[bx]

       mov es:0[si],ax

       mov ax,2[bx]

       mov es:2[si],ax

       ;写入空格

       mov al,20H

       mov es:4[si],al

       ;写入收入

       mov ax,84[bx]

       mov es:5[si],ax

       mov ax,86[bx]

       mov es:7[si],ax

       ;写入空格

       mov al,20H

       mov es:9[si],al

       ;雇员数

       mov ax,168[di]

       mov es:10[si],ax

       ;写入空格

       mov al,20H

       mov es:12[si],al

       ;除法后写入人均收入

       mov ax,[bx+84]

       mov dx,[bx+86]

       ;没有办法,用个bp变量吧

       mov bp,[di+168]

       div bp

       mov es:13[si],ax

       ;写入空格

       mov al,20H

       mov es:15[si],al

       ;bx、si、di变量的递增

       add bx,4

       add si,16

       add di,2

       loop s66

       ;恢复寄存器变量,并返回主调程序

       pop cx

       pop di

       pop si

       pop bx

       pop es

       pop ds

       pop ax

       ret

;-----

code ends

end start

      程序讲解:

      1)这个子程序在主程序中,直接调用就可以了。这里我们要对子程序有个初步的认识:子程序相当于其他用户和程序员来说,就是个黑盒子;你不必去关心它的内部运行如何?你只关心它需要什么样子的数据(参数、入口参数)。它的运行结果是什么?返回值是什么?

      子程序运行后,达到什么目的(是进行了复杂运算了等等),给调用的主程序的返回值(就是主程序需要的一些结果值)。

      2)我们来具体分析下这个子程序:

      子程序名称:to_table

      功能:将data1中的数据按要求整理,并写入table内存段中

      入口参数:data1内存段、table内存段。

      返回值:无

      3)这个子程序的改造,我们其实什么也不需要改动,只是将它的头加上子程序名称to_table的标号,前部是变量的保存(压栈);后部变量的恢复(弹栈),结尾加上ret。

      4)此时需要注意子程序中的标号的使用:尽量选用有意义的标号名(虽然机器不认这东东,但编译器识别它);在一个程序中(有许多子程序),标号应该不重名,否则编译器会报错!在上面程序中,看看S66标号。最好使用英语及组合名(哎,实在英语不强的,汉语拼音行不?这时候你需要一个软件:词霸)。

      5)我们把这个编译、连接后,debug看看它是否能运行。这个运行没有错误。

      6)注意程序运行后,将公司的数据(data1内存段)和table段内存(已经整理好的数据)都写入到了相应的内存中去了。

     二)由于要用到divdw这个子程序,我们稍微改动下,因为调用它的程序有个寄存器变量有冲突。

汇编代码如下:(测试状态)

assume cs:code

code segment

start:   ;实现47F4240H/7B4H

         ;入口参数赋值

     mov ax, 4240H

     mov dx, 47FH

     mov cx, 7B4H

    

     call divdw

    

     mov ax, 4c00H

     int 21H

;-------

;子程序名称:divdw

;功能:实现32位的除法,被除数存储在2个16位的寄存器中,除数存储在1个16位    ;寄存器中;余数存储在另一个16位寄存器中。解决div除法溢出的问题。

;入口参数:被除数的(ax)低16位,(dx)高16位,除数:(cx)。

;返回值:结果的商:(ax)低16位,(dx)高16位,余数:(bp)。

;-----------

divdw:                        ;子程序开始

     push ax             ;将被除数低16位先压栈保存。

     mov ax, dx          ;(ax)=(dx)

     mov dx, 0000H       ;

     div cx              ;被除数dx+ax(组合),除数cx。

     mov bx, ax          ;将H/N结果的商先保存在bx中,(bx)=0001H

    

     pop ax              ;将L值弹栈到ax

     div cx              ;此时(dx)=0005H,(ax)=4240H,组合成54240H

     mov bp, dx          ;返回值(cx)等于最终结果的余数

     mov dx, bx          ;最终结果高16位值=(bx)

     ret

;------------   

code ends

end start

      程序测试:单独编译并连接后,debug结果如下:

AX=9574 BX=0000  CX=07B4  DX=0000 SP=0000  BP=00B0  SI=0000 DI=0000

DS=0B55 ES=0B55  SS=0B65  CS=0B65 IP=000C   NV UP EI PL NZ NA PO NC

0B65:000C B8004C        MOV    AX,4C00

32位除法:47F4240H/7B4H等价于75448896/1972;

运算的结果是商是:00009574H(38260),余数00B0H(176);其中结果的商低16位存储在ax中,高16位在dx中,余数在bp中。它们就是返回值。

      三)改写dtoc为ddtoc子程序。(都叫ddtoc,我也叫这个吧!)

程序分析:

      1)程序的目的:是将一个存储在dx(高16位)+ax(低16位的)的数值组合后的十进制数值,以字符串形式,并末尾为0;按照顺序写入到data内存段中。

      2)这个子程序也适用于16位的字符转换,当然,你把16位存储ax中,(dx)=0000H。

      3)它的原理也是将这个32位的数值除以10,求它的余数;王爽老师希望我们使用jcxz指令用于判断商是否为0?在代码中,怎样判断商为0?如果高16位和低16位的值相加为0,那么这个数肯定是0。

      4)在程序运行过程中,要注意保存和及时恢复ax和dx的值,因为它们是divdw的入口参数。

      5)注意栈空间的使用,因为程序就使用了一个栈空间结构(自动的,免费的。呵呵!),它的栈帧值在头脑中要有个清楚的认识,先进后出,后进先出,栈帧都是16位的空间单元。

      6)继续熟悉调用子程序时,入口参数的意义;返回值;程序的目的。

程序代码如下:并测试

assume cs:code

data segment

   db 10 dup (0)

data ends

 

code segment

start: ;程序测试:47F4240H==75448896,只是测试

       ;ds:si指向data段

       mov ax, data

       mov ds, ax

       mov si, 0

       ;入口参数赋值

       mov ax, 4240H

       mov dx, 047FH

       call ddtoc          ;调用子程序

  

       mov ax, 4c00H

       int 21H

;-----

;子程序名称:ddtoc

;功能:将一个32位数字转换成字符串,并写入data段中。

;入口参数:ax(低16位), dx(高16位)

;返回值:无

;-----    

ddtoc:     ;保护寄存器变量值,因为下面的变量子程序都用到。

           push ax

           push cx

           push bx

           push si

           push bp

           push dx

          

           mov si, 0       ;偏移地址置零

change: mov cx, 10      ;设置除数cx=10

           mov bx, 0       ;divdw中导致bx变化,故清零

           mov bp, 0       ;余数bp=0

           call divdw      ;将(dx+ax)/cx求余数bp    

          

           push ax         ;将ax和dx压栈保护

           push dx

          

           add ax, dx      ;(dx)+(ax)整个的商的值

           mov cx, ax      ;将商赋值给cx,判断整个的商是否为0?  

              

           pop dx          ;将ax和dx弹栈恢复

           pop ax

 

           jcxz last       ;判断cx是否为0?

           add bp, 30H     ;将数字转换成ASCII码

           push bp         ;将字符的ASCII值压栈保存

          

           inc si

           jmp short change

  

   last:   ;最后一次也要转换并压栈

           add bp, 30H     ;将数字转换成ASCII码

           push bp         ; 将字符的ASCII值压栈保存

           inc si        

           ;将栈中数据倒序写入内存data段中  

           mov cx, si      ;si=数字的字符个数,设置循环次数

           mov si, 0

   write:  pop ds:[si]

           inc si

           loop write

          

            mov byte ptr ds:[si], 0 ;以0作为字符串结尾。

    ;恢复寄存器,并返回主调程序。

           pop dx

           pop bp

           pop si

           pop bx

           pop cx

           pop ax

           ret

;-----------  

;子程序名称:divdw

;功能:实现32位的除法,被除数存储在2个16位的寄存器中,除数存储在1个16位  ;寄存器中;余数存储在另一个16位寄存器中。解决div除法溢出的问题。

;入口参数:被除数的(ax)低16位,(dx)高16位,除数:(cx)。

;返回值:结果的商:(ax)低16位,(dx)高16位,余数:(bp)。

;-----------

divdw:                      ;子程序开始

       push ax             ;将被除数低16位先压栈保存。

       mov ax, dx          ;(ax)=(dx)

       mov dx, 0000H       ;

       div cx              ;被除数dx+ax(组合),除数cx。

       mov bx, ax          ;将H/N结果的商先保存在bx中,(bx)=0001H

      

       pop ax              ;将L值弹栈到ax

       div cx              ;此时(dx)=0005H,(ax)=4240H,组合成54240H

       mov bp, dx          ;返回值(cx)等于最终结果的余数

       mov dx, bx          ;最终结果高16位值=(bx)

       ret;------------      

code ends

end start

      程序结果:

      将程序编译,连接后,使用debug测试:

-d ds:0

0B65:0000 37 35 34 34 38 38 39 36-00 00 00 00 00 00 00 00   75448896........

      由于47F4240H==75448896,故结果完全正确!

      程序分析:

      1)ddtoc子程序,包含了一个divdw子程序的调用,前面我们已经将divdw子程序也调试完毕了,并且在ddtoc子程序中调用正常了。我们就不用关注divdw了,目前我们只关心ddtoc这个子程序就可以了。

      2)ddtoc子程序的入口参数是:ax(低16位), dx(高16位)。它们的组合是一个32位的数值,换句话说,将一个数存储在dx+ax中,调用ddtoc就能将这个数转换成字符串,并且存储在data内存段中。

    3)注意在汇编语言中字符串的存储方法,最后一个字节写入0(数值)

    讲解:这里虽然data段内存都是0(数值),但为了保障我们每次向data段写入的是一个字符串,必须在字符序列最后写入一个0作为结尾。

      四)考察show_str子程序:

子程序功能:将ds:si指向的内存段中的字符串写入到显存中,并设置相应的字符显示属性。

入口参数:dh-屏幕输出开始的行数;dl-屏幕输出开始所在的列数;cl-颜色及字符属性。

返回值:无

      这个子程序目前没有什么问题,就是将字符串显示在屏幕上。

 

      五)编写清屏程序:cls。

      要满足课程设计1的要求(根据书中的显示结果),我们发现dos或命令提示符窗口没有乱七八糟的显示字符,这提示我们程序首先运用了清屏的功能。这个功能将来我们可能要重复使用,故将其编写成一个子程序。

;----------

;程序名称:cls

;程序功能:dos或命令提示符窗口清屏,满黑显示

;入口参数:无

;返回值:无

;----------

cls:       push cx

           push di

           push si             ;将子程序用到的寄存器变量入栈保存

          

           mov ax, 0b800H    

           mov es, ax          ;设置es:di指向显示缓冲区内存段

          

           mov cx, 80*24       ;设置循环次数,屏幕是24行80列,这注意

           mov di, 0

          

scr_cls:   mov byte ptr es:[di+0], ' ' ;第一个字节写入空格,

           mov byte ptr es:[di+1], 0   ;第二个字节写入字符属性0(代表黑色无底)

           inc di                    

           inc di                      ;为毛这样?自己考虑(11章有提示)

           loop scr_cls

           ;恢复寄存器

           pop si            

           pop di

           pop cx            

           ret

       六)编写主程序,并调用子程序。

       我们开始实施方案,大体程序的框架是:

    数据段开始

   ……

    数据段结束

   code segment

   main:

       ;调用子程序to_table生成table数据段,包括入口参数初始化

       call to_table 

       ;调用cls子程序,清屏

       call cls

       ;将table段中内容,按照显示格式写入到data中

       ……(其中使用ddtoc和show_str、divdw子程序。)

       ;调用show_str子程序将data段字符串显示。

       call show_str

 

       mov 4c00H

       int 21H

   ;----

   to_table:

       …

       ret

   ;----

   cls:

       …

       ret

   ;----

   show_str:

       …

       ret

   ;----

   ddtoc:

       …

       ret

   ;----

   divdw:

       …

       ret

   code ends

   end main

      其他的子程序都准备好了。我们开始编写最后一个主程序:

程序代码如下:

assume cs:code

data1 segment 

   db '1975','1976','1977','1978','1979','1980','1981','1982','1983' 

   db '1984','1985','1986','1987','1988','1989','1990','1991','1992' 

   db '1993','1994','1995'

    ;以上是表示21年的21个字符串

   dd 16,22,382,1356,2390,8000,16000,24486,50065,97479,140417,197514 

   dd45980,590827,803530,1183000,1843000,2759000,3753000,4649000,5937000 

    ;以上是表示21年 公司总收入的21个dword型数据

   dw3,7,9,13,28,38,130,220,476,778,1001,1442,2258,2793,4037,5635,8226 

   dw 11542,14430,15257,17800

    ;以上是表示21公司雇员人数的21个Word型数据。

data1 ends 

table segment 

   db 21 dup ('year summ ne ?? ') 

table ends

data segment

   db 10 dup (0)

data ends

code segment

start:

       ;初始化2个数据段,并将ds指向data1,es指向table

       mov ax,data1

       mov ds,ax

       mov ax,table

       mov es,ax

       ;将数据创建生成table段

       call to_table           ;调用子程序

      

       call cls                ;清屏

      

        ;初始化2个数据段,es:bx继续指向table,ds:si指向data段。

       mov ax, table

       mov es, ax

       mov bx, 0

       mov ax, data

       mov ds, ax

       mov si, 0

      

        mov cx, 21          ;一共显示21行,初始化cx计数器

    mov dh, 1           ;初始在屏幕第几行显示,show_str的入口参数。

show_info:

       push cx             ;计数器压栈,保存cx,下面经常使用cx,保护

      

       ;显示年份

       mov ax, es:[bx]   

       mov ds:[si], ax   

       mov ax, es:[bx+2]   ;注意[bx+idata]的寻址方式

       mov ds:[si+2], ax   ;由于年份就是字符串,直接复制到data段中。   

       mov byte ptr ds:[si+4], 0   ;以0作为字符串结尾。

      

       mov dl, 3           ;设置入口参数:所在行的列数,从第几列开始显示

       mov ch, 0           ;ch清零,防止高8位不为零。

       mov cl, 2           ;字符颜色属性(此处应是二进制数0000 0010代表绿色)

        call show_str       ;调用子程序,显示字符串。

              

       ;显示总收入    

       push dx             ;后面要使用dx变量,先保存dx

       mov ax, es:[bx+5]   ;将总收入的低16位送入ax

       mov dx, es:[bx+7]   ;将总收入的高16位送入dx,ddtoc的入口参数

       call ddtoc          ;将总收入转换成字符串后写入data段中。

       pop dx              ;恢复寄存器dx,此时dh也就恢复了。

      

       mov dl, 14          ;所在行的列数

       mov ch, 0           ;ch清零,防止高8位不为零。

       mov cl, 2           ;字符颜色属性(此处应是二进制数0000 0010)

       call show_str

      

       ;显示公司总人数

       push dx

       mov ax, es:[bx+10]

       mov dx, 0

       call ddtoc

       pop dx

      

       mov dl, 34          ;所在行的列数

       mov ch, 0           ;ch清零,防止高8位不为零。

       mov cl, 2           ;字符颜色属性(此处应是二进制数0000 0010)

       call show_str

      

       ;显示总收入

       push dx

       mov ax, es:[bx+13]

       mov dx, 0

       call ddtoc

       pop dx

      

       mov dl, 50          ;所在行的列数

       mov ch, 0           ;ch清零,防止高8位不为零。

       mov cl, 2           ;颜色属性(此处应是二进制数00000010)

       call show_str

                      

       add bx, 10H         ;bx指向下一行。(table中16字节是一行)

       mov si, 0           ;si置零,ds:si总指向第一个字节。

       add dh, 1           ;累加dh(下一行显示)

      

       pop cx              ;弹栈到cx,计数器自动减1.

       loop show_info      ;循环,直到cx=0

      

       mov ax, 4c00H

       int 21H

;------

;to_table:

;功能:将data1中的数据整理并写入table段中

;入口参数:data1内存段、table内存段

;返回值:无

;------   

to_table:

       ;保护寄存器变量

       push ax

       push ds

       push es

       push bx

       push si

       push di

       push cx

       ;将to_table中需要使用的寄存器变量都保存起来。

      

       ;初始化偏址变量

       mov bx,0

       mov si,0

       mov di,0

      

       mov cx,21               ;初始化计数器

   s66:  

       ;写入年份

       mov ax,0[bx]

       mov es:0[si],ax

       mov ax,2[bx]

       mov es:2[si],ax

       ;写入空格

       mov al,20H

       mov es:4[si],al

       ;写入收入

       mov ax,84[bx]

       mov es:5[si],ax

       mov ax,86[bx]

       mov es:7[si],ax

        ;写入空格

       mov al,20H

       mov es:9[si],al

       ;雇员数

       mov ax,168[di]

       mov es:10[si],ax

       ;写入空格

       mov al,20H

       mov es:12[si],al

       ;除法后写入人均收入

       mov ax,[bx+84]

       mov dx,[bx+86]

       ;没有办法,用个bp变量吧

       mov bp,[di+168]

       div bp

       mov es:13[si],ax

       ;写入空格

       mov al,20H

       mov es:15[si],al

       ;bx、si、di变量的递增

       add bx,4

       add si,16

       add di,2

       loop s66

       ;恢复寄存器变量,并返回主调程序

       pop cx

       pop di

       pop si

       pop bx

       pop es

       pop ds

       pop ax

       ret

;-----

;子程序名称:ddtoc

;功能:将一个32位数字转换成字符串,并写入data段中。

;入口参数:ax(低16位), dx(高16位)

;返回值:无

;-----    

ddtoc:     ;保护寄存器变量值,因为下面的变量子程序都用到。

            push ax

           push cx

           push bx

           push si

           push bp

           push dx

          

           mov si, 0       ;偏移地址置零

          

   change: mov cx, 10      ;设置除数cx=10

           mov bx, 0       ;divdw中导致bx变化,故清零

           mov bp, 0       ;余数bp=0

           call divdw      ;将(dx+ax)/cx求余数bp   

          

           push ax         ;将ax和dx压栈保护

           push dx

          

           add ax, dx      ;(dx)+(ax)整个的商的值 

           mov cx, ax      ;将商赋值给cx,判断整个的商是否为0?

              

           pop dx          ;将ax和dx弹栈恢复

           pop ax

 

           jcxz last       ;判断cx是否为0?

           add bp, 30H     ;将数字转换成ASCII码

           push bp         ;将字符的ASCII值压栈保存

          

           inc si

           jmp short change

  

   last:   ;最后一次也要转换并压栈

           add bp, 30H     ;将数字转换成ASCII码

           push bp         ; 将字符的ASCII值压栈保存

           inc si        

    ;将栈中数据倒序写入内存data段中

           mov cx, si      ;si=数字的字符个数,设置循环次数

           mov si, 0

   write:  pop ds:[si]

           inc si

           loop write

          

           mov byte ptr ds:[si], 0 ;以0作为字符串结尾。

    ;恢复寄存器,并返回主调程序。

           pop dx

           pop bp

            pop si

           pop bx

           pop cx

           pop ax

           ret

;-------

;子程序名称:divdw

;功能:实现32位的除法,被除数存储在2个16位的寄存器中,除数存储在1个16位 ;寄存器中;余数存储在另一个16位寄存器中。解决div除法溢出的问题。

;入口参数:被除数的(ax)低16位,(dx)高16位,除数:(cx)。

;返回值:结果的商:(ax)低16位,(dx)高16位,余数:(bp)。

;-----------

divdw:                      ;子程序开始

       push ax             ;将被除数低16位先压栈保存。

       mov ax, dx          ;(ax)=(dx)

       mov dx, 0000H       ;

       div cx              ;被除数dx+ax(组合),除数cx。

       mov bx, ax          ;将H/N结果的商先保存在bx中,(bx)=0001H

      

       pop ax              ;将L值弹栈到ax

       div cx              ;此时(dx)=0005H,(ax)=4240H,组合成54240H

       mov bp, dx          ;返回值(cx)等于最终结果的余数

       mov dx, bx          ;最终结果高16位值=(bx)

        ret

;---------

;程序名称:cls

;程序功能:dos或命令提示符窗口清屏,满黑显示

;入口参数:无

;返回值:无

;----------

cls:       push cx

           push di

           push si             ;将子程序用到的寄存器变量入栈保存

          

           mov ax, 0b800H    

           mov es, ax          ;设置es:di指向显示缓冲区内存段

          

           mov cx, 80*24       ;设置循环次数,屏幕是24行80列

           mov di, 0

          

scr_cls:   mov byte ptr es:[di+0], ' ' ;第一个字节写入空格,

           mov byte ptr es:[di+1], 0   ;第二个字节写入字符属性0(代表黑色无底)

           inc di                     

           inc di                      ;为毛这样?自己考虑(11章有提示)

           loop scr_cls

           ;恢复寄存器

           pop si            

           pop di

           pop cx            

           ret

;------------ 

;show_str功能 :按行和列及字符属性显示字符串 

    ;入口参数:dh-行数、dl-列数、cl-字符属性, ds:si

    ;返回值:无

show_str:  push dx

           push cx

           push si           

           push bx           

           push es             ;将子程序用到的寄存器入栈

          

           mov ax, 0b800H

           mov es, ax          ;设置显示缓冲区内存段

          

           mov ax, 0           ;(ax)= 0,防止高位不为零 

           mov al, 160         ;0a0H-   160字节/行

           mul dh              ;相对于0b800:0000第dh行偏移量

           mov bx, ax          ;将第(dh)行的偏移地址送入bx,bx代表行偏移

           mov ax, 0

           mov al, 2           ;列的标准偏移量是2个字节

           mul dl              ;同一行列的偏移量,尽量使用乘法,(al)=列偏移

           add bx, ax          ;最终获得偏移地址(bx)=506H

           mov di,0            ;将di作为每个字符的偏移量

           mov al, cl          ;将字符属性写入al中

           mov ch, 0           ;将cx高8位设置为0

          

   show:   mov cl, ds:[si]     ;将字符串单个字符读入cl中

           jcxz ok             ;判断字符串是否为零。

           mov es:[bx+di+0], cl    ;在显示缓冲区中写入字符

           mov es:[bx+di+1], al    ;在显示缓冲区中写入字符属性

           add di, 2

           inc si

           jmp short show

  

       ok: ;字符串字符为0,结尾

           pop es              ;恢复寄存器

           pop bx

           pop si            

           pop cx

           pop dx            

           ret

code ends

end start

最终结果是:

  1975       16                  3               5

  1976       22                  7               3

  1977       382                 9               42

  1978       1356                13              104

  1979       2390                28              85

  1980       8000                38              210

  1981       16000               130             123

  1982       24486               220             111

  1983       50065               476             105

  1984       97479               778             125

  1985       140417              1001            140

  1986       197514              1442            136

  1987       345980              2258            153

  1988       590827              2793            211

  1989       803530              4037            199

  1990       1183000             5635            209

  1991       1843000             8226            224

  1992       2759000             11542           239

  1993       3753000             14430           260

  1994       4649000             15257           304

  1995       5937000             17800           333

      程序总结:

      1)在子程序中,如果有破坏(修改)寄存器变量的行为,我们要及时保护寄存器变量的值,并及时恢复。

      2)加深栈的理解:push和pop是成对的,要避免push和pop操作的不是一个字单元,这样会导致程序错误。

      3)理解子程序的黑盒子理论。考虑入口参数、功能、返回值。

 

      4)内存的寻址方式。

  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
实验Power idea公司数据按照一定的格式在屏幕上显示出来的汇编语言实验设计思路如下: 1. 确定数据类型和数据范围:首先需要确定Power idea公司数据的类型和数据范围,包括公司名称、销售额、利润、员工数等信息。根据实验要求,可以将公司名称定义为字符串类型,销售额和利润定义为带小数点的实数类型,员工数定义为整数类型。 2. 定义数据结构和变量:根据数据类型和数据范围,需要定义相应的数据结构和变量。比如,可以定义一个结构体来表示Power idea公司数据,包括公司名称、销售额、利润、员工数等成员变量。同时,需要定义相应的变量来存储和处理这些数据。 3. 定义显示格式:根据实验要求,需要将Power idea公司数据按照一定的格式显示在屏幕上,比如按照表格的形式排列,每个数据之间用适当的分隔符隔开等。因此需要定义显示格式,包括表头、表格边框、数据显示格式等。 4. 数据处理:将Power idea公司数据按照要求处理,比如计算销售额和利润的增长率、计算平均员工数等。这些处理可以在代码实现,也可以在数据录入时预处理。 5. 数据显示:使用DOS提供的字符输出函数,将Power idea公司数据按照定义的显示格式输出到屏幕上。在输出过程,需要注意控制输出位置和格式,以保证输出结果的美观和可读性。 6. 测试和调试:对程序进行测试和调试,保证程序的正确性和稳定性。可以使用一些测试用例来验证程序的功能和性能,使用调试工具来定位和解决问题。 在实现过程,需要注意以下几点: 首先,需要保证代码的逻辑清晰,将整个实验分为定义数据、定义显示格式、数据处理和数据显示四个步骤,每个步骤的功能清晰明确。 其次,需要注意汇编语言的特性,比如寄存器的使用、内存地址的访问等问题,确保代码的正确性和可靠性。 同时,还需要注意代码的可读性和可维护性,尽量使用易于理解的变量名和注释,以便今后进行修改和扩展。 最后,需要注意输出结果的美观和可读性,保证输出格式规范、对齐,同时添加适当的分隔符和表格边框,使结果更加易于阅读和理解。 综上所述,将实验Power idea公司数据按照一定的格式在屏幕上显示出来的汇编语言实验设计思路清晰明了,步骤清晰,同时注重代码的可维护性和输出结果的美观性,是一份优秀的实验设计思路。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值