;2015年3月29日23时31分51秒
assume cs:code
;------------------------------------------------------------------------------------------------------------
data1 segment
db '1975','1976','1977','1978','1979','1980','1981'
db '1982','1983','1984','1985','1986','1987','1988'
db '1989','1990','1991','1992','1993','1994','1995'
;以上是表示21年的21个字符
;这里虽然定义的是DB型数据,但因为每个年份都是四位数,所以一个年份就占了4个字节,也就是两个字。
;所以,这段数据就占据了42个字,84个字节。
;现在我总算明白了什么叫字符型数字了,字符型数字就是以ASCII码的十六进制存储在内存中的数据,也就是说要+30H
;所以把字符型数字转换成数字就是-30H,相反,把数字转换成字符型数据就是+30H
;字符型数字就是以数字的ASCII码的数值存储的数据,它并不是真正的数字,而是数字的ASCII码数值
dd 16,22,382,1356,2390,8000,16000
dd 24486,50065,97479,140417,197514,345980,590827
dd 803530,1183000,1843000,2759000,3753000,4649000,5937000
;以上是表示21年公司总收入的21个DWORD型数据
;DD就是DOUBLE WORD型数据,就是双字,21*2=42个字,84个字节。和上一段占据的内存一样大小。
dw 3,7,9,13,28,38,130
dw 220,476,778,1001,1442,2258,2793
dw 4037,5635,8226,11542,14430,15257,17800
;以上是表示21年公司雇员人数的21个WORD型数据
;21个字,也就是42个字节,比上两段少一半。
data1 ends
;------------------------------------------------------------------------------------------------------------
;------------------------------------------------------------------------------------------------------------
data2 segment
db 16 dup(0)
;这个是用来存放HTOD的商值的,起始偏移地址是210
data2 ends
;------------------------------------------------------------------------------------------------------------
;------------------------------------------------------------------------------------------------------------
stack segment
db 16 dup(0)
stack ends
;------------------------------------------------------------------------------------------------------------
;------------------------------------------------------------------------------------------------------------
code segment
start: mov ax,stack;
mov ss,ax;
mov sp,16;设置栈段
;------------------------------------------------------------------------------------------------------------
mov ax,data1;
mov ds,ax;设置DATA1段的段地址
mov ax,0b800h;
mov es,ax;设置显存段地址
mov bx,0;设置DATA0段偏移地址
mov bp,282h;设置显存偏移地址,行4(280H)列1(2H),从0开始
mov cx,21;设置第1个循环的次数,因为是21行,所以CX为21
xh1: push cx;把第1个CX的值入栈
push bp;把显存偏移地址的值入栈
push bx;把数据段偏移地址的值入栈
mov cx,4;设置第2个循环,因为是4列,所以CX为4
xh1_1: call s_str;进入这个子程序,PUSH IP
inc bx;数据段偏移地址自增1
inc bp;
inc bp;显存段偏移地址自增2
loop xh1_1;如果CX不为0,那么跳转到标号处继续执行,如果为0则向下执行
pop bx;把数据段偏移地址的值出栈
pop bp;把显存偏移地址的值出栈
pop cx;把第1个CX的值出栈
add bx,4;数据段偏移地址+4,指向下一个年份
add bp,160;显存段偏移地址+160,指向下一行
loop xh1;如果CX不为0,那么跳转到标号处继续执行,如果为0则向下执行
;以上为显示年份的程序
;------------------------------------------------------------------------------------------------------------
;------------------------------------------------------------------------------------------------------------
mov ax,data2;
mov ds,ax;这是存放H2D的余数的段地址
mov bx,84;因为是前面84个字节不是,所以从85个字节开始,(0-83为84个字节),所以这里的84=85
mov bp,290h;设置显存偏移地址,行4(280H)列8(10H),从0开始
mov cx,21;循环21次
xh2: push cx;CX入栈,因为后面要用到CX
push bp;BP入栈,原因同上
mov ax,data1;
mov es,ax;这是被除数的数据段地址
call r_dw_h2d;这是双字数据的H2D,进入这个子程序,PUSH IP
mov ax,0b800h;显存段地址
mov es,ax;设置显存段地址,因为显存段地址用到了BP,所以必须加上段前缀
pop bp;把BP的值出栈,因为下面要用到
call s_h2d;这是显示H2D,进入这个子程序,PUSH IP
add bx,4;被除数每次自增4,因为是双字,4个字节
add bp,160;BP每次自增一行,一行为160个字节
pop cx;把CX的值出栈
loop xh2;如果CX不为0,那么就继续进行
;以上为显示总收入的程序
;------------------------------------------------------------------------------------------------------------
;------------------------------------------------------------------------------------------------------------
mov bx,168;因为是前面168个字节不是,所以从168个字节开始,(0-167为168个字节),所以这里的168=169
mov bp,2a4h;设置显存偏移地址,行4(280H)列18(24H),从0开始
mov cx,21;循环21次
xh3: push cx;CX入栈,因为后面要用到CX
push bp;BP入栈,原因同上
mov ax,data1;
mov es,ax;这是被除数的数据段地址
mov si,1;这是存放H2D的商值的DATA2段偏移地址
mov di,0ah;这是除数
mov ax,es:[bx];因为是字除,所以只需要复制一个16位寄存器就行了
mov dx,0;高16位的寄存器清零
call r_divw;这是字H2D的子程序
mov ax,0b800h;显存段地址
mov es,ax;设置显存段地址,因为显存段地址用到了BP,所以必须加上段前缀
pop bp;把BP的值出栈,因为下面要用到
call s_h2d;这是显示H2D,进入这个子程序,PUSH IP
add bx,2;被除数每次自增4,因为是双字,4个字节
add bp,160;BP每次自增一行,一行为160个字节
pop cx;把CX的值出栈
loop xh3;如果CX不为0,那么就继续进行
;以上为显示雇员数的程序
;------------------------------------------------------------------------------------------------------------
;------------------------------------------------------------------------------------------------------------
mov ax,data1;
mov es,ax;这是被除数和除数的数据段地址
mov bx,84;这是被除数的偏移地址
mov si,168;这是除数的偏移地址
mov bp,2b4h;设置显存偏移地址,行4(280H)列26(34H),从0开始
mov cx,21;因为21列,所以循环21次
xh4: push cx;CX入栈,因为后面有JCXZ的操作,要用到CX
push si;SI的值入栈,因为后面要SI当余数的偏移地址
push es;ES的值入栈,因为后面要用ES来当显存的段地址
mov ax,es:[bx];AX接收被除数的低16位
mov dx,es:[bx+2];DX接收被除数的高16位
mov di,es:[si];DI接收除数
div di;进行16位除法操作,商在AX中,余数在DX中。这里的余数可以废弃
mov dx,0;DX清零,因为这个余数不需要用到,但如果DX不清零,会影响后面的除法操作
mov di,0ah;H2D除数10
mov si,1;保存余数内存段的偏移地址
call r_divw;进行字除取余操作
mov ax,0b800h;显存段地址
mov es,ax;设置显存段地址,因为显存段地址用到了BP,所以必须加上段前缀
call s_h2d;这是显示H2D,进入这个子程序,PUSH IP
pop es;ES的值出栈,因为等一下要用到原来ES的值当被除数与除数的段地址
pop si;SI出栈,因为等一下要用它当除数的偏移地址
pop cx;CX出栈,以便进行LOOP循环判断
add bx,4;转到下一个被除数的偏移地址
add si,2;转到下一个除数的偏移地址
add bp,160;转到下一行
loop xh4;如果不为0就转吧,如果为0就结束这个程序
;以上为显示人均收入的程序
;------------------------------------------------------------------------------------------------------------
mov ax,4c00h
int 21h
;------------------------------------------------------------------------------------------------------------
s_str: push ax;因为AX要用到,为了使程序更保险,所以不管AX原来是什么,都先入栈,在这个程序结束前再出栈
mov ax,0;AX清零
mov al,[bx];把数据的值给AL进行中转,因为是字节操作,所以用8位寄存器
mov es:[bp],al;把数据的值传输给显存,以便在屏幕上显示出来
mov byte ptr es:[bp+1],4;显示颜色,这个可以自己修改成自己希望的颜色属性值
pop ax;AX出栈
ret;子程序结束,POP IP
;这个子程序的功能就是把指定的数据段位置的数据直接显示在指定的显存的位置
;一次只执行一个字符的显示,显示的是该数据的ASCII码值,而且可以选择颜色,颜色值以立即数的形式给出
;这个子程序的外接参数必须有,要显示的数据的段地址与偏移地址,要显示在显存上位置的段地址与偏移地址
;这个子程序没有返回值;这个子程序的功能就是把指定的数据段位置的数据直接显示在指定的显存的位置
;一次只执行一个字符的显示,显示的是该数据的ASCII码值,而且可以选择颜色,颜色值以立即数的形式给出
;这个子程序的外接参数必须有,要显示的数据的段地址与偏移地址,要显示在显存上位置的段地址与偏移地址
;这个子程序没有返回值
;------------------------------------------------------------------------------------------------------------
;------------------------------------------------------------------------------------------------------------
r_dw_h2d: mov si,1;这是存放H2D的商值的DATA2段偏移地址
mov di,0ah;这是除数
mov ax,es:[bx];把被除数的低16位给AX
mov dx,es:[bx+2];把被除数的高16位给DX
mov cx,dx;把被除数的高16位给CX,目的是为了进行是否跳转的判断
jcxz r_divw;如果DX等于0则跳转,进行字除,如果不等于0就不跳转,进行双字除
r_divdw: mov bp,ax;这里是开始进行双字除取余,先把低16的值给BP
mov ax,dx;把高16位的值给AX
mov dx,0;高16位寄存器清零
div di;第一次除,商在AX中,余数在DX中
mov cx,ax;把第一次得到的高16位商给CX
mov ax,bp;把原来的低16位被除数还给AX,如果DX中有余数,则余数直接充当高16位被除数
div di;第二次除,商在AX中,余数在DX中
mov [si],dl;把余数的值给内存
mov dx,cx;把高16位商还给DX
inc si;SI自增1,以便保存下一个余数值
jcxz r_divw;如果CX中的高位商为0了,那么就跳转进行字除,如果不为0,那么就继续进行双字除
jmp short r_divdw;
r_divw: div di;这里是开始进行字除取余,因为这说明DX中已经为0了,所以,DX不用清零,被除数在AX中
mov [si],dl;把余数给内存
mov cx,ax;把商给CX,进行是否跳转的判断
jcxz tc_r_dw_h2d;如果商为0,说明取余操作全部完成,可以跳转进行下一步的显示数值ASCII码的操作
mov dx,0;如果AX中的商不为0,那么就还要继续进行除法操作,所以要先把DX中的余数清零
inc si;SI自增1
jmp short r_divw;AX中的商不为0,还要继续进行字除操作
tc_r_dw_h2d:ret;退出这个子程序,POP IP
;这个子程序的功能是把双字数据的十六进制数据转换成十进制数据
;这个程序需要外接参数为:被除数的段地址与偏移地址,保存余数的段地址与偏移地址
;这个程序有返回值,返回值就是SI的值,代表着保存着余数数据段的偏移地址,下一个程序要用到
;------------------------------------------------------------------------------------------------------------
;------------------------------------------------------------------------------------------------------------
s_h2d: mov di,0;这是显存的变址
zl_s_h2d: mov al,[si];把内存中的余数值给AL
add al,30h;数字的ASCII码都要+30H
mov es:[bp+di],al;把AL的值给显存
inc di;变址自增1
mov byte ptr es:[bp+di],4;给字符显示颜色,红色
inc di;变址自增1
dec si;因为是倒着放的,所以这里也要倒着取,所以是自减1
mov cx,si;如果SI
jcxz tc_s_h2d;
jmp short zl_s_h2d
tc_s_h2d: ret;返回,POP IP
;这个子程序的功能是把保存在某个数据段中的十进制数值+30H,然后在指定的屏幕位置显示出来
;行固定,列由具体的数值决定
;外接参数为保存十进制数值的数据段的段地址与偏移地址,屏幕显示的段地址与偏移起始地址
;这个程序没有返回值
;------------------------------------------------------------------------------------------------------------
code ends
;------------------------------------------------------------------------------------------------------------
end start