王爽汇编的课程设计对初学者来说是不是难了点?写了两天修改了一天还是搞不定,感觉已经精疲力尽了...
本帖最后由 u011536790 于 2013-08-23 22:02:31 编辑
运行后闪一下就没了,看了很久找不到原因。而且很可能不止一个问题,初学汇编没多久,搞不定可以直接跳过往下学习么?
;使数据显示为四列,每列占40个字节汇编
assume cs:code,ds:data,ss:stack,es:data0
data segment
;以下数据段为年份
db '1975','1976','1977','1978','1979','1980','1981','1982','1983','1984','1985','1986','1987','1988','1989','1990','1991','1992','1993','1994','1995'
;至此共占84字节
;以下数据段为总资产
dd 16,22,382,1356,2390,8000,16000,24486,50065,97479,140417,197514,345980,590827,803530,1183000,1843000,2759000,3753000,4649000,5937000
;至此共占85+88=168字节
;以下数据段为人数
dw 3,7,9,13,28,38,130,220,476,778,1001,1442,2258,2793,4037,5635,8226,11452,14430,15257,17800
;至此共占85+88+44=210字节
;以下数据段为平均资产
dd 21 dup (0)
data ends
data0 segment ;字符串段,暂存转化的字符
db 10 dup (0)
data0 ends
stack segment ;栈段
db 32 dup (0)
stack ends
code segment
start:
mov ax,data
mov ds,ax
mov ax,stack
mov ss,ax
mov ax,data0
mov es,ax ;关联各段
mov di,0 ;双字单元起始偏移地址为0
mov si,0 ;字单元起始偏移地址为0
call pj ;调用子程序:计算平均资产
mov cx,21;
l: ;循环显示21行数据
;显示年份:(参数:数据段中年份起始偏移地址ds:[di]、显存单元中对应的年份起始偏移地址bx)
call nf ;调用子程序:显示年份
;显示总资产:(参数:数据段中总资产起始偏移地址ds:85[di]、显存单元中对应的总资产起始偏移地址bx)
mov ax,84[di] ;总资产数值低位赋给ax作为被除数低位
mov dx,84[di+2] ;总资产数值高位赋给ax作为被除数高位
call dtoc ;调用子程序:双字数值转化为字符。总资产数值转化为字符
mov bx,160+40 ;总资产字符串显示的起始偏移地址
mov dx,0
mov dl,02h ;设置字体颜色
call show_str ;调用子程序:显示字符串。在第二列显示总资产字符串
;显示人数:(参数:数据段中人数起始偏移地址ds:173[si]、显存单元中对应的人数起始偏移地址bx)
mov ax,168[si] ;人数赋给ax作为被除数低位
mov dx,0 ;人数被除数高位为0
call dtocz ;调用子程序:字数值转化为字符。人数转化为字符
mov bx,160+80 ;人数字符串显示的起始偏移地址
mov dx,0
mov dl,02h ;设置字体颜色
call show_str ;调用子程序:显示字符串。在第三列显示人数字符串
;显示平均资产:(参数:数据段中平均资产起始偏移地址ds:217[si]、显存单元中对应的平均资产起始偏移地址bx)
mov ax,210[si] ;平均资产数值赋给ax作为被除数低位
mov dx,0 ;平均资产被除数高位为0
call dtocz ;调用子程序:字数值转化为字符。平均资产数值转化为字符
mov bx,160+120 ;平均资产字符串显示的起始偏移地址
mov dx,0
mov dl,02h ;设置字体颜色
call show_str ;调用子程序:显示字符串。在第四列显示平均资产字符串
add si,2 ;字单元后移2个
add di,4 ;双字单元后移4个
add bx,160 ;显存单元后移160个,换行输出显示
loop l ;如循环次数不为0则返回到l处,重复循环
mov ax,4c00h
int 21h
pause proc near ;子程序定义(起退出缓冲作用)
jmp short exitbegin
exitstr db 0ah,0dh,'Press any key to exit...$'
exitbegin:
push ax
push ds
push dx
;以上3条push指令的作用是保存现场
mov ax,seg pause ;取子程序名pause所在的段地址
mov ds,ax
lea dx,exitstr
mov ah,9 ;显示字符串exitstr的内容
int 21h
mov ah,7 ;等待输入一个[无回显]字符
int 21h
pop dx
pop ds
pop ax
;以上3条pop指令的作用是恢复现场
ret
pause endp ;子程序定义结束
nf: ;进入子程序:显示年份。相关寄存器入栈
push ax
push bx
push di
push es
mov ax,0b800h
mov es,ax ;设置显存段地址es
mov ax,[di]
mov es:[bx],ax ;年份的前半部分存入显存单元
mov ax,[di+2]
mov es:[bx+2],ax ;年份的后半部分存入显存单元
pop es ;相关寄存器出栈
pop di
pop bx
pop ax
ret ;结束子程序,返回主程序
show_str: ;进入子程序:显示字符串。相关寄存器入栈
push ax
push bx
push cx
push dx
push ds
push es
push di
push si
mov ax,es
mov ds,ax ;暂存字符串的es段改为ds段
mov ax,0b800h
mov es,ax ;设置显存段地址es
j3:
mov cl,[si]
mov ch,0 ;[si]赋给cx,检测是否已到字符串末尾即[si]是否为0
jcxz r3 ;cx不为0就继续往下执行,为0则跳转到r3
mov al,[si] ;[si]中的字符赋给al
mov ah,dl ;字体颜色赋给ah
mov es:[bx+di],ax ;字符存入对应的显存单元
inc si ;字符串内存单元后移1个
add di,2 ;显存单元后移2个
jmp j3 ;跳转到j3重复循环
r3: ;相关寄存器出栈
pop si
pop di
pop es
pop ds
pop dx
pop cx
pop bx
pop ax
ret ;结束子程序,返回主程序
divdw: ;进入子程序:解决除法溢出的除法运算。相关寄存器入栈
push ax
push dx
push cx
push bx
push di
push si
mov si,ax ;存放被除数低位到si
mov ax,dx ;被除数高位赋给ax作为被除数低位
mov dx,0 ;被除数低位为0
div cx ;除法运算,商高位在ax中,余数高位在dx中
mov bx,ax ;bx存放商高位
mov ax,si ;被除数低位赋给ax,余数高位dx作为被除数高位
div cx ;除法运算,余数在dx中,商低位在ax中
mov cx,dx ;余数存放在cx中
mov dx,bx ;商高位存放在dx中
pop si ;相关寄存器出栈
pop di
pop bx
pop cx
pop dx
pop ax
ret ;结束子程序,返回子程序dtoc
dtoc: ;进入子程序:双字数值转化为字符。调用子程序divdw来解决除法溢出的问题。相关寄存器入栈
push ax
push bx
push cx
push dx
push di
push si
j2:
mov cx,10
call divdw ;调用子程序:解决除法溢出的除法运算
add cx,30h ;余数转为对应的字符
push cx ;字符入栈
inc di ;字符个数计数
mov cx,ax ;商赋给cx
jcxz r ;检测低位商是否为0,不为0继续往下执行,为0则跳转到r
jmp short j2 ;跳转到j2,重复循环
r:mov cx,dx
jcxz r2 ;检测高位商是否为0,不为0继续往下执行,为0则跳转到r2
jmp short j2 ;跳转到j2,重复循环
r2: ;把栈段中的字符存放到es段中
mov cx,di ;字符个数作为循环次数
l1:pop dx ;字符出栈
mov es:[si],dl ;dl中的字符赋给es的内存单元
inc si ;es的内存单元后移1位
loop l1 ;如果循环次数不为0则返回到l0处重复循环,为0则继续往下执行
mov ax,0
mov es:[si],ax ;使字符串的末尾为0
pop si ;相关寄存器出栈
pop di
pop dx
pop cx
pop bx
pop ax
ret ;结束子程序,返回主程序
dtocz: ;进入子程序:字数值转化为字符。相关寄存器入栈
push ax
push bx
push cx
push dx
push di
push si
j1:
mov bx,10
div bx ;除法运算,商存放在ax中,余数存放在dx中
add dx,30h ;余数转为对应的字符
push dx ;字符入栈
inc di ;字符个数计数
mov cx,ax ;商赋给cx
jcxz r1 ;检测商是否为0,不为0继续往下执行,为0则跳转到r1
jmp short j1 ;跳转到j1,重复循环
r1: ;把栈段中的字符存放到es段中
mov cx,di ;字符个数作为循环次数
l0:pop dx ;字符出栈
mov es:[si],dl ;dl中的字符赋给es的内存单元
inc si ;es的内存单元后移1位
loop l0 ;如果循环次数不为0则返回到l0处重复循环,为0则继续往下执行
mov ax,0
mov es:[si],ax ;使字符串的末尾为0
pop si ;相关寄存器出栈
pop di
pop dx
pop cx
pop bx
pop ax
ret ;结束子程序,返回主程序
pj: ;进入子程序:计算平均资产。相关寄存器入栈
push ax
push cx
push dx
push di
push si
mov cx,21
lp:
push cx
mov cx,168[si] ;将除数赋给cx
jcxz r0 ;如果除数为0,跳转到r0处
mov ax,84[di] ;被除数低位赋给ax;
mov dx,84[di+2];被除数高位赋给dx;
div cx ;除法运算,平均资产存放在ax中
mov 210[si],ax ;平均资产从ax存放到平均资产的内存单元
add di,4 ;总资产的内存单元后移4个
add si,2 ;存放平均资产的内存单元后移2个
pop cx
loop lp ;跳转到lp处重复循环
r0: ;相关寄存器出栈
pop si
pop di
pop dx
pop cx
pop ax
ret ;结束子程序,返回主程序
code ends
end start
assume cs:code , ds:data , ss:stack
; 数据段
data segment
db '1975','1976','1977','1978','1979','1980','1981','1982','1983';第一个偏移值:0
db '1984','1985','1986','1987','1988','1989','1990','1991','1992'
db '1993','1994','1995';最后一个偏移值:53H(83D)
;以上是表示21年的21个字符串
dd 16,22,382,1356,2390,8000,16000,24486,50065,97479,140417,197514;第一个偏移值:54H(84D)
dd 345980,590827,803530,1183000,1843000,2759000,3753000,4649000,5937000;最后一个偏移值:A7H(167D)
;以上是表示21年公司总收入的21个dword型数据
dw 3,7,9,13,28,38,130,220,476,778,1001,1442,2258,2793,4037,5635,8226;第一个偏移值是:A8H(168D)
dw 11542,14430,15257,17800;最后一个偏移值是:D2(210D)
;以上是表示21年公司雇员人数的21个word型数据
dw 64 dup(?); 因为最后转换成字符串的空间不够,所以需要再多申请这么多空间
data ends
; 栈段
stack segment
db 256 dup (?)
stack ends
; 计算后的数据存放段
table segment
db 21 dup ('year sunm ne ?? ')
dw 32 dup(?); 因为最后转换成字符串的空间不够,所以需要再多申请这么多空间
table ends
; 代码段
code segment
start:
; 设置栈段地址
mov ax , stack
mov ss , ax
mov sp , 256
; 计算数据段中的数据(计算人均收入,也就是收入除以雇员数)
mov ax , data
mov ds , ax
mov bx , 0
call Calculate
; 循环,将双字节的数据的整型数转换成字符型
mov bx , 0
mov si , 0; 设置字符串输出的首地址
mov cx , 21; 设置循环次数
again:
; 复制年份到ds段
mov ax , es:[ bx ]
mov ds:[ si ] , ax
mov ax , es:[ bx + 2 ]
mov ds:[ si + 2 ] , ax
mov word ptr ds:[ si + 4 ] , 0; 存入0,表示间隔
add bx , 5; 因为只需跳过一个空格,空格占一个字节,再加上前面读取的4个字节
add si , 6
mov ax , es:[ bx ]; 获取公司总收入的dword型数据的低位
mov dx , es:[ bx + 2 ];; 获取公司总收入的dword型数据的高位
call dtoc; 调用子程序,将dword型数据转换成数字符串型,放到ds:si指定的位置
add bx , 5; 跳过已经读取的字节再加上一个空格
inc si; 如果去掉这个自增1,则存放他们之间的间隔0为一个字节,没有去掉这个自增就为一个字
mov ax , es:[ bx ]; 获取公司雇员人数的word型数据
mov dx , 0; 因为是word型数据,所以高位不需要,为0
call dtoc; 调用子程序,将dword型数据转换成数字符串型,放到ds:si指定的位置
add bx , 3; 跳过已经读取的字节再加上一个空格
inc si; 如果去掉这个自增1,则存放他们之间的间隔0为一个字节,没有去掉这个自增就为一个字
mov ax , es:[ bx ]; 获取公司雇员的人均收入
mov dx , 0; 因为是word型数据,所以高位不需要,为0
call dtoc; 调用子程序,将dword型数据转换成数字符串型,放到ds:si指定的位置
add bx , 3; 跳过已经读取的字节再加上一个空格
inc si; 如果去掉这个自增1,则存放他们之间的间隔0为一个字节,没有去掉这个自增就为一个字
loop again
; 循环,用于显示所有的字符串
mov dh , 0; 第0行
mov dl , 2; 第2列
mov si , 0; 设置指向字符串首地址偏移量
mov cx , 22; 外循环共循环21次(因为第一次为了直接跳到循环处而一次性执行循环,所以需要多加一次循环)
jmp show_start; 跳到开始出,以便能直接使用loop循环一次性输出
show_again1:
push cx; 现场保护
mov cx , 4; 内循环共循环4次
show_again2:
push cx; 因为CX还要用来设置颜色,所以还要保护
mov ch , 0
mov cl , 01h; 设置颜色(蓝色)
call show_str
add si , 2; 加上间隔符,进行下一组字符串的输出
add dl , 10; 每输出一串字符列就空开几列
pop cx; 恢复内循环的循环次数值
loop show_again2
add dh , 1; 输出完一组就要换下一行
mov dl , 2; 输出完一组列就要归位
pop cx; 恢复外循环的循环次数值
show_start:
loop show_again1
; 结束程序
mov ax , 4c00h
int 21h
; 子程序
; 计算数据段中的数据(计算平均收入,也就是收入除以雇员数)
; 入口参数:ds:bx为待计算的数据段
; 出口参数:计算好后的数据将放在table数据段中,也可以用 es:0 来直接获取这个table数据段的首地址
Calculate:
mov ax , table
mov es , ax;设置要写入数据的段寄存器
mov bp , 0;设置table段的偏移地址
mov di , 0;设置雇员数每次增加的值(因为与其他值相比只有雇员数是2字节,所以要做特殊处理)
mov cx , 21;设置循环的值为21次
again_1:
mov ax , ds:[bx];复制年份的低字
mov es:[bp] , ax
mov ax , ds:[bx + 2];复制年份的高字
mov es:[bp + 2] , ax
mov ax , ds:0A8H.[di];复制雇员数(十进制168)
mov es:[bp + 0AH] , ax;加10是因为复制了2个字节,而且写入的时候要输入一个空格,
;并且这个位置是在复制了4个字节后的再加5个字节的位置
mov ax , ds:54H.[bx];复制收入的低字节(十进制84)
mov es:[bp + 5] , ax
mov dx , ds:54H.[bx + 2];复制收入的高字节(十进制84)
mov es:[bp + 7] , dx
div word ptr ds:0A8H.[di];因为除数是2个字节(16位)的,所以也用di(di每次循环增加2)
mov es:[bp + 0DH] , ax;复制算好的人均收入
add bp , 16;进行下一组写入
add di , 2;用于计算雇员数的值加2,因为他一次值复制了2个字节
add bx , 4;复制了一次后要加上4字节,以便下一次读出和计算
loop again_1
ret; Calculate子程序返回
;参数: (ax) = dword型数据的低16位
; (dx) = dword型数据的高16位
; (cx) = 除数
;返回: (dx) = 结果的高16位
; (ax) = 结果的低16位
; (cx) = 余数
divdw: ;子程序定义开始
push ax
mov ax,dx
mov dx,0
div cx
mov bx,ax
pop ax
div cx
mov cx,dx
mov dx,bx
ret ;子程序定义结束
; 功能:将dword型数据转变为表示十进制数的字符,字符以0为结尾符
; 参数:(ax) = dword型数据的低16位
; (dx) = dword型数据的高16位
; ds:si指向字符串的首地址
; 返回:无
dtoc: push cx; 保护现场
push bx; 保护现场
push si; 保存用来输出结果的字符串的首地址
mov byte ptr ds:[ si ] , 0;先写入结束符(写在首位,以便配合栈结构)
inc si
mov cx , 1
again_3:mov cx , 10; 做除数,每次都除以10,以便得到余数,从而求出每个位的值
call divdw; 调用除法子程序,因为这个除法子程序可以防止数据溢出
add cl , 30H;加上30H成为字符的ASCII码
mov ds:[ si ] , cl;存入到指定的数据段
; 判断商的低位和高位是否都为0如果都为0,则表示结束
mov cx , 0
or cx , ax
or cx , dx
jcxz ok1
inc si
inc cx;因为loop会将cx减少1,所以要先加1
loop again_3
ok1: mov byte ptr ds:[ si + 1 ] , 0;尾部也写入结束符
pop si; 取出用来输出结果的首地址
mov bx , si; bx用来做最后的数据存入,所以也是为输出结果的首地址
mov al , ds:[ si ];从得到的第一个数据开始遍历
mov ah , 0;只要低位,高位归零
push ax;结束符先入栈
;把得出的字符倒过来,因为求余得到的数是倒着的
again_4:inc si
mov al , ds:[ si ]
mov ah , 0;只要低位,高位归零
mov cx , ax
jcxz ok2;已经全部临时存入栈中
push ax
inc cx;因为loop会将cx减少1,所以要先加1
loop again_4
ok2: pop cx;从栈中取出,正好实现倒叙
mov ds:[ bx ] , cl;只要低位,高位归零
jcxz return
inc bx
inc cx;因为loop会将cx减少1,所以要先加1
loop ok2
return: pop bx; 恢复现场
pop cx; 恢复现场
ret; 返回
;子程序
;功能:在指定的位置 , 用指定的颜色 , 显示一个用0结束的字符串
;参数:(dh) = 行号(取值范围0~24) , (dl) = 列号(取值范围0~79)
; (cl) = 颜色 , ds:si指向字符串首地址
show_str:
push dx; 现场保护
mov ax , 000ah;转换成行需要乘以a(10)
mul dh;计算写入位置的行
add ax , 0b800h;B800是起始位置,所以加上规定的行数
add dl , dl;计算写入位置的列
mov dh , 0
mov es , ax;设置写入位置的寄存器
mov di , dx
mov al , cl;保存颜色
mov cx , 2
jmp first_start
again_2:
mov cl , ds:[si];获取要显示的数据
mov ch , 0;不需要高位
jcxz ok;如果是0则返回(结束这个子程序)
mov es:[di] , cl;将字符写入
mov es:[di + 1] , al;写入颜色
add di , 2;指向下一个要写入的位置
inc si;指向下一个要读取字符的位置
mov cl , 2;again循环每次都成立
first_start:
loop again_2
ok:pop dx; 恢复
ret;返回(结束)这个子程序
code ends
end start