千哥读书笔记:汇编语言(王爽第四版)第10章课程设计1

先来看看这个课程设计1的要求:

本课程设计的难度,是到目前为止最难的。

首先,涉及到第8章的实验七的内容,也就是对Power idea公司的数据的分析和程序的编写,关于这一部分内容,可以复习一下千哥前面写的笔记:《千哥读书笔记:汇编语言(王爽第四版)第8章实验七》

其次,涉及到本章的三个程序的编写,即“显示字符串(show_str)”、“解决除法溢出的问题(divdw)”、“数值显示(dtoc)”。

关于这一部分内容,可以复习一下千哥前面写的两篇笔记:

《千哥读书笔记:汇编语言(王爽第四版)第10章实验10第2题divdw程序编写》

《千哥读书笔记:汇编语言(王爽)第10章实验10三个子程序的源码》

进一步说,如果不搞清楚以上内容,要想写出课程设计1的程序,几乎是不可能完成的任务。

接下来,根据前面的基础内容,捋一下编写这个程序的思路:

1、Power idea公司的相关数据,也就是年份、收入、雇员人数、人均收入这4组数据,要向b800H显示缓冲区输出,最终显示在屏幕上。因此编程的思路和《汇编语言(王爽第四版)第8章实验七》是完全不一样的,实验七是要求将数据存放在table段中,并覆盖原先的“year summ ne ??”字符串。

2、整个编程的流程,主要分为两个大的部分:一是将年份、收入、雇员人数、人均收入这4组数据进行初始化,将相关数据转化为以0结尾的字符串;二是将这些字符串按照上述字段顺序排列,显示在屏幕上。

3、对于该流程的第一部分,需建立相应数据的缓冲区,将转化后的字符串保存在相应的缓冲区;对于该程序的第二部分,从相应的缓冲区中读取转化后的字符串,然后输出到b800H显示缓冲区,完成字符串在屏幕的显示。

4、在原书的设定中,收入数据定义了21个dword型数据。每一个数据(比如最后一个收入数据5937000,16进制为5A9768H)必须用到两个寄存器(AX、DX)作为被除数、10作为除数,进行32位的除法运算,以获得在字符串转化过程中所需的余数(即:字符=余数+30H)。但一些数据在进行除法运算的时候,会导致溢出,为防止这一过程产生,就会调用到divdw子程序。因此,对divdw子程序的理解和调用,在编程的过程中非常重要。

5、对于其他不会产生32位除法溢出的数据,只需要调用原生的DIV指令进行32位除法运算,得到字符串转化过程中的所需的余数即可。

6、在所有字段的字符串初始化完毕之后,调用show_str子程序,将相关字符串显示在屏幕上。

基本的设计流程如下图:

根据以上思路,贴出一部分代码:

一、建立相应的字符串缓冲区

===================

assume cs:codesg

data 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型数据

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

dw 11542,14430,15257,17800

;以上是表示21年雇员人数的21个word型数据

data ends

year_buffer segment

db 105 dup(0);年份数据缓冲区,每个年份数据占4个字节,再加上1个0占1个字节,21组年份数据共105个字节

year_buffer ends

gross_buffer segment

db 147 dup(0);收入数据缓冲区,以最大数值5937000的字符串长度7为标准,每个数字的字符占1个字节,21组收入数据占147个字节

gross_buffer ends

employee_buffer segment

db 105 dup(0);雇员人数数据缓冲区,以最大数值17800的字符串长度5为标准,每个数字的字符占1个字节,21组雇员人数数据共105个字节

employee_buffer ends

average_buffer segment

db 63 dup(0);人均收入数据缓冲区,以最大数值5937000/17800=333的字符串长度3为标准,每个数字的字符占1个字节,21组人均收入数据共63个字节

average_buffer ends

===================

在这一步很简单,建立了四个缓冲区:year_buffer 、gross_buffer、employee、average。建立的过程中,进行初步测算,确立每个缓冲区的大小。

二、始化21个年份数据

====================

codesg segment

start:

;初始化21个年份数据,让每个年份数据的尾部加上0使其成为一个字符串,然后保存在year_buffer中

mov ax,data

mov ds,ax

mov bx,0

mov si,0

mov di,0

mov cx,21

year_save:

mov ax,year_buffer

mov es,ax

mov ax,ds:[si]

mov es:[bx],ax

mov ax,ds:[si+2]

mov es:[bx+2],ax

mov ax,0;每个年份数据的尾部加上0

mov es:[bx+4],al

add si,4

add bx,5

loop year_save

====================

这一步相对比较简单,因为年份数据本身是以字符的形式保存在data段的,所以在读取相应的字符之后,在其尾部加上0,然后保存在year_buffer中即可。

三、初始化21个收入数据

====================

;初始化21个收入数据,将每组数据转换为字符串,然后保存在gross_buffer中

mov ax,data

mov ds,ax

mov ax,gross_buffer

mov es,ax

mov bx,0

mov si,84;21个收入数据,从第84字节开始

mov di,0

mov cx,21

gross_save:

push cx

mov ax,ds:[si];将每一个收入数据的低16位数据保存在AX中

mov dx,ds:[si+2];将每一个收入数据的高16位数据保存在DX中

mov bp,0;BP作为计数器,用于记录每次调用divdw的次数

divdw_loop:

mov cx,0ah;让CX为10,作为除数

call divdw;调用防止溢出的divdw子程序,每次调用一次,获得一个余数,最后一次调用获得AX的低16位商0

inc bp;BP每次加1,记录每次调用divdw循环的次数

push cx;将每次获得的余数保存在栈中

mov cx,ax;将每次调用divdw获得的商AX的低16位数据保存在CX中,如果商AX为0,表示最后一次调用

jcxz divdw_ok;比较CX是否为0,为0表示最后一次调用

jmp short divdw_loop

divdw_ok:

mov cx,bp;将每次读取一组收入数据调用的divdw程序的次数,保存在CX中,作为出栈的次数

gross_trans:

pop ax;将每次获得的余数出栈,保存在AX中

add ax,30h;将余数加上30H,得到余数对应的字符

mov es:[di],ax;将余数字符保存在缓冲区中

inc di;DI每次加1,位移1个字节

loop gross_trans

mov al,0;将0保存在AL中,作为字符串的结束符

inc di;DI再加1,指向最后一个0字符

mov es:[di],al;将0保存在缓冲区中

add si,4;在21个收入数据中,每次循环SI加4,指向下一个收入数据

pop cx

loop gross_save

===================

这一步相对比较复杂,特别是涉及对divdw子程序的理解和调用,其基本原理在我写的《千哥读书笔记:汇编语言(王爽第四版)第10章实验10第2题divdw程序编写》有详细介绍,这里不作过多阐述。

总之,调用一次divdw子程序,就会产生一个与相关数据对应的余数。比如最后一个收入数据5937000,第一次调用的时候会产生余数0,第二次调用的时候会产生余数0,以此类推,在最后一次调用之后,会产生余数5。而这些余数最终于30H相加,就会得到相应数字的字符串编码。

在这个过程中,必须将调用divdw子程序所产生的余数,压入栈中,然后通过一个循环体来弹出这些余数。而根据栈操作“后进先出”的原则,就可以按照人类书写数字的习惯,从左到右将相关数字进行排列。

比如5937000,其压栈的顺序是0,0,0,7,3,9,5;出栈的顺序则是5,9,3,7,0,0,0。通过这种方式,将转化后的字符16进制值(余数+30H),在gross_buffer缓冲区中,从低字节向高字节方向保存。

之所以用到一个寄存器BP,来记录并保存调用divdw子程序的次数,是因为每次进行一个收入数据(比如5937000)到字符串的转化过程中,每个收入数据的字符串长度是不一样的。

每一个收入数据(比如5937000)进行字符串转化时,当寄存器BP获得调用divdw子程序的次数后,再通过BP将循环次数赋值给CX,最后通过 loop gross_trans循环,实现每一个转化后的字符串在gross_buffer缓冲区的保存。

下面是divdw子程序代码:

===================

;防止溢出的除法子程序,利用公式:X/N = int(H/N)*65536 + [rem(H/N)*65536+L]/N。例如:被除数为5A9768H(十进制5937000),除数是0aH(十进制10)

divdw:

mov bx,ax;将AX中保存了被除数5A9768H的低16位数据9768H,保存到BX中,BX作为临时变量。

mov ax,dx;将DX中保存了被除数5A9768H的高16位数据005aH,保存到AX中,AX作为除法运算被除数的低16位。

mov dx,0;将DX清零,作为除法运算被除数的高16位。

div cx ;CX作为除数,这时被除数是32位的,即0000005aH,DX是0000H,AX是005aH。运行后,DX存放余数,AX存放商。

push ax ;通过压栈操作,保存AX中的商,即是最后商的高16位。

mov ax,bx ;将临时变量BX中的被除数5A9768H的低16位数据9768H,恢复到AX,作为新一轮除法运算中被除数的低16位数据。

div cx ;此时在上一轮除法运算中DX中的余数,作为被除数的高16位,AX中的数据作为被除数的低16位,进行除法运算。

mov cx,dx ;得到的余数保存在DX中,然后通过DX保存到cx

pop dx ;将最后商的高16位弹出栈,保存在DX中,成为最后商的高16位,低16位已经在ax中。

ret

===================

而本课程设计中的divdw子程序,仅仅是对原有《千哥读书笔记:汇编语言(王爽第四版)第10章实验10第2题divdw程序编写》的divdw子程序进行了略微的修改,以适应始化21个收入数据的需要。

四、初始化21个雇员人数数据

===================

;初始化21个雇员人员数据,将每组数据转换为字符串,然后保存在employee_buffer中

mov ax,data

mov ds,ax

mov ax,employee_buffer

mov es,ax

mov si,168;21个雇员人数数据,从第168字节开始

mov di,0

mov cx,21

employee_save:

push cx

mov ax,ds:[si];将每一个雇员人数数据的低16位数据保存在AX中

mov bp,0;BP作为计数器,用于记录每次调用div的次数

mov bx,0ah;让bX为10,作为除数

employ_div:

mov dx,0;使用32位除以16位的除法,被除数的高16位设置为0,存放在DX中,被除数的低16位存放在AX中

div bx;32位除以16位除法,商默认保存在AX中,余数默认保存在DX中

inc bp;BP每次加1,记录每次调用div循环的次数

mov cx,ax;将商赋值给CX

push dx;将DX保存的余数压入栈中

jcxz ediv_ok;如果CX为零就跳出循环,进入ediv_ok

jmp short employ_div

ediv_ok:

mov cx,bp;将每次调用div的次数,保存在CX中,作为出栈的次数

employee_trans:

pop ax;将每次获得的余数出栈,保存在AX中

add ax,30h;将余数加上30H,得到余数对应的字符

mov es:[di],ax;将余数字符保存在缓存区中

inc di;DI每次加1,位移1个字节

loop employee_trans

mov al,0;将0保存在aL中,作为字符串的结束符

inc di;DI再加1,指向最后一个0字符

mov es:[di],al;将0保存在缓存区中

add si,2;在21个雇员人数数据中,每次循环SI加2,指向下一个雇员人数数据

pop cx

loop employee_save

===================

这一步的基本思路,和第三步(始化21个收入数据)差不多,只是运用除法运算的方式上有所差别。由于21个雇员人数数据都不是超长数据,在进行32位除法的时候,不会产生溢出,因此直接调用原生DIV指令就可以达到相应效果。

五、初始化21个人均收入数据

===================

;初始化21个人均收入数据,将每组数据转换为字符串,然后保存在average_buffer中

mov ax,data

mov ds,ax

mov ax,average_buffer

mov es,ax

mov si,84;21个收入数据,从第84字节开始

mov bx,168;21个雇员人数数据,从第168字节开始

mov di,0

mov cx,21

average_save:

push cx

mov ax,ds:[si];将每一个收入数据的低16位数据保存在AX中,AX保存被除数的低16位数据

mov dx,ds:[si+2];将每一个收入数据的高16位数据保存在DX中,DX保存被除数的高16位数据

div word ptr ds:[bx];21个雇员人数数据,从第168字节开始,每个雇员人数作为除数,商默认保存在AX中

push bx;将bx保存在栈中

mov bx,0ah;让bX为10,在标号average_div中作为除数,进行字符串转化

mov bp,0;BP作为计数器,用于记录每次调用div的次数

average_div:

mov dx,0;使用32位除以16位的除法,被除数的高16位设置为0,存放在DX中,被除数的低16位存放在AX中

div bx;32位除以16位除法,商默认保存在AX中,余数默认保存在DX中

inc bp;BP每次加1,记录每次调用div循环的次数

mov cx,ax;将商赋值给CX

push dx;将DX保存的余数压入栈中

jcxz adiv_ok;如果CX为零就跳出循环,进入adiv_ok

jmp short average_div

adiv_ok:

mov cx,bp;将每次调用div的次数,保存在CX中,作为出栈的次数

average_trans:

pop ax;将每次获得的余数出栈,保存在AX中

add ax,30h;将余数加上30H,得到余数对应的字符

mov es:[di],ax;将余数字符保存在average_buffer缓存区中

inc di;DI每次加1,位移1个字节

loop average_trans

mov al,0;将0保存在AL中,作为字符串的结束符

inc di;DI再加1,指向最后一个0字符

mov es:[di],al;将0保存在average_buffer缓存区中

add si,4;在21个收入数据中,每次循环SI加4,指向下一个收入数据

pop bx;恢复BX数据

add bx,2;在21个雇员人数数据中,每次循环BX加2,

pop cx

loop average_save

===================

在这一步的字符串转化和存储过程中,由于21个人均收入数据并不是一个事先设定好的数据,需要通过收入数据和雇员人数进行除法运算才能得到,因此才有了这段代码:

mov ax,ds:[si];将每一个收入数据的低16位数据保存在AX中,AX保存被除数的低16位数据

mov dx,ds:[si+2];将每一个收入数据的高16位数据保存在DX中,DX保存被除数的高16位数据

div word ptr ds:[bx];21个雇员人数数据,从第168字节开始,每个雇员人数作为除数,商默认保存在AX中

通过对AX、DX的赋值,然后直接除以DATA段中保存了雇员人数数据的内存单元内容,即div word ptr ds:[bx],获得相应的商,这个商值就是人均收入数据,默认保存在AX中,然后又可以进行下一步的字符串转化。

六、逐次向显示缓冲区输出年份、收入、雇员人数、人均收入这4组数据转化后的字符串

===================

;调用show_year子程序,显示年份数据。

mov bx,644;从显示缓冲区的第4行第2列开始

mov ax,year_buffer;将year_buffer地址保存在AX中,并将DS指向该地址

mov ds,ax

call show_str

;调用show_str子程序,显示收入数据。

mov bx,662;从显示缓冲区的第4行第11列开始

mov ax,gross_buffer;将gross_buffer地址保存在AX中,并将DS指向该地址

mov ds,ax

call show_str

;调用show_str子程序,显示总雇员人数数据。

mov bx,686;从显示缓冲区的第4行第23列开始

mov ax,employee_buffer;将employee_buffer地址保存在AX中,并将DS指向该地址

mov ds,ax

call show_str

;调用show_str子程序,显示人均收入数据。

mov bx,706;从显示缓冲区的第4行第33列开始

mov ax,average_buffer;将average_buffe地址保存在AX中,并将DS指向该地址

mov ds,ax

call show_str

mov ax,4c00h

int 21h

===================

这一步没什么可多讲的,无非就是计算好每一组字符串应在屏幕显示的位置,然后调用show_str子程序,下面是show_str子程序代码:

===================

;字符串显示子程序

show_str:

mov ax,0b800h;将显示缓冲区地址保存在AX中,并将ES指向该地址

mov es,ax

mov al,111B;将白色属性00000111赋值给al

mov cx,21

mov si,0

str_loop1:

push cx

mov di,0

str_loop2:

mov cl,ds:[si];按字节读取1个字符

mov ch,0

jcxz str_loop_ok

mov ch,al;将白色属性00000111赋值给CH

mov es:[bx+di],cx;

inc si

add di,2;di的位移为2,也是因为每一列的字符,实际占了两个字节

jmp short str_loop2

str_loop_ok:

pop cx

inc si

add bx,160

loop str_loop1

ret

codesg ends

end start

===================

以上即是千哥对本课程设计的所有思路和方法,当然,如果你有更好的思路,欢迎交流。

最后,贴出完整的代码:

===================

assume cs:codesg

data 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型数据

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

dw 11542,14430,15257,17800

;以上是表示21年雇员人数的21个word型数据

data ends

year_buffer segment

db 105 dup(0);年份数据缓存区,每个年份数据占4个字节,再加上1个0占1个字节,21组年份数据共105个字节

year_buffer ends

gross_buffer segment

db 147 dup(0);收入数据缓存区,以最大数值5937000的字符串长度7为标准,每个数字的字符占1个字节,21组收入数据占147个字节

gross_buffer ends

employee_buffer segment

db 105 dup(0);雇员人数数据缓存区,以最大数值17800的字符串长度5为标准,每个数字的字符占1个字节,21组雇员人数数据共105个字节

employee_buffer ends

average_buffer segment

db 63 dup(0);人均收入数据缓存区,以最大数值5937000/17800=333的字符串长度3为标准,每个数字的字符占1个字节,21组人均收入数据共63个字节

average_buffer ends

codesg segment

start:

;初始化21个年份数据,让每个年份数据的尾部加上0使其成为一个字符串,然后保存在year_buffer中

mov ax,data

mov ds,ax

mov bx,0

mov si,0

mov di,0

mov cx,21

year_save:

mov ax,year_buffer

mov es,ax

mov ax,ds:[si]

mov es:[bx],ax

mov ax,ds:[si+2]

mov es:[bx+2],ax

mov ax,0;每个年份数据的尾部加上0

mov es:[bx+4],al

add si,4

add bx,5

loop year_save

;初始化21个收入数据,将每组数据转换为字符串,然后保存在gross_buffer中

mov ax,data

mov ds,ax

mov ax,gross_buffer

mov es,ax

mov bx,0

mov si,84;21个收入数据,从第84字节开始

mov di,0

mov cx,21

gross_save:

push cx

mov ax,ds:[si];将每一个收入数据的低16位数据保存在AX中

mov dx,ds:[si+2];将每一个收入数据的高16位数据保存在DX中

mov bp,0;BP作为计数器,用于记录每次调用divdw的次数

divdw_loop:

mov cx,0ah;让CX为10,作为除数

call divdw;调用防止溢出的divdw子程序,每次调用一次,获得一个余数,最后一次调用获得AX的低16位商0

inc bp;BP每次加1,记录每次调用divdw循环的次数

push cx;将每次获得的余数保存在栈中

mov cx,ax;将每次调用divdw获得的商AX的低16位数据保存在CX中,如果商AX为0,表示最后一次调用

jcxz divdw_ok;比较CX是否为0,为0表示最后一次调用

jmp short divdw_loop

divdw_ok:

mov cx,bp;将每次读取一组收入数据调用的divdw程序的次数,保存在CX中,作为出栈的次数

gross_trans:

pop ax;将每次获得的余数出栈,保存在AX中

add ax,30h;将余数加上30H,得到余数对应的字符

mov es:[di],ax;将余数字符保存在缓存区中

inc di;DI每次加1,位移1个字节

loop gross_trans

mov al,0;将0保存在aL中,作为字符串的结束符

inc di;DI再加1,指向最后一个0字符

mov es:[di],al;将0保存在缓存区中

add si,4;在21个收入数据中,每次循环SI加4,指向下一个收入数据

pop cx

loop gross_save

;初始化21个雇员人员数据,将每组数据转换为字符串,然后保存在employee_buffer中

mov ax,data

mov ds,ax

mov ax,employee_buffer

mov es,ax

mov si,168;21个雇员人数数据,从第168字节开始

mov di,0

mov cx,21

employee_save:

push cx

mov ax,ds:[si];将每一个雇员人数数据的低16位数据保存在AX中

mov bp,0;BP作为计数器,用于记录每次调用div的次数

mov bx,0ah;让bX为10,作为除数

employ_div:

mov dx,0;使用32位除以16位的除法,被除数的高16位设置为0,存放在DX中,被除数的低16位存放在AX中

div bx;32位除以16位除法,商默认保存在AX中,余数默认保存在DX中

inc bp;BP每次加1,记录每次调用div循环的次数

mov cx,ax;将商赋值给CX

push dx;将DX保存的余数压入栈中

jcxz ediv_ok;如果CX为零就跳出循环,进入ediv_ok

jmp short employ_div

ediv_ok:

mov cx,bp;将每次调用div的次数,保存在CX中,作为出栈的次数

employee_trans:

pop ax;将每次获得的余数出栈,保存在AX中

add ax,30h;将余数加上30H,得到余数对应的字符

mov es:[di],ax;将余数字符保存在缓存区中

inc di;DI每次加1,位移1个字节

loop employee_trans

mov al,0;将0保存在aL中,作为字符串的结束符

inc di;DI再加1,指向最后一个0字符

mov es:[di],al;将0保存在缓存区中

add si,2;在21个雇员人数数据中,每次循环SI加2,指向下一个雇员人数数据

pop cx

loop employee_save

;初始化21个人均收入数据,将每组数据转换为字符串,然后保存在average_buffer中

mov ax,data

mov ds,ax

mov ax,average_buffer

mov es,ax

mov si,84;21个收入数据,从第84字节开始

mov bx,168;21个雇员人数数据,从第168字节开始

mov di,0

mov cx,21

average_save:

push cx

mov ax,ds:[si];将每一个收入数据的低16位数据保存在AX中,AX保存被除数的低16位数据

mov dx,ds:[si+2];将每一个收入数据的高16位数据保存在DX中,DX保存被除数的高16位数据

div word ptr ds:[bx];21个雇员人数数据,从第168字节开始,每个雇员人数作为除数,商默认保存在AX中

push bx;将bx保存在栈中

mov bx,0ah;让bX为10,在标号average_div中作为除数,进行字符串转化

mov bp,0;BP作为计数器,用于记录每次调用div的次数

average_div:

mov dx,0;使用32位除以16位的除法,被除数的高16位设置为0,存放在DX中,被除数的低16位存放在AX中

div bx;32位除以16位除法,商默认保存在AX中,余数默认保存在DX中

inc bp;BP每次加1,记录每次调用div循环的次数

mov cx,ax;将商赋值给CX

push dx;将DX保存的余数压入栈中

jcxz adiv_ok;如果CX为零就跳出循环,进入adiv_ok

jmp short average_div

adiv_ok:

mov cx,bp;将每次调用div的次数,保存在CX中,作为出栈的次数

average_trans:

pop ax;将每次获得的余数出栈,保存在AX中

add ax,30h;将余数加上30H,得到余数对应的字符

mov es:[di],ax;将余数字符保存在average_buffer缓存区中

inc di;DI每次加1,位移1个字节

loop average_trans

mov al,0;将0保存在AL中,作为字符串的结束符

inc di;DI再加1,指向最后一个0字符

mov es:[di],al;将0保存在average_buffer缓存区中

add si,4;在21个收入数据中,每次循环SI加4,指向下一个收入数据

pop bx;恢复BX数据

add bx,2;在21个雇员人数数据中,每次循环BX加2,

pop cx

loop average_save

;调用show_year子程序,显示年份数据。

mov bx,644;从显示缓冲区的第4行第2列开始

mov ax,year_buffer;将year_buffer地址保存在AX中,并将DS指向该地址

mov ds,ax

call show_str

;调用show_str子程序,显示收入数据。

mov bx,662;从显示缓冲区的第4行第11列开始

mov ax,gross_buffer;将gross_buffer地址保存在AX中,并将DS指向该地址

mov ds,ax

call show_str

;调用show_str子程序,显示总雇员人数数据。

mov bx,686;从显示缓冲区的第4行第23列开始

mov ax,employee_buffer;将employee_buffer地址保存在AX中,并将DS指向该地址

mov ds,ax

call show_str

;调用show_str子程序,显示人均收入数据。

mov bx,706;从显示缓冲区的第4行第33列开始

mov ax,average_buffer;将average_buffe地址保存在AX中,并将DS指向该地址

mov ds,ax

call show_str

mov ax,4c00h

int 21h

;防止溢出的除法子程序,利用公式:X/N = int(H/N)*65536 + [rem(H/N)*65536+L]/N。例如:被除数为5A9768H(十进制5937000),除数是0aH(十进制10)。

divdw:

mov bx,ax;将AX中保存了被除数5A9768H的低16位数据9768H,保存到BX中,BX作为临时变量。

mov ax,dx;将DX中保存了被除数5A9768H的高16位数据005aH,保存到AX中,AX作为除法运算被除数的低16位。

mov dx,0;将DX清零,作为除法运算被除数的高16位。

div cx ;CX作为除数,这时被除数是32位的,即0000005aH,DX是0000H,AX是005aH。运行后,DX存放余数,AX存放商。

push ax ;通过压栈操作,保存AX中的商,即是最后商的高16位。

mov ax,bx ;将临时变量BX中的被除数5A9768H的低16位数据9768H,恢复到AX,作为新一轮除法运算中被除数的低16位数据。

div cx ;此时在上一轮除法运算中DX中的余数,作为被除数的高16位,AX中的数据作为被除数的低16位,进行除法运算。

mov cx,dx ;得到的余数保存在DX中,然后通过DX保存到cx

pop dx ;将最后商的高16位弹出栈,保存在DX中,成为最后商的高16位,低16位已经在AX中。

ret

;字符串显示子程序

show_str:

mov ax,0b800h;将显示缓冲区地址保存在AX中,并将ES指向该地址

mov es,ax

mov al,111B;将白色属性00000111赋值给al

mov cx,21

mov si,0

str_loop1:

push cx

mov di,0

str_loop2:

mov cl,ds:[si];按字节读取1个字符

mov ch,0

jcxz str_loop_ok

mov ch,al;将白色属性00000111赋值给CH

mov es:[bx+di],cx;

inc si

add di,2;di的位移为2,也是因为每一列的字符,实际占了两个字节

jmp short str_loop2

str_loop_ok:

pop cx

inc si

add bx,160

loop str_loop1

ret

codesg ends

end start

===================

将该代码命名为:p10104.asm,编译通过,然后在DOSBOX下运行,如下图所示:

完美实现本课程设计要求。打完收工。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值